3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
33 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
35 Environment::Environment():
40 Environment::~Environment()
43 for(core::list<Player*>::Iterator i = m_players.begin();
44 i != m_players.end(); i++)
50 void Environment::addPlayer(Player *player)
52 DSTACK(__FUNCTION_NAME);
54 Check that peer_ids are unique.
55 Also check that names are unique.
56 Exception: there can be multiple players with peer_id=0
58 // If peer id is non-zero, it has to be unique.
59 if(player->peer_id != 0)
60 assert(getPlayer(player->peer_id) == NULL);
61 // Name has to be unique.
62 assert(getPlayer(player->getName()) == NULL);
64 m_players.push_back(player);
67 void Environment::removePlayer(u16 peer_id)
69 DSTACK(__FUNCTION_NAME);
71 for(core::list<Player*>::Iterator i = m_players.begin();
72 i != m_players.end(); i++)
75 if(player->peer_id != peer_id)
80 // See if there is an another one
81 // (shouldn't be, but just to be sure)
86 Player * Environment::getPlayer(u16 peer_id)
88 for(core::list<Player*>::Iterator i = m_players.begin();
89 i != m_players.end(); i++)
92 if(player->peer_id == peer_id)
98 Player * Environment::getPlayer(const char *name)
100 for(core::list<Player*>::Iterator i = m_players.begin();
101 i != m_players.end(); i++)
104 if(strcmp(player->getName(), name) == 0)
110 Player * Environment::getRandomConnectedPlayer()
112 core::list<Player*> connected_players = getPlayers(true);
113 u32 chosen_one = myrand() % connected_players.size();
115 for(core::list<Player*>::Iterator
116 i = connected_players.begin();
117 i != connected_players.end(); i++)
129 Player * Environment::getNearestConnectedPlayer(v3f pos)
131 core::list<Player*> connected_players = getPlayers(true);
133 Player *nearest_player = NULL;
134 for(core::list<Player*>::Iterator
135 i = connected_players.begin();
136 i != connected_players.end(); i++)
139 f32 d = player->getPosition().getDistanceFrom(pos);
140 if(d < nearest_d || nearest_player == NULL)
143 nearest_player = player;
146 return nearest_player;
149 core::list<Player*> Environment::getPlayers()
154 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
156 core::list<Player*> newlist;
157 for(core::list<Player*>::Iterator
158 i = m_players.begin();
159 i != m_players.end(); i++)
163 if(ignore_disconnected)
165 // Ignore disconnected players
166 if(player->peer_id == 0)
170 newlist.push_back(player);
175 void Environment::printPlayers(std::ostream &o)
177 o<<"Players in environment:"<<std::endl;
178 for(core::list<Player*>::Iterator i = m_players.begin();
179 i != m_players.end(); i++)
182 o<<"Player peer_id="<<player->peer_id<<std::endl;
186 /*void Environment::setDayNightRatio(u32 r)
188 getDayNightRatio() = r;
191 u32 Environment::getDayNightRatio()
193 //return getDayNightRatio();
194 return time_to_daynight_ratio(m_time_of_day);
201 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
204 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
205 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
206 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
213 void ActiveBlockList::update(core::list<v3s16> &active_positions,
215 core::map<v3s16, bool> &blocks_removed,
216 core::map<v3s16, bool> &blocks_added)
221 core::map<v3s16, bool> newlist;
222 for(core::list<v3s16>::Iterator i = active_positions.begin();
223 i != active_positions.end(); i++)
225 fillRadiusBlock(*i, radius, newlist);
229 Find out which blocks on the old list are not on the new list
231 // Go through old list
232 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
233 i.atEnd()==false; i++)
235 v3s16 p = i.getNode()->getKey();
236 // If not on new list, it's been removed
237 if(newlist.find(p) == NULL)
238 blocks_removed.insert(p, true);
242 Find out which blocks on the new list are not on the old list
244 // Go through new list
245 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
246 i.atEnd()==false; i++)
248 v3s16 p = i.getNode()->getKey();
249 // If not on old list, it's been added
250 if(m_list.find(p) == NULL)
251 blocks_added.insert(p, true);
258 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
259 i.atEnd()==false; i++)
261 v3s16 p = i.getNode()->getKey();
262 m_list.insert(p, true);
270 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
273 m_random_spawn_timer(3),
274 m_send_recommended_timer(0),
276 m_game_time_fraction_counter(0)
280 ServerEnvironment::~ServerEnvironment()
282 // Clear active block list.
283 // This makes the next one delete all active objects.
284 m_active_blocks.clear();
286 // Convert all objects to static and delete the active objects
287 deactivateFarObjects(true);
293 void ServerEnvironment::serializePlayers(const std::string &savedir)
295 std::string players_path = savedir + "/players";
296 fs::CreateDir(players_path);
298 core::map<Player*, bool> saved_players;
300 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
301 for(u32 i=0; i<player_files.size(); i++)
303 if(player_files[i].dir)
306 // Full path to this file
307 std::string path = players_path + "/" + player_files[i].name;
309 //infostream<<"Checking player file "<<path<<std::endl;
311 // Load player to see what is its name
312 ServerRemotePlayer testplayer;
314 // Open file and deserialize
315 std::ifstream is(path.c_str(), std::ios_base::binary);
316 if(is.good() == false)
318 infostream<<"Failed to read "<<path<<std::endl;
321 testplayer.deSerialize(is);
324 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
326 // Search for the player
327 std::string playername = testplayer.getName();
328 Player *player = getPlayer(playername.c_str());
331 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
335 //infostream<<"Found matching player, overwriting."<<std::endl;
337 // OK, found. Save player there.
339 // Open file and serialize
340 std::ofstream os(path.c_str(), std::ios_base::binary);
341 if(os.good() == false)
343 infostream<<"Failed to overwrite "<<path<<std::endl;
346 player->serialize(os);
347 saved_players.insert(player, true);
351 for(core::list<Player*>::Iterator i = m_players.begin();
352 i != m_players.end(); i++)
355 if(saved_players.find(player) != NULL)
357 /*infostream<<"Player "<<player->getName()
358 <<" was already saved."<<std::endl;*/
361 std::string playername = player->getName();
362 // Don't save unnamed player
365 //infostream<<"Not saving unnamed player."<<std::endl;
371 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
372 playername = "player";
373 std::string path = players_path + "/" + playername;
375 for(u32 i=0; i<1000; i++)
377 if(fs::PathExists(path) == false)
382 path = players_path + "/" + playername + itos(i);
386 infostream<<"Didn't find free file for player"<<std::endl;
391 /*infostream<<"Saving player "<<player->getName()<<" to "
393 // Open file and serialize
394 std::ofstream os(path.c_str(), std::ios_base::binary);
395 if(os.good() == false)
397 infostream<<"Failed to overwrite "<<path<<std::endl;
400 player->serialize(os);
401 saved_players.insert(player, true);
405 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
408 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
410 std::string players_path = savedir + "/players";
412 core::map<Player*, bool> saved_players;
414 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
415 for(u32 i=0; i<player_files.size(); i++)
417 if(player_files[i].dir)
420 // Full path to this file
421 std::string path = players_path + "/" + player_files[i].name;
423 infostream<<"Checking player file "<<path<<std::endl;
425 // Load player to see what is its name
426 ServerRemotePlayer testplayer;
428 // Open file and deserialize
429 std::ifstream is(path.c_str(), std::ios_base::binary);
430 if(is.good() == false)
432 infostream<<"Failed to read "<<path<<std::endl;
435 testplayer.deSerialize(is);
438 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
440 infostream<<"Not loading player with invalid name: "
441 <<testplayer.getName()<<std::endl;
444 infostream<<"Loaded test player with name "<<testplayer.getName()
447 // Search for the player
448 std::string playername = testplayer.getName();
449 Player *player = getPlayer(playername.c_str());
450 bool newplayer = false;
453 infostream<<"Is a new player"<<std::endl;
454 player = new ServerRemotePlayer();
460 infostream<<"Reading player "<<testplayer.getName()<<" from "
462 // Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
464 if(is.good() == false)
466 infostream<<"Failed to read "<<path<<std::endl;
469 player->deSerialize(is);
477 void ServerEnvironment::saveMeta(const std::string &savedir)
479 std::string path = savedir + "/env_meta.txt";
481 // Open file and serialize
482 std::ofstream os(path.c_str(), std::ios_base::binary);
483 if(os.good() == false)
485 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
487 throw SerializationError("Couldn't save env meta");
491 args.setU64("game_time", m_game_time);
492 args.setU64("time_of_day", getTimeOfDay());
497 void ServerEnvironment::loadMeta(const std::string &savedir)
499 std::string path = savedir + "/env_meta.txt";
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
507 throw SerializationError("Couldn't load env meta");
515 throw SerializationError
516 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
518 std::getline(is, line);
519 std::string trimmedline = trim(line);
520 if(trimmedline == "EnvArgsEnd")
522 args.parseConfigLine(line);
526 m_game_time = args.getU64("game_time");
527 }catch(SettingNotFoundException &e){
528 // Getting this is crucial, otherwise timestamps are useless
529 throw SerializationError("Couldn't load env meta game_time");
533 m_time_of_day = args.getU64("time_of_day");
534 }catch(SettingNotFoundException &e){
535 // This is not as important
536 m_time_of_day = 9000;
541 // This is probably very useless
542 void spawnRandomObjects(MapBlock *block)
544 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
545 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
547 bool last_node_walkable = false;
548 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
551 MapNode n = block->getNodeNoEx(p);
552 if(n.getContent() == CONTENT_IGNORE)
554 if(content_features(n).liquid_type != LIQUID_NONE)
556 if(content_features(n).walkable)
558 last_node_walkable = true;
561 if(last_node_walkable)
563 // If block contains light information
564 if(content_features(n).param_type == CPT_LIGHT)
566 if(n.getLight(LIGHTBANK_DAY) <= 5)
568 if(myrand() % 1000 == 0)
570 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
572 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
573 std::string data = obj->getStaticData();
574 StaticObject s_obj(obj->getType(),
575 obj->getBasePosition(), data);
577 block->m_static_objects.insert(0, s_obj);
579 block->setChangedFlag();
584 last_node_walkable = false;
590 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
592 // Get time difference
594 u32 stamp = block->getTimestamp();
595 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
596 dtime_s = m_game_time - block->getTimestamp();
597 dtime_s += additional_dtime;
599 // Set current time as timestamp (and let it set ChangedFlag)
600 block->setTimestamp(m_game_time);
602 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
604 // Activate stored objects
605 activateObjects(block);
608 bool changed = block->m_node_metadata.step((float)dtime_s);
612 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
613 event.p = block->getPos();
614 m_map->dispatchEvent(&event);
616 block->setChangedFlag();
619 // TODO: Do something
620 // TODO: Implement usage of ActiveBlockModifier
622 // Here's a quick demonstration
624 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
625 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
626 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
628 v3s16 p = p0 + block->getPosRelative();
629 MapNode n = block->getNodeNoEx(p0);
632 // Convert all mud under proper day lighting to grass
633 if(n.getContent() == CONTENT_MUD)
637 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
638 if(content_features(n_top).air_equivalent &&
639 n_top.getLight(LIGHTBANK_DAY) >= 13)
641 n.setContent(CONTENT_GRASS);
642 m_map->addNodeWithEvent(p, n);
650 static void getMob_dungeon_master(Settings &properties)
652 properties.set("looks", "dungeon_master");
653 properties.setFloat("yaw", 1.57);
654 properties.setFloat("hp", 30);
655 properties.setBool("bright_shooting", true);
656 properties.set("shoot_type", "fireball");
657 properties.set("shoot_y", "0.7");
658 properties.set("player_hit_damage", "1");
659 properties.set("player_hit_distance", "1.0");
660 properties.set("player_hit_interval", "0.5");
661 properties.setBool("mindless_rage", myrand_range(0,100)==0);
664 void ServerEnvironment::step(float dtime)
666 DSTACK(__FUNCTION_NAME);
668 //TimeTaker timer("ServerEnv step");
671 bool footprints = g_settings->getBool("footprints");
677 m_game_time_fraction_counter += dtime;
678 u32 inc_i = (u32)m_game_time_fraction_counter;
679 m_game_time += inc_i;
680 m_game_time_fraction_counter -= (float)inc_i;
687 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_LOWPASS);
688 for(core::list<Player*>::Iterator i = m_players.begin();
689 i != m_players.end(); i++)
693 // Ignore disconnected players
694 if(player->peer_id == 0)
697 v3f playerpos = player->getPosition();
700 player->move(dtime, *m_map, 100*BS);
703 Add footsteps to grass
707 // Get node that is at BS/4 under player
708 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
710 MapNode n = m_map->getNode(bottompos);
711 if(n.getContent() == CONTENT_GRASS)
713 n.setContent(CONTENT_GRASS_FOOTSTEPS);
714 m_map->setNode(bottompos, n);
717 catch(InvalidPositionException &e)
725 Manage active block list
727 if(m_active_blocks_management_interval.step(dtime, 2.0))
729 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg", SPT_LOWPASS);
731 Get player block positions
733 core::list<v3s16> players_blockpos;
734 for(core::list<Player*>::Iterator
735 i = m_players.begin();
736 i != m_players.end(); i++)
739 // Ignore disconnected players
740 if(player->peer_id == 0)
742 v3s16 blockpos = getNodeBlockPos(
743 floatToInt(player->getPosition(), BS));
744 players_blockpos.push_back(blockpos);
748 Update list of active blocks, collecting changes
750 const s16 active_block_range = g_settings->getS16("active_block_range");
751 core::map<v3s16, bool> blocks_removed;
752 core::map<v3s16, bool> blocks_added;
753 m_active_blocks.update(players_blockpos, active_block_range,
754 blocks_removed, blocks_added);
757 Handle removed blocks
760 // Convert active objects that are no more in active blocks to static
761 deactivateFarObjects(false);
763 for(core::map<v3s16, bool>::Iterator
764 i = blocks_removed.getIterator();
765 i.atEnd()==false; i++)
767 v3s16 p = i.getNode()->getKey();
769 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
770 <<") became inactive"<<std::endl;*/
772 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
776 // Set current time as timestamp (and let it set ChangedFlag)
777 block->setTimestamp(m_game_time);
784 for(core::map<v3s16, bool>::Iterator
785 i = blocks_added.getIterator();
786 i.atEnd()==false; i++)
788 v3s16 p = i.getNode()->getKey();
790 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
791 <<") became active"<<std::endl;*/
793 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
797 activateBlock(block);
802 Mess around in active blocks
804 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
806 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg", SPT_LOWPASS);
810 for(core::map<v3s16, bool>::Iterator
811 i = m_active_blocks.m_list.getIterator();
812 i.atEnd()==false; i++)
814 v3s16 p = i.getNode()->getKey();
816 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
817 <<") being handled"<<std::endl;*/
819 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
823 // Reset block usage timer
824 block->resetUsageTimer();
826 // Set current time as timestamp
827 block->setTimestampNoChangedFlag(m_game_time);
830 bool changed = block->m_node_metadata.step(dtime);
834 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
836 m_map->dispatchEvent(&event);
838 block->setChangedFlag();
843 if(m_active_blocks_test_interval.step(dtime, 10.0))
845 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg", SPT_LOWPASS);
846 //float dtime = 10.0;
848 for(core::map<v3s16, bool>::Iterator
849 i = m_active_blocks.m_list.getIterator();
850 i.atEnd()==false; i++)
852 v3s16 p = i.getNode()->getKey();
854 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
855 <<") being handled"<<std::endl;*/
857 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
861 // Set current time as timestamp
862 block->setTimestampNoChangedFlag(m_game_time);
867 Note that map modifications should be done using the event-
868 making map methods so that the server gets information
871 Reading can be done quickly directly from the block.
873 Everything should bind to inside this single content
874 searching loop to keep things fast.
876 // TODO: Implement usage of ActiveBlockModifier
878 // Find out how many objects the block contains
879 //u32 active_object_count = block->m_static_objects.m_active.size();
880 // Find out how many objects this and all the neighbors contain
881 u32 active_object_count_wider = 0;
882 for(s16 x=-1; x<=1; x++)
883 for(s16 y=-1; y<=1; y++)
884 for(s16 z=-1; z<=1; z++)
886 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
889 active_object_count_wider +=
890 block->m_static_objects.m_active.size();
894 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
895 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
896 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
898 v3s16 p = p0 + block->getPosRelative();
899 MapNode n = block->getNodeNoEx(p0);
903 Convert mud under proper lighting to grass
905 if(n.getContent() == CONTENT_MUD)
909 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
910 if(content_features(n_top).air_equivalent &&
911 n_top.getLightBlend(getDayNightRatio()) >= 13)
913 n.setContent(CONTENT_GRASS);
914 m_map->addNodeWithEvent(p, n);
919 Convert grass into mud if under something else than air
921 if(n.getContent() == CONTENT_GRASS)
923 //if(myrand()%20 == 0)
925 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
926 if(content_features(n_top).air_equivalent == false)
928 n.setContent(CONTENT_MUD);
929 m_map->addNodeWithEvent(p, n);
934 Rats spawn around regular trees
936 if(n.getContent() == CONTENT_TREE ||
937 n.getContent() == CONTENT_JUNGLETREE)
939 if(myrand()%200 == 0 && active_object_count_wider == 0)
941 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
942 0, myrand_range(-2, 2));
943 MapNode n1 = m_map->getNodeNoEx(p1);
944 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
945 if(n1b.getContent() == CONTENT_GRASS &&
946 n1.getContent() == CONTENT_AIR)
948 v3f pos = intToFloat(p1, BS);
949 ServerActiveObject *obj = new RatSAO(this, 0, pos);
950 addActiveObject(obj);
955 Fun things spawn in caves and dungeons
957 if(n.getContent() == CONTENT_STONE ||
958 n.getContent() == CONTENT_MOSSYCOBBLE)
960 if(myrand()%200 == 0 && active_object_count_wider == 0)
962 v3s16 p1 = p + v3s16(0,1,0);
963 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
964 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
965 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
966 if(n1a.getContent() == CONTENT_AIR &&
967 n1b.getContent() == CONTENT_AIR)
969 v3f pos = intToFloat(p1, BS);
971 if(i == 0 || i == 1){
972 actionstream<<"A dungeon master spawns at "
975 getMob_dungeon_master(properties);
976 ServerActiveObject *obj = new MobV2SAO(
977 this, 0, pos, &properties);
978 addActiveObject(obj);
979 } else if(i == 2 || i == 3){
980 actionstream<<"Rats spawn at "
982 for(int j=0; j<3; j++){
983 ServerActiveObject *obj = new RatSAO(
985 addActiveObject(obj);
988 actionstream<<"An oerkki spawns at "
990 ServerActiveObject *obj = new Oerkki1SAO(
992 addActiveObject(obj);
999 Make trees from saplings!
1001 if(n.getContent() == CONTENT_SAPLING)
1003 if(myrand()%50 == 0)
1005 actionstream<<"A sapling grows into a tree at "
1008 core::map<v3s16, MapBlock*> modified_blocks;
1010 ManualMapVoxelManipulator vmanip(m_map);
1011 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1012 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1013 bool is_apple_tree = myrand()%4 == 0;
1014 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1015 vmanip.blitBackAll(&modified_blocks);
1018 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1025 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1027 // Send a MEET_OTHER event
1029 event.type = MEET_OTHER;
1030 for(core::map<v3s16, MapBlock*>::Iterator
1031 i = modified_blocks.getIterator();
1032 i.atEnd() == false; i++)
1034 v3s16 p = i.getNode()->getKey();
1035 event.modified_blocks.insert(p, true);
1037 m_map->dispatchEvent(&event);
1048 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_LOWPASS);
1049 //TimeTaker timer("Step active objects");
1051 // This helps the objects to send data at the same time
1052 bool send_recommended = false;
1053 m_send_recommended_timer += dtime;
1054 if(m_send_recommended_timer > 0.10)
1056 m_send_recommended_timer = 0;
1057 send_recommended = true;
1060 for(core::map<u16, ServerActiveObject*>::Iterator
1061 i = m_active_objects.getIterator();
1062 i.atEnd()==false; i++)
1064 ServerActiveObject* obj = i.getNode()->getValue();
1065 // Remove non-peaceful mobs on peaceful mode
1066 if(g_settings->getBool("only_peaceful_mobs")){
1067 if(!obj->isPeaceful())
1068 obj->m_removed = true;
1070 // Don't step if is to be removed or stored statically
1071 if(obj->m_removed || obj->m_pending_deactivation)
1074 obj->step(dtime, send_recommended);
1075 // Read messages from object
1076 while(obj->m_messages_out.size() > 0)
1078 m_active_object_messages.push_back(
1079 obj->m_messages_out.pop_front());
1085 Manage active objects
1087 if(m_object_management_interval.step(dtime, 0.5))
1089 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg", SPT_LOWPASS);
1091 Remove objects that satisfy (m_removed && m_known_by_count==0)
1093 removeRemovedObjects();
1096 if(g_settings->getBool("enable_experimental"))
1103 m_random_spawn_timer -= dtime;
1104 if(m_random_spawn_timer < 0)
1106 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1107 //m_random_spawn_timer += 2.0;
1108 m_random_spawn_timer += 200.0;
1114 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1115 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1116 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1118 Player *player = getRandomConnectedPlayer();
1121 pos = player->getPosition();
1123 myrand_range(-3,3)*BS,
1125 myrand_range(-3,3)*BS
1129 Create a ServerActiveObject
1132 //TestSAO *obj = new TestSAO(this, 0, pos);
1133 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1134 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1135 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1136 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1138 infostream<<"Server: Spawning MobV2SAO at "
1139 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1141 Settings properties;
1142 getMob_dungeon_master(properties);
1143 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1144 addActiveObject(obj);
1148 } // enable_experimental
1151 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1153 core::map<u16, ServerActiveObject*>::Node *n;
1154 n = m_active_objects.find(id);
1157 return n->getValue();
1160 bool isFreeServerActiveObjectId(u16 id,
1161 core::map<u16, ServerActiveObject*> &objects)
1166 for(core::map<u16, ServerActiveObject*>::Iterator
1167 i = objects.getIterator();
1168 i.atEnd()==false; i++)
1170 if(i.getNode()->getKey() == id)
1176 u16 getFreeServerActiveObjectId(
1177 core::map<u16, ServerActiveObject*> &objects)
1182 if(isFreeServerActiveObjectId(new_id, objects))
1192 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1195 u16 id = addActiveObjectRaw(object, true);
1199 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1203 v3f objectpos = obj->getBasePosition();
1205 // The block in which the object resides in
1206 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1209 Update the static data
1212 // Create new static object
1213 std::string staticdata = obj->getStaticData();
1214 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1215 // Add to the block where the object is located in
1216 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1217 // Get or generate the block
1218 MapBlock *block = m_map->emergeBlock(blockpos);
1220 bool succeeded = false;
1224 block->m_static_objects.insert(0, s_obj);
1225 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1229 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1230 <<"Could not find or generate "
1231 <<"a block for storing static object"<<std::endl;
1241 Finds out what new objects have been added to
1242 inside a radius around a position
1244 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1245 core::map<u16, bool> ¤t_objects,
1246 core::map<u16, bool> &added_objects)
1248 v3f pos_f = intToFloat(pos, BS);
1249 f32 radius_f = radius * BS;
1251 Go through the object list,
1252 - discard m_removed objects,
1253 - discard objects that are too far away,
1254 - discard objects that are found in current_objects.
1255 - add remaining objects to added_objects
1257 for(core::map<u16, ServerActiveObject*>::Iterator
1258 i = m_active_objects.getIterator();
1259 i.atEnd()==false; i++)
1261 u16 id = i.getNode()->getKey();
1263 ServerActiveObject *object = i.getNode()->getValue();
1266 // Discard if removed
1267 if(object->m_removed)
1269 // Discard if too far
1270 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1271 if(distance_f > radius_f)
1273 // Discard if already on current_objects
1274 core::map<u16, bool>::Node *n;
1275 n = current_objects.find(id);
1278 // Add to added_objects
1279 added_objects.insert(id, false);
1284 Finds out what objects have been removed from
1285 inside a radius around a position
1287 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1288 core::map<u16, bool> ¤t_objects,
1289 core::map<u16, bool> &removed_objects)
1291 v3f pos_f = intToFloat(pos, BS);
1292 f32 radius_f = radius * BS;
1294 Go through current_objects; object is removed if:
1295 - object is not found in m_active_objects (this is actually an
1296 error condition; objects should be set m_removed=true and removed
1297 only after all clients have been informed about removal), or
1298 - object has m_removed=true, or
1299 - object is too far away
1301 for(core::map<u16, bool>::Iterator
1302 i = current_objects.getIterator();
1303 i.atEnd()==false; i++)
1305 u16 id = i.getNode()->getKey();
1306 ServerActiveObject *object = getActiveObject(id);
1309 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1310 <<" object in current_objects is NULL"<<std::endl;
1312 else if(object->m_removed == false)
1314 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1315 /*infostream<<"removed == false"
1316 <<"distance_f = "<<distance_f
1317 <<", radius_f = "<<radius_f<<std::endl;*/
1318 if(distance_f < radius_f)
1324 removed_objects.insert(id, false);
1328 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1330 if(m_active_object_messages.size() == 0)
1331 return ActiveObjectMessage(0);
1333 return m_active_object_messages.pop_front();
1337 ************ Private methods *************
1340 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1344 if(object->getId() == 0)
1346 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1349 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1350 <<"no free ids available"<<std::endl;
1354 object->setId(new_id);
1356 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1358 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1359 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1363 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1364 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1366 m_active_objects.insert(object->getId(), object);
1368 // Add static object to active static list of the block
1369 v3f objectpos = object->getBasePosition();
1370 std::string staticdata = object->getStaticData();
1371 StaticObject s_obj(object->getType(), objectpos, staticdata);
1372 // Add to the block where the object is located in
1373 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1374 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1377 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1378 object->m_static_exists = true;
1379 object->m_static_block = blockpos;
1382 block->setChangedFlag();
1385 infostream<<"ServerEnv: Could not find a block for "
1386 <<"storing newly added static active object"<<std::endl;
1389 return object->getId();
1393 Remove objects that satisfy (m_removed && m_known_by_count==0)
1395 void ServerEnvironment::removeRemovedObjects()
1397 core::list<u16> objects_to_remove;
1398 for(core::map<u16, ServerActiveObject*>::Iterator
1399 i = m_active_objects.getIterator();
1400 i.atEnd()==false; i++)
1402 u16 id = i.getNode()->getKey();
1403 ServerActiveObject* obj = i.getNode()->getValue();
1404 // This shouldn't happen but check it
1407 infostream<<"NULL object found in ServerEnvironment"
1408 <<" while finding removed objects. id="<<id<<std::endl;
1409 // Id to be removed from m_active_objects
1410 objects_to_remove.push_back(id);
1415 We will delete objects that are marked as removed or thatare
1416 waiting for deletion after deactivation
1418 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1422 Delete static data from block if is marked as removed
1424 if(obj->m_static_exists && obj->m_removed)
1426 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1429 block->m_static_objects.remove(id);
1430 block->setChangedFlag();
1434 // If m_known_by_count > 0, don't actually remove.
1435 if(obj->m_known_by_count > 0)
1440 // Id to be removed from m_active_objects
1441 objects_to_remove.push_back(id);
1443 // Remove references from m_active_objects
1444 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1445 i != objects_to_remove.end(); i++)
1447 m_active_objects.remove(*i);
1452 Convert stored objects from blocks near the players to active.
1454 void ServerEnvironment::activateObjects(MapBlock *block)
1458 // Ignore if no stored objects (to not set changed flag)
1459 if(block->m_static_objects.m_stored.size() == 0)
1461 // A list for objects that couldn't be converted to static for some
1462 // reason. They will be stored back.
1463 core::list<StaticObject> new_stored;
1464 // Loop through stored static objects
1465 for(core::list<StaticObject>::Iterator
1466 i = block->m_static_objects.m_stored.begin();
1467 i != block->m_static_objects.m_stored.end(); i++)
1469 /*infostream<<"Server: Creating an active object from "
1470 <<"static data"<<std::endl;*/
1471 StaticObject &s_obj = *i;
1472 // Create an active object from the data
1473 ServerActiveObject *obj = ServerActiveObject::create
1474 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1475 // If couldn't create object, store static data back.
1478 new_stored.push_back(s_obj);
1481 // This will also add the object to the active static list
1482 addActiveObjectRaw(obj, false);
1483 //u16 id = addActiveObjectRaw(obj, false);
1485 // Clear stored list
1486 block->m_static_objects.m_stored.clear();
1487 // Add leftover failed stuff to stored list
1488 for(core::list<StaticObject>::Iterator
1489 i = new_stored.begin();
1490 i != new_stored.end(); i++)
1492 StaticObject &s_obj = *i;
1493 block->m_static_objects.m_stored.push_back(s_obj);
1495 // Block has been modified
1496 // NOTE: No it has not really. Save I/O here.
1497 //block->setChangedFlag();
1501 Convert objects that are not in active blocks to static.
1503 If m_known_by_count != 0, active object is not deleted, but static
1504 data is still updated.
1506 If force_delete is set, active object is deleted nevertheless. It
1507 shall only be set so in the destructor of the environment.
1509 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1511 core::list<u16> objects_to_remove;
1512 for(core::map<u16, ServerActiveObject*>::Iterator
1513 i = m_active_objects.getIterator();
1514 i.atEnd()==false; i++)
1516 ServerActiveObject* obj = i.getNode()->getValue();
1518 // This shouldn't happen but check it
1521 infostream<<"NULL object found in ServerEnvironment"
1527 u16 id = i.getNode()->getKey();
1528 v3f objectpos = obj->getBasePosition();
1530 // The block in which the object resides in
1531 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1533 // If block is active, don't remove
1534 if(m_active_blocks.contains(blockpos_o))
1538 Update the static data
1541 // Delete old static object
1542 MapBlock *oldblock = NULL;
1543 if(obj->m_static_exists)
1545 MapBlock *block = m_map->getBlockNoCreateNoEx
1546 (obj->m_static_block);
1549 block->m_static_objects.remove(id);
1553 // Create new static object
1554 std::string staticdata = obj->getStaticData();
1555 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1556 // Add to the block where the object is located in
1557 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1558 // Get or generate the block
1559 MapBlock *block = m_map->emergeBlock(blockpos);
1561 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1564 // Block not found. Is the old block still ok?
1567 // Load from disk or generate
1569 block = m_map->emergeBlock(blockpos);
1574 block->m_static_objects.insert(0, s_obj);
1575 block->setChangedFlag();
1576 obj->m_static_exists = true;
1577 obj->m_static_block = block->getPos();
1580 infostream<<"ServerEnv: Could not find or generate "
1581 <<"a block for storing static object"<<std::endl;
1582 obj->m_static_exists = false;
1587 Delete active object if not known by some client,
1588 else set pending deactivation
1591 // If known by some client, don't delete.
1592 if(obj->m_known_by_count > 0 && force_delete == false)
1594 obj->m_pending_deactivation = true;
1598 /*infostream<<"Server: Stored static data. Deleting object."
1600 // Delete active object
1602 // Id to be removed from m_active_objects
1603 objects_to_remove.push_back(id);
1606 // Remove references from m_active_objects
1607 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1608 i != objects_to_remove.end(); i++)
1610 m_active_objects.remove(*i);
1621 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1629 ClientEnvironment::~ClientEnvironment()
1631 // delete active objects
1632 for(core::map<u16, ClientActiveObject*>::Iterator
1633 i = m_active_objects.getIterator();
1634 i.atEnd()==false; i++)
1636 delete i.getNode()->getValue();
1643 void ClientEnvironment::addPlayer(Player *player)
1645 DSTACK(__FUNCTION_NAME);
1647 It is a failure if player is local and there already is a local
1650 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1652 Environment::addPlayer(player);
1655 LocalPlayer * ClientEnvironment::getLocalPlayer()
1657 for(core::list<Player*>::Iterator i = m_players.begin();
1658 i != m_players.end(); i++)
1660 Player *player = *i;
1661 if(player->isLocal())
1662 return (LocalPlayer*)player;
1667 void ClientEnvironment::step(float dtime)
1669 DSTACK(__FUNCTION_NAME);
1671 // Get some settings
1672 bool free_move = g_settings->getBool("free_move");
1673 bool footprints = g_settings->getBool("footprints");
1676 LocalPlayer *lplayer = getLocalPlayer();
1678 // collision info queue
1679 core::list<CollisionInfo> player_collisions;
1682 Get the speed the player is going
1684 bool is_climbing = lplayer->is_climbing;
1686 f32 player_speed = 0.001; // just some small value
1687 player_speed = lplayer->getSpeed().getLength();
1690 Maximum position increment
1692 //f32 position_max_increment = 0.05*BS;
1693 f32 position_max_increment = 0.1*BS;
1695 // Maximum time increment (for collision detection etc)
1696 // time = distance / speed
1697 f32 dtime_max_increment = position_max_increment / player_speed;
1699 // Maximum time increment is 10ms or lower
1700 if(dtime_max_increment > 0.01)
1701 dtime_max_increment = 0.01;
1703 // Don't allow overly huge dtime
1707 f32 dtime_downcount = dtime;
1710 Stuff that has a maximum time increment
1719 if(dtime_downcount > dtime_max_increment)
1721 dtime_part = dtime_max_increment;
1722 dtime_downcount -= dtime_part;
1726 dtime_part = dtime_downcount;
1728 Setting this to 0 (no -=dtime_part) disables an infinite loop
1729 when dtime_part is so small that dtime_downcount -= dtime_part
1732 dtime_downcount = 0;
1740 v3f lplayerpos = lplayer->getPosition();
1743 if(free_move == false && is_climbing == false)
1746 v3f speed = lplayer->getSpeed();
1747 if(lplayer->swimming_up == false)
1748 speed.Y -= 9.81 * BS * dtime_part * 2;
1751 if(lplayer->in_water_stable || lplayer->in_water)
1753 f32 max_down = 2.0*BS;
1754 if(speed.Y < -max_down) speed.Y = -max_down;
1757 if(speed.getLength() > max)
1759 speed = speed / speed.getLength() * max;
1763 lplayer->setSpeed(speed);
1768 This also does collision detection.
1770 lplayer->move(dtime_part, *m_map, position_max_increment,
1771 &player_collisions);
1774 while(dtime_downcount > 0.001);
1776 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1778 for(core::list<CollisionInfo>::Iterator
1779 i = player_collisions.begin();
1780 i != player_collisions.end(); i++)
1782 CollisionInfo &info = *i;
1783 if(info.t == COLLISION_FALL)
1785 //f32 tolerance = BS*10; // 2 without damage
1786 f32 tolerance = BS*12; // 3 without damage
1788 if(info.speed > tolerance)
1790 f32 damage_f = (info.speed - tolerance)/BS*factor;
1791 u16 damage = (u16)(damage_f+0.5);
1792 if(lplayer->hp > damage)
1793 lplayer->hp -= damage;
1797 ClientEnvEvent event;
1798 event.type = CEE_PLAYER_DAMAGE;
1799 event.player_damage.amount = damage;
1800 m_client_event_queue.push_back(event);
1806 A quick draft of lava damage
1808 if(m_lava_hurt_interval.step(dtime, 1.0))
1810 v3f pf = lplayer->getPosition();
1812 // Feet, middle and head
1813 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1814 MapNode n1 = m_map->getNodeNoEx(p1);
1815 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1816 MapNode n2 = m_map->getNodeNoEx(p2);
1817 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1818 MapNode n3 = m_map->getNodeNoEx(p2);
1820 u32 damage_per_second = 0;
1821 damage_per_second = MYMAX(damage_per_second,
1822 content_features(n1).damage_per_second);
1823 damage_per_second = MYMAX(damage_per_second,
1824 content_features(n2).damage_per_second);
1825 damage_per_second = MYMAX(damage_per_second,
1826 content_features(n3).damage_per_second);
1828 if(damage_per_second != 0)
1830 ClientEnvEvent event;
1831 event.type = CEE_PLAYER_DAMAGE;
1832 event.player_damage.amount = damage_per_second;
1833 m_client_event_queue.push_back(event);
1838 Stuff that can be done in an arbitarily large dtime
1840 for(core::list<Player*>::Iterator i = m_players.begin();
1841 i != m_players.end(); i++)
1843 Player *player = *i;
1844 v3f playerpos = player->getPosition();
1847 Handle non-local players
1849 if(player->isLocal() == false)
1852 player->move(dtime, *m_map, 100*BS);
1856 // Update lighting on all players on client
1857 u8 light = LIGHT_MAX;
1860 v3s16 p = player->getLightPosition();
1861 MapNode n = m_map->getNode(p);
1862 light = n.getLightBlend(getDayNightRatio());
1864 catch(InvalidPositionException &e) {}
1865 player->updateLight(light);
1868 Add footsteps to grass
1872 // Get node that is at BS/4 under player
1873 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1875 MapNode n = m_map->getNode(bottompos);
1876 if(n.getContent() == CONTENT_GRASS)
1878 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1879 m_map->setNode(bottompos, n);
1880 // Update mesh on client
1881 if(m_map->mapType() == MAPTYPE_CLIENT)
1883 v3s16 p_blocks = getNodeBlockPos(bottompos);
1884 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1885 //b->updateMesh(getDayNightRatio());
1886 b->setMeshExpired(true);
1890 catch(InvalidPositionException &e)
1897 Step active objects and update lighting of them
1900 for(core::map<u16, ClientActiveObject*>::Iterator
1901 i = m_active_objects.getIterator();
1902 i.atEnd()==false; i++)
1904 ClientActiveObject* obj = i.getNode()->getValue();
1906 obj->step(dtime, this);
1908 if(m_active_object_light_update_interval.step(dtime, 0.21))
1911 //u8 light = LIGHT_MAX;
1915 v3s16 p = obj->getLightPosition();
1916 MapNode n = m_map->getNode(p);
1917 light = n.getLightBlend(getDayNightRatio());
1919 catch(InvalidPositionException &e) {}
1920 obj->updateLight(light);
1925 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1927 m_map->updateMeshes(blockpos, getDayNightRatio());
1930 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1932 m_map->expireMeshes(only_daynight_diffed);
1935 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1937 core::map<u16, ClientActiveObject*>::Node *n;
1938 n = m_active_objects.find(id);
1941 return n->getValue();
1944 bool isFreeClientActiveObjectId(u16 id,
1945 core::map<u16, ClientActiveObject*> &objects)
1950 for(core::map<u16, ClientActiveObject*>::Iterator
1951 i = objects.getIterator();
1952 i.atEnd()==false; i++)
1954 if(i.getNode()->getKey() == id)
1960 u16 getFreeClientActiveObjectId(
1961 core::map<u16, ClientActiveObject*> &objects)
1966 if(isFreeClientActiveObjectId(new_id, objects))
1976 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1979 if(object->getId() == 0)
1981 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1984 infostream<<"ClientEnvironment::addActiveObject(): "
1985 <<"no free ids available"<<std::endl;
1989 object->setId(new_id);
1991 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1993 infostream<<"ClientEnvironment::addActiveObject(): "
1994 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1998 infostream<<"ClientEnvironment::addActiveObject(): "
1999 <<"added (id="<<object->getId()<<")"<<std::endl;
2000 m_active_objects.insert(object->getId(), object);
2001 object->addToScene(m_smgr);
2002 return object->getId();
2005 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2006 const std::string &init_data)
2008 ClientActiveObject* obj = ClientActiveObject::create(type);
2011 infostream<<"ClientEnvironment::addActiveObject(): "
2012 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2019 obj->initialize(init_data);
2021 addActiveObject(obj);
2024 void ClientEnvironment::removeActiveObject(u16 id)
2026 infostream<<"ClientEnvironment::removeActiveObject(): "
2027 <<"id="<<id<<std::endl;
2028 ClientActiveObject* obj = getActiveObject(id);
2031 infostream<<"ClientEnvironment::removeActiveObject(): "
2032 <<"id="<<id<<" not found"<<std::endl;
2035 obj->removeFromScene();
2037 m_active_objects.remove(id);
2040 void ClientEnvironment::processActiveObjectMessage(u16 id,
2041 const std::string &data)
2043 ClientActiveObject* obj = getActiveObject(id);
2046 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2047 <<" got message for id="<<id<<", which doesn't exist."
2051 obj->processMessage(data);
2055 Callbacks for activeobjects
2058 void ClientEnvironment::damageLocalPlayer(u8 damage)
2060 LocalPlayer *lplayer = getLocalPlayer();
2063 if(lplayer->hp > damage)
2064 lplayer->hp -= damage;
2068 ClientEnvEvent event;
2069 event.type = CEE_PLAYER_DAMAGE;
2070 event.player_damage.amount = damage;
2071 m_client_event_queue.push_back(event);
2075 Client likes to call these
2078 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2079 core::array<DistanceSortedActiveObject> &dest)
2081 for(core::map<u16, ClientActiveObject*>::Iterator
2082 i = m_active_objects.getIterator();
2083 i.atEnd()==false; i++)
2085 ClientActiveObject* obj = i.getNode()->getValue();
2087 f32 d = (obj->getPosition() - origin).getLength();
2092 DistanceSortedActiveObject dso(obj, d);
2094 dest.push_back(dso);
2098 ClientEnvEvent ClientEnvironment::getClientEvent()
2100 if(m_client_event_queue.size() == 0)
2102 ClientEnvEvent event;
2103 event.type = CEE_NONE;
2106 return m_client_event_queue.pop_front();
2109 #endif // #ifndef SERVER