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"
31 Environment::Environment():
36 Environment::~Environment()
39 for(core::list<Player*>::Iterator i = m_players.begin();
40 i != m_players.end(); i++)
46 void Environment::addPlayer(Player *player)
48 DSTACK(__FUNCTION_NAME);
50 Check that peer_ids are unique.
51 Also check that names are unique.
52 Exception: there can be multiple players with peer_id=0
54 // If peer id is non-zero, it has to be unique.
55 if(player->peer_id != 0)
56 assert(getPlayer(player->peer_id) == NULL);
57 // Name has to be unique.
58 assert(getPlayer(player->getName()) == NULL);
60 m_players.push_back(player);
63 void Environment::removePlayer(u16 peer_id)
65 DSTACK(__FUNCTION_NAME);
67 for(core::list<Player*>::Iterator i = m_players.begin();
68 i != m_players.end(); i++)
71 if(player->peer_id != peer_id)
76 // See if there is an another one
77 // (shouldn't be, but just to be sure)
82 Player * Environment::getPlayer(u16 peer_id)
84 for(core::list<Player*>::Iterator i = m_players.begin();
85 i != m_players.end(); i++)
88 if(player->peer_id == peer_id)
94 Player * Environment::getPlayer(const char *name)
96 for(core::list<Player*>::Iterator i = m_players.begin();
97 i != m_players.end(); i++)
100 if(strcmp(player->getName(), name) == 0)
106 Player * Environment::getRandomConnectedPlayer()
108 core::list<Player*> connected_players = getPlayers(true);
109 u32 chosen_one = myrand() % connected_players.size();
111 for(core::list<Player*>::Iterator
112 i = connected_players.begin();
113 i != connected_players.end(); i++)
125 Player * Environment::getNearestConnectedPlayer(v3f pos)
127 core::list<Player*> connected_players = getPlayers(true);
129 Player *nearest_player = NULL;
130 for(core::list<Player*>::Iterator
131 i = connected_players.begin();
132 i != connected_players.end(); i++)
135 f32 d = player->getPosition().getDistanceFrom(pos);
136 if(d < nearest_d || nearest_player == NULL)
139 nearest_player = player;
142 return nearest_player;
145 core::list<Player*> Environment::getPlayers()
150 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
152 core::list<Player*> newlist;
153 for(core::list<Player*>::Iterator
154 i = m_players.begin();
155 i != m_players.end(); i++)
159 if(ignore_disconnected)
161 // Ignore disconnected players
162 if(player->peer_id == 0)
166 newlist.push_back(player);
171 void Environment::printPlayers(std::ostream &o)
173 o<<"Players in environment:"<<std::endl;
174 for(core::list<Player*>::Iterator i = m_players.begin();
175 i != m_players.end(); i++)
178 o<<"Player peer_id="<<player->peer_id<<std::endl;
182 /*void Environment::setDayNightRatio(u32 r)
184 getDayNightRatio() = r;
187 u32 Environment::getDayNightRatio()
189 //return getDayNightRatio();
190 return time_to_daynight_ratio(m_time_of_day);
197 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
200 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
201 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
202 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
209 void ActiveBlockList::update(core::list<v3s16> &active_positions,
211 core::map<v3s16, bool> &blocks_removed,
212 core::map<v3s16, bool> &blocks_added)
217 core::map<v3s16, bool> newlist;
218 for(core::list<v3s16>::Iterator i = active_positions.begin();
219 i != active_positions.end(); i++)
221 fillRadiusBlock(*i, radius, newlist);
225 Find out which blocks on the old list are not on the new list
227 // Go through old list
228 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
229 i.atEnd()==false; i++)
231 v3s16 p = i.getNode()->getKey();
232 // If not on new list, it's been removed
233 if(newlist.find(p) == NULL)
234 blocks_removed.insert(p, true);
238 Find out which blocks on the new list are not on the old list
240 // Go through new list
241 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
242 i.atEnd()==false; i++)
244 v3s16 p = i.getNode()->getKey();
245 // If not on old list, it's been added
246 if(m_list.find(p) == NULL)
247 blocks_added.insert(p, true);
254 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
255 i.atEnd()==false; i++)
257 v3s16 p = i.getNode()->getKey();
258 m_list.insert(p, true);
266 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
269 m_random_spawn_timer(3),
270 m_send_recommended_timer(0),
272 m_game_time_fraction_counter(0)
276 ServerEnvironment::~ServerEnvironment()
278 // Clear active block list.
279 // This makes the next one delete all active objects.
280 m_active_blocks.clear();
282 // Convert all objects to static and delete the active objects
283 deactivateFarObjects(true);
289 void ServerEnvironment::serializePlayers(const std::string &savedir)
291 std::string players_path = savedir + "/players";
292 fs::CreateDir(players_path);
294 core::map<Player*, bool> saved_players;
296 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
297 for(u32 i=0; i<player_files.size(); i++)
299 if(player_files[i].dir)
302 // Full path to this file
303 std::string path = players_path + "/" + player_files[i].name;
305 //dstream<<"Checking player file "<<path<<std::endl;
307 // Load player to see what is its name
308 ServerRemotePlayer testplayer;
310 // Open file and deserialize
311 std::ifstream is(path.c_str(), std::ios_base::binary);
312 if(is.good() == false)
314 dstream<<"Failed to read "<<path<<std::endl;
317 testplayer.deSerialize(is);
320 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
322 // Search for the player
323 std::string playername = testplayer.getName();
324 Player *player = getPlayer(playername.c_str());
327 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
331 //dstream<<"Found matching player, overwriting."<<std::endl;
333 // OK, found. Save player there.
335 // Open file and serialize
336 std::ofstream os(path.c_str(), std::ios_base::binary);
337 if(os.good() == false)
339 dstream<<"Failed to overwrite "<<path<<std::endl;
342 player->serialize(os);
343 saved_players.insert(player, true);
347 for(core::list<Player*>::Iterator i = m_players.begin();
348 i != m_players.end(); i++)
351 if(saved_players.find(player) != NULL)
353 /*dstream<<"Player "<<player->getName()
354 <<" was already saved."<<std::endl;*/
357 std::string playername = player->getName();
358 // Don't save unnamed player
361 //dstream<<"Not saving unnamed player."<<std::endl;
367 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
368 playername = "player";
369 std::string path = players_path + "/" + playername;
371 for(u32 i=0; i<1000; i++)
373 if(fs::PathExists(path) == false)
378 path = players_path + "/" + playername + itos(i);
382 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
387 /*dstream<<"Saving player "<<player->getName()<<" to "
389 // Open file and serialize
390 std::ofstream os(path.c_str(), std::ios_base::binary);
391 if(os.good() == false)
393 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
396 player->serialize(os);
397 saved_players.insert(player, true);
401 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
404 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
406 std::string players_path = savedir + "/players";
408 core::map<Player*, bool> saved_players;
410 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
411 for(u32 i=0; i<player_files.size(); i++)
413 if(player_files[i].dir)
416 // Full path to this file
417 std::string path = players_path + "/" + player_files[i].name;
419 dstream<<"Checking player file "<<path<<std::endl;
421 // Load player to see what is its name
422 ServerRemotePlayer testplayer;
424 // Open file and deserialize
425 std::ifstream is(path.c_str(), std::ios_base::binary);
426 if(is.good() == false)
428 dstream<<"Failed to read "<<path<<std::endl;
431 testplayer.deSerialize(is);
434 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
436 dstream<<"Not loading player with invalid name: "
437 <<testplayer.getName()<<std::endl;
440 dstream<<"Loaded test player with name "<<testplayer.getName()
443 // Search for the player
444 std::string playername = testplayer.getName();
445 Player *player = getPlayer(playername.c_str());
446 bool newplayer = false;
449 dstream<<"Is a new player"<<std::endl;
450 player = new ServerRemotePlayer();
456 dstream<<"Reading player "<<testplayer.getName()<<" from "
458 // Open file and deserialize
459 std::ifstream is(path.c_str(), std::ios_base::binary);
460 if(is.good() == false)
462 dstream<<"Failed to read "<<path<<std::endl;
465 player->deSerialize(is);
473 void ServerEnvironment::saveMeta(const std::string &savedir)
475 std::string path = savedir + "/env_meta.txt";
477 // Open file and serialize
478 std::ofstream os(path.c_str(), std::ios_base::binary);
479 if(os.good() == false)
481 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
483 throw SerializationError("Couldn't save env meta");
487 args.setU64("game_time", m_game_time);
488 args.setU64("time_of_day", getTimeOfDay());
493 void ServerEnvironment::loadMeta(const std::string &savedir)
495 std::string path = savedir + "/env_meta.txt";
497 // Open file and deserialize
498 std::ifstream is(path.c_str(), std::ios_base::binary);
499 if(is.good() == false)
501 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
503 throw SerializationError("Couldn't load env meta");
511 throw SerializationError
512 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
514 std::getline(is, line);
515 std::string trimmedline = trim(line);
516 if(trimmedline == "EnvArgsEnd")
518 args.parseConfigLine(line);
522 m_game_time = args.getU64("game_time");
523 }catch(SettingNotFoundException &e){
524 // Getting this is crucial, otherwise timestamps are useless
525 throw SerializationError("Couldn't load env meta game_time");
529 m_time_of_day = args.getU64("time_of_day");
530 }catch(SettingNotFoundException &e){
531 // This is not as important
532 m_time_of_day = 9000;
537 // This is probably very useless
538 void spawnRandomObjects(MapBlock *block)
540 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
541 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
543 bool last_node_walkable = false;
544 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
547 MapNode n = block->getNodeNoEx(p);
548 if(n.getContent() == CONTENT_IGNORE)
550 if(content_features(n).liquid_type != LIQUID_NONE)
552 if(content_features(n).walkable)
554 last_node_walkable = true;
557 if(last_node_walkable)
559 // If block contains light information
560 if(content_features(n).param_type == CPT_LIGHT)
562 if(n.getLight(LIGHTBANK_DAY) <= 5)
564 if(myrand() % 1000 == 0)
566 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
568 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
569 std::string data = obj->getStaticData();
570 StaticObject s_obj(obj->getType(),
571 obj->getBasePosition(), data);
573 block->m_static_objects.insert(0, s_obj);
575 block->setChangedFlag();
580 last_node_walkable = false;
586 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
588 // Get time difference
590 u32 stamp = block->getTimestamp();
591 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
592 dtime_s = m_game_time - block->getTimestamp();
593 dtime_s += additional_dtime;
595 // Set current time as timestamp (and let it set ChangedFlag)
596 block->setTimestamp(m_game_time);
598 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
600 // Activate stored objects
601 activateObjects(block);
604 bool changed = block->m_node_metadata.step((float)dtime_s);
608 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
609 event.p = block->getPos();
610 m_map->dispatchEvent(&event);
612 block->setChangedFlag();
615 // TODO: Do something
616 // TODO: Implement usage of ActiveBlockModifier
618 // Here's a quick demonstration
620 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
621 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
622 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
624 v3s16 p = p0 + block->getPosRelative();
625 MapNode n = block->getNodeNoEx(p0);
628 // Convert all mud under proper day lighting to grass
629 if(n.getContent() == CONTENT_MUD)
633 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
634 if(content_features(n_top).air_equivalent &&
635 n_top.getLight(LIGHTBANK_DAY) >= 13)
637 n.setContent(CONTENT_GRASS);
638 m_map->addNodeWithEvent(p, n);
646 static void getMob_dungeon_master(Settings &properties)
648 properties.set("looks", "dungeon_master");
649 properties.setFloat("yaw", 1.57);
650 properties.setFloat("hp", 20);
651 properties.setBool("bright_shooting", true);
652 properties.set("shoot_type", "fireball");
653 properties.set("shoot_y", "0.7");
654 properties.set("player_hit_damage", "1");
655 properties.set("player_hit_distance", "1.0");
656 properties.set("player_hit_interval", "0.5");
659 void ServerEnvironment::step(float dtime)
661 DSTACK(__FUNCTION_NAME);
663 //TimeTaker timer("ServerEnv step");
666 bool footprints = g_settings->getBool("footprints");
672 m_game_time_fraction_counter += dtime;
673 u32 inc_i = (u32)m_game_time_fraction_counter;
674 m_game_time += inc_i;
675 m_game_time_fraction_counter -= (float)inc_i;
681 for(core::list<Player*>::Iterator i = m_players.begin();
682 i != m_players.end(); i++)
686 // Ignore disconnected players
687 if(player->peer_id == 0)
690 v3f playerpos = player->getPosition();
693 player->move(dtime, *m_map, 100*BS);
696 Add footsteps to grass
700 // Get node that is at BS/4 under player
701 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
703 MapNode n = m_map->getNode(bottompos);
704 if(n.getContent() == CONTENT_GRASS)
706 n.setContent(CONTENT_GRASS_FOOTSTEPS);
707 m_map->setNode(bottompos, n);
710 catch(InvalidPositionException &e)
717 Manage active block list
719 if(m_active_blocks_management_interval.step(dtime, 2.0))
722 Get player block positions
724 core::list<v3s16> players_blockpos;
725 for(core::list<Player*>::Iterator
726 i = m_players.begin();
727 i != m_players.end(); i++)
730 // Ignore disconnected players
731 if(player->peer_id == 0)
733 v3s16 blockpos = getNodeBlockPos(
734 floatToInt(player->getPosition(), BS));
735 players_blockpos.push_back(blockpos);
739 Update list of active blocks, collecting changes
741 const s16 active_block_range = g_settings->getS16("active_block_range");
742 core::map<v3s16, bool> blocks_removed;
743 core::map<v3s16, bool> blocks_added;
744 m_active_blocks.update(players_blockpos, active_block_range,
745 blocks_removed, blocks_added);
748 Handle removed blocks
751 // Convert active objects that are no more in active blocks to static
752 deactivateFarObjects(false);
754 for(core::map<v3s16, bool>::Iterator
755 i = blocks_removed.getIterator();
756 i.atEnd()==false; i++)
758 v3s16 p = i.getNode()->getKey();
760 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
761 <<") became inactive"<<std::endl;*/
763 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
767 // Set current time as timestamp (and let it set ChangedFlag)
768 block->setTimestamp(m_game_time);
775 for(core::map<v3s16, bool>::Iterator
776 i = blocks_added.getIterator();
777 i.atEnd()==false; i++)
779 v3s16 p = i.getNode()->getKey();
781 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
782 <<") became active"<<std::endl;*/
784 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
788 activateBlock(block);
793 Mess around in active blocks
795 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
799 for(core::map<v3s16, bool>::Iterator
800 i = m_active_blocks.m_list.getIterator();
801 i.atEnd()==false; i++)
803 v3s16 p = i.getNode()->getKey();
805 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
806 <<") being handled"<<std::endl;*/
808 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
812 // Reset block usage timer
813 block->resetUsageTimer();
815 // Set current time as timestamp
816 block->setTimestampNoChangedFlag(m_game_time);
819 bool changed = block->m_node_metadata.step(dtime);
823 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
825 m_map->dispatchEvent(&event);
827 block->setChangedFlag();
831 if(m_active_blocks_test_interval.step(dtime, 10.0))
833 //float dtime = 10.0;
835 for(core::map<v3s16, bool>::Iterator
836 i = m_active_blocks.m_list.getIterator();
837 i.atEnd()==false; i++)
839 v3s16 p = i.getNode()->getKey();
841 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
842 <<") being handled"<<std::endl;*/
844 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
848 // Set current time as timestamp
849 block->setTimestampNoChangedFlag(m_game_time);
854 Note that map modifications should be done using the event-
855 making map methods so that the server gets information
858 Reading can be done quickly directly from the block.
860 Everything should bind to inside this single content
861 searching loop to keep things fast.
863 // TODO: Implement usage of ActiveBlockModifier
865 // Find out how many objects the block contains
866 u32 active_object_count = block->m_static_objects.m_active.size();
867 // Find out how many objects this and all the neighbors contain
868 u32 active_object_count_wider = 0;
869 for(s16 x=-1; x<=1; x++)
870 for(s16 y=-1; y<=1; y++)
871 for(s16 z=-1; z<=1; z++)
873 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
876 active_object_count_wider +=
877 block->m_static_objects.m_active.size();
881 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
882 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
883 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
885 v3s16 p = p0 + block->getPosRelative();
886 MapNode n = block->getNodeNoEx(p0);
890 Convert mud under proper lighting to grass
892 if(n.getContent() == CONTENT_MUD)
896 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
897 if(content_features(n_top).air_equivalent &&
898 n_top.getLightBlend(getDayNightRatio()) >= 13)
900 n.setContent(CONTENT_GRASS);
901 m_map->addNodeWithEvent(p, n);
906 Convert grass into mud if under something else than air
908 if(n.getContent() == CONTENT_GRASS)
910 //if(myrand()%20 == 0)
912 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
913 if(content_features(n_top).air_equivalent == false)
915 n.setContent(CONTENT_MUD);
916 m_map->addNodeWithEvent(p, n);
921 Rats spawn around regular trees
923 if(n.getContent() == CONTENT_TREE ||
924 n.getContent() == CONTENT_JUNGLETREE)
926 if(myrand()%200 == 0 && active_object_count_wider == 0)
928 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
929 0, myrand_range(-2, 2));
930 MapNode n1 = m_map->getNodeNoEx(p1);
931 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
932 if(n1b.getContent() == CONTENT_GRASS &&
933 n1.getContent() == CONTENT_AIR)
935 v3f pos = intToFloat(p1, BS);
936 ServerActiveObject *obj = new RatSAO(this, 0, pos);
937 addActiveObject(obj);
942 Fun things spawn in caves and dungeons
944 if(n.getContent() == CONTENT_STONE ||
945 n.getContent() == CONTENT_MOSSYCOBBLE)
947 if(myrand()%200 == 0 && active_object_count_wider == 0)
949 v3s16 p1 = p + v3s16(0,1,0);
950 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
951 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
952 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
953 if(n1a.getContent() == CONTENT_AIR &&
954 n1b.getContent() == CONTENT_AIR)
956 v3f pos = intToFloat(p1, BS);
958 if(i == 0 || i == 1){
960 getMob_dungeon_master(properties);
961 ServerActiveObject *obj = new MobV2SAO(
962 this, 0, pos, &properties);
963 addActiveObject(obj);
964 } else if(i == 2 || i == 3){
965 for(int j=0; j<3; j++){
966 ServerActiveObject *obj = new RatSAO(
968 addActiveObject(obj);
971 ServerActiveObject *obj = new Oerkki1SAO(
973 addActiveObject(obj);
980 Make trees from saplings!
982 if(n.getContent() == CONTENT_SAPLING)
986 core::map<v3s16, MapBlock*> modified_blocks;
988 ManualMapVoxelManipulator vmanip(m_map);
989 v3s16 tree_blockp = getNodeBlockPos(tree_p);
990 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
991 bool is_apple_tree = myrand()%4 == 0;
992 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
993 vmanip.blitBackAll(&modified_blocks);
996 core::map<v3s16, MapBlock*> lighting_modified_blocks;
997 for(core::map<v3s16, MapBlock*>::Iterator
998 i = modified_blocks.getIterator();
999 i.atEnd() == false; i++)
1001 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1003 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1005 // Send a MEET_OTHER event
1007 event.type = MEET_OTHER;
1008 for(core::map<v3s16, MapBlock*>::Iterator
1009 i = modified_blocks.getIterator();
1010 i.atEnd() == false; i++)
1012 v3s16 p = i.getNode()->getKey();
1013 event.modified_blocks.insert(p, true);
1015 m_map->dispatchEvent(&event);
1026 //TimeTaker timer("Step active objects");
1028 // This helps the objects to send data at the same time
1029 bool send_recommended = false;
1030 m_send_recommended_timer += dtime;
1031 if(m_send_recommended_timer > 0.10)
1033 m_send_recommended_timer = 0;
1034 send_recommended = true;
1037 for(core::map<u16, ServerActiveObject*>::Iterator
1038 i = m_active_objects.getIterator();
1039 i.atEnd()==false; i++)
1041 ServerActiveObject* obj = i.getNode()->getValue();
1042 // Don't step if is to be removed or stored statically
1043 if(obj->m_removed || obj->m_pending_deactivation)
1046 obj->step(dtime, send_recommended);
1047 // Read messages from object
1048 while(obj->m_messages_out.size() > 0)
1050 m_active_object_messages.push_back(
1051 obj->m_messages_out.pop_front());
1057 Manage active objects
1059 if(m_object_management_interval.step(dtime, 0.5))
1062 Remove objects that satisfy (m_removed && m_known_by_count==0)
1064 removeRemovedObjects();
1067 if(g_settings->getBool("enable_experimental"))
1074 m_random_spawn_timer -= dtime;
1075 if(m_random_spawn_timer < 0)
1077 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1078 //m_random_spawn_timer += 2.0;
1079 m_random_spawn_timer += 200.0;
1085 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1086 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1087 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1089 Player *player = getRandomConnectedPlayer();
1092 pos = player->getPosition();
1094 myrand_range(-3,3)*BS,
1096 myrand_range(-3,3)*BS
1100 Create a ServerActiveObject
1103 //TestSAO *obj = new TestSAO(this, 0, pos);
1104 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1105 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1106 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1107 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1109 dstream<<DTIME<<"INFO: Server: Spawning MobV2SAO at "
1110 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1112 Settings properties;
1113 getMob_dungeon_master(properties);
1114 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1115 addActiveObject(obj);
1119 } // enable_experimental
1122 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1124 core::map<u16, ServerActiveObject*>::Node *n;
1125 n = m_active_objects.find(id);
1128 return n->getValue();
1131 bool isFreeServerActiveObjectId(u16 id,
1132 core::map<u16, ServerActiveObject*> &objects)
1137 for(core::map<u16, ServerActiveObject*>::Iterator
1138 i = objects.getIterator();
1139 i.atEnd()==false; i++)
1141 if(i.getNode()->getKey() == id)
1147 u16 getFreeServerActiveObjectId(
1148 core::map<u16, ServerActiveObject*> &objects)
1153 if(isFreeServerActiveObjectId(new_id, objects))
1163 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1166 u16 id = addActiveObjectRaw(object, true);
1170 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1174 v3f objectpos = obj->getBasePosition();
1176 // The block in which the object resides in
1177 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1180 Update the static data
1183 // Create new static object
1184 std::string staticdata = obj->getStaticData();
1185 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1186 // Add to the block where the object is located in
1187 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1188 // Get or generate the block
1189 MapBlock *block = m_map->emergeBlock(blockpos);
1191 bool succeeded = false;
1195 block->m_static_objects.insert(0, s_obj);
1196 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1200 dstream<<"WARNING: ServerEnvironment::addActiveObjectAsStatic: "
1201 <<"Could not find or generate "
1202 <<"a block for storing static object"<<std::endl;
1212 Finds out what new objects have been added to
1213 inside a radius around a position
1215 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1216 core::map<u16, bool> ¤t_objects,
1217 core::map<u16, bool> &added_objects)
1219 v3f pos_f = intToFloat(pos, BS);
1220 f32 radius_f = radius * BS;
1222 Go through the object list,
1223 - discard m_removed objects,
1224 - discard objects that are too far away,
1225 - discard objects that are found in current_objects.
1226 - add remaining objects to added_objects
1228 for(core::map<u16, ServerActiveObject*>::Iterator
1229 i = m_active_objects.getIterator();
1230 i.atEnd()==false; i++)
1232 u16 id = i.getNode()->getKey();
1234 ServerActiveObject *object = i.getNode()->getValue();
1237 // Discard if removed
1238 if(object->m_removed)
1240 // Discard if too far
1241 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1242 if(distance_f > radius_f)
1244 // Discard if already on current_objects
1245 core::map<u16, bool>::Node *n;
1246 n = current_objects.find(id);
1249 // Add to added_objects
1250 added_objects.insert(id, false);
1255 Finds out what objects have been removed from
1256 inside a radius around a position
1258 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1259 core::map<u16, bool> ¤t_objects,
1260 core::map<u16, bool> &removed_objects)
1262 v3f pos_f = intToFloat(pos, BS);
1263 f32 radius_f = radius * BS;
1265 Go through current_objects; object is removed if:
1266 - object is not found in m_active_objects (this is actually an
1267 error condition; objects should be set m_removed=true and removed
1268 only after all clients have been informed about removal), or
1269 - object has m_removed=true, or
1270 - object is too far away
1272 for(core::map<u16, bool>::Iterator
1273 i = current_objects.getIterator();
1274 i.atEnd()==false; i++)
1276 u16 id = i.getNode()->getKey();
1277 ServerActiveObject *object = getActiveObject(id);
1280 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1281 <<" object in current_objects is NULL"<<std::endl;
1283 else if(object->m_removed == false)
1285 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1286 /*dstream<<"removed == false"
1287 <<"distance_f = "<<distance_f
1288 <<", radius_f = "<<radius_f<<std::endl;*/
1289 if(distance_f < radius_f)
1295 removed_objects.insert(id, false);
1299 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1301 if(m_active_object_messages.size() == 0)
1302 return ActiveObjectMessage(0);
1304 return m_active_object_messages.pop_front();
1308 ************ Private methods *************
1311 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1315 if(object->getId() == 0)
1317 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1320 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1321 <<"no free ids available"<<std::endl;
1325 object->setId(new_id);
1327 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1329 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1330 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1334 /*dstream<<"INFO: ServerEnvironment::addActiveObjectRaw(): "
1335 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1337 m_active_objects.insert(object->getId(), object);
1339 // Add static object to active static list of the block
1340 v3f objectpos = object->getBasePosition();
1341 std::string staticdata = object->getStaticData();
1342 StaticObject s_obj(object->getType(), objectpos, staticdata);
1343 // Add to the block where the object is located in
1344 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1345 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1348 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1349 object->m_static_exists = true;
1350 object->m_static_block = blockpos;
1353 block->setChangedFlag();
1356 dstream<<"WARNING: ServerEnv: Could not find a block for "
1357 <<"storing newly added static active object"<<std::endl;
1360 return object->getId();
1364 Remove objects that satisfy (m_removed && m_known_by_count==0)
1366 void ServerEnvironment::removeRemovedObjects()
1368 core::list<u16> objects_to_remove;
1369 for(core::map<u16, ServerActiveObject*>::Iterator
1370 i = m_active_objects.getIterator();
1371 i.atEnd()==false; i++)
1373 u16 id = i.getNode()->getKey();
1374 ServerActiveObject* obj = i.getNode()->getValue();
1375 // This shouldn't happen but check it
1378 dstream<<"WARNING: NULL object found in ServerEnvironment"
1379 <<" while finding removed objects. id="<<id<<std::endl;
1380 // Id to be removed from m_active_objects
1381 objects_to_remove.push_back(id);
1386 We will delete objects that are marked as removed or thatare
1387 waiting for deletion after deactivation
1389 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1393 Delete static data from block if is marked as removed
1395 if(obj->m_static_exists && obj->m_removed)
1397 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1400 block->m_static_objects.remove(id);
1401 block->setChangedFlag();
1405 // If m_known_by_count > 0, don't actually remove.
1406 if(obj->m_known_by_count > 0)
1411 // Id to be removed from m_active_objects
1412 objects_to_remove.push_back(id);
1414 // Remove references from m_active_objects
1415 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1416 i != objects_to_remove.end(); i++)
1418 m_active_objects.remove(*i);
1423 Convert stored objects from blocks near the players to active.
1425 void ServerEnvironment::activateObjects(MapBlock *block)
1429 // Ignore if no stored objects (to not set changed flag)
1430 if(block->m_static_objects.m_stored.size() == 0)
1432 // A list for objects that couldn't be converted to static for some
1433 // reason. They will be stored back.
1434 core::list<StaticObject> new_stored;
1435 // Loop through stored static objects
1436 for(core::list<StaticObject>::Iterator
1437 i = block->m_static_objects.m_stored.begin();
1438 i != block->m_static_objects.m_stored.end(); i++)
1440 /*dstream<<"INFO: Server: Creating an active object from "
1441 <<"static data"<<std::endl;*/
1442 StaticObject &s_obj = *i;
1443 // Create an active object from the data
1444 ServerActiveObject *obj = ServerActiveObject::create
1445 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1446 // If couldn't create object, store static data back.
1449 new_stored.push_back(s_obj);
1452 // This will also add the object to the active static list
1453 addActiveObjectRaw(obj, false);
1454 //u16 id = addActiveObjectRaw(obj, false);
1456 // Clear stored list
1457 block->m_static_objects.m_stored.clear();
1458 // Add leftover failed stuff to stored list
1459 for(core::list<StaticObject>::Iterator
1460 i = new_stored.begin();
1461 i != new_stored.end(); i++)
1463 StaticObject &s_obj = *i;
1464 block->m_static_objects.m_stored.push_back(s_obj);
1466 // Block has been modified
1467 // NOTE: No it has not really. Save I/O here.
1468 //block->setChangedFlag();
1472 Convert objects that are not in active blocks to static.
1474 If m_known_by_count != 0, active object is not deleted, but static
1475 data is still updated.
1477 If force_delete is set, active object is deleted nevertheless. It
1478 shall only be set so in the destructor of the environment.
1480 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1482 core::list<u16> objects_to_remove;
1483 for(core::map<u16, ServerActiveObject*>::Iterator
1484 i = m_active_objects.getIterator();
1485 i.atEnd()==false; i++)
1487 ServerActiveObject* obj = i.getNode()->getValue();
1489 // This shouldn't happen but check it
1492 dstream<<"WARNING: NULL object found in ServerEnvironment"
1498 u16 id = i.getNode()->getKey();
1499 v3f objectpos = obj->getBasePosition();
1501 // The block in which the object resides in
1502 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1504 // If block is active, don't remove
1505 if(m_active_blocks.contains(blockpos_o))
1509 Update the static data
1512 // Delete old static object
1513 MapBlock *oldblock = NULL;
1514 if(obj->m_static_exists)
1516 MapBlock *block = m_map->getBlockNoCreateNoEx
1517 (obj->m_static_block);
1520 block->m_static_objects.remove(id);
1524 // Create new static object
1525 std::string staticdata = obj->getStaticData();
1526 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1527 // Add to the block where the object is located in
1528 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1529 // Get or generate the block
1530 MapBlock *block = m_map->emergeBlock(blockpos);
1532 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1535 // Block not found. Is the old block still ok?
1538 // Load from disk or generate
1540 block = m_map->emergeBlock(blockpos);
1545 block->m_static_objects.insert(0, s_obj);
1546 block->setChangedFlag();
1547 obj->m_static_exists = true;
1548 obj->m_static_block = block->getPos();
1551 dstream<<"WARNING: ServerEnv: Could not find or generate "
1552 <<"a block for storing static object"<<std::endl;
1553 obj->m_static_exists = false;
1558 Delete active object if not known by some client,
1559 else set pending deactivation
1562 // If known by some client, don't delete.
1563 if(obj->m_known_by_count > 0 && force_delete == false)
1565 obj->m_pending_deactivation = true;
1569 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1571 // Delete active object
1573 // Id to be removed from m_active_objects
1574 objects_to_remove.push_back(id);
1577 // Remove references from m_active_objects
1578 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1579 i != objects_to_remove.end(); i++)
1581 m_active_objects.remove(*i);
1592 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1600 ClientEnvironment::~ClientEnvironment()
1602 // delete active objects
1603 for(core::map<u16, ClientActiveObject*>::Iterator
1604 i = m_active_objects.getIterator();
1605 i.atEnd()==false; i++)
1607 delete i.getNode()->getValue();
1614 void ClientEnvironment::addPlayer(Player *player)
1616 DSTACK(__FUNCTION_NAME);
1618 It is a failure if player is local and there already is a local
1621 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1623 Environment::addPlayer(player);
1626 LocalPlayer * ClientEnvironment::getLocalPlayer()
1628 for(core::list<Player*>::Iterator i = m_players.begin();
1629 i != m_players.end(); i++)
1631 Player *player = *i;
1632 if(player->isLocal())
1633 return (LocalPlayer*)player;
1638 void ClientEnvironment::step(float dtime)
1640 DSTACK(__FUNCTION_NAME);
1642 // Get some settings
1643 bool free_move = g_settings->getBool("free_move");
1644 bool footprints = g_settings->getBool("footprints");
1647 LocalPlayer *lplayer = getLocalPlayer();
1649 // collision info queue
1650 core::list<CollisionInfo> player_collisions;
1653 Get the speed the player is going
1655 bool is_climbing = lplayer->is_climbing;
1658 Check if the player is frozen (don't apply physics)
1660 bool is_frozen = lplayer->is_frozen;
1662 f32 player_speed = 0.001; // just some small value
1663 player_speed = lplayer->getSpeed().getLength();
1666 Maximum position increment
1668 //f32 position_max_increment = 0.05*BS;
1669 f32 position_max_increment = 0.1*BS;
1671 // Maximum time increment (for collision detection etc)
1672 // time = distance / speed
1673 f32 dtime_max_increment = position_max_increment / player_speed;
1675 // Maximum time increment is 10ms or lower
1676 if(dtime_max_increment > 0.01)
1677 dtime_max_increment = 0.01;
1679 // Don't allow overly huge dtime
1683 f32 dtime_downcount = dtime;
1686 Stuff that has a maximum time increment
1695 if(dtime_downcount > dtime_max_increment)
1697 dtime_part = dtime_max_increment;
1698 dtime_downcount -= dtime_part;
1702 dtime_part = dtime_downcount;
1704 Setting this to 0 (no -=dtime_part) disables an infinite loop
1705 when dtime_part is so small that dtime_downcount -= dtime_part
1708 dtime_downcount = 0;
1716 v3f lplayerpos = lplayer->getPosition();
1719 if(free_move == false && is_climbing == false && is_frozen == false)
1722 v3f speed = lplayer->getSpeed();
1723 if(lplayer->swimming_up == false)
1724 speed.Y -= 9.81 * BS * dtime_part * 2;
1727 if(lplayer->in_water_stable || lplayer->in_water)
1729 f32 max_down = 2.0*BS;
1730 if(speed.Y < -max_down) speed.Y = -max_down;
1733 if(speed.getLength() > max)
1735 speed = speed / speed.getLength() * max;
1739 lplayer->setSpeed(speed);
1744 This also does collision detection.
1746 lplayer->move(dtime_part, *m_map, position_max_increment,
1747 &player_collisions);
1750 while(dtime_downcount > 0.001);
1752 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1754 for(core::list<CollisionInfo>::Iterator
1755 i = player_collisions.begin();
1756 i != player_collisions.end(); i++)
1758 CollisionInfo &info = *i;
1759 if(info.t == COLLISION_FALL)
1761 //f32 tolerance = BS*10; // 2 without damage
1762 f32 tolerance = BS*12; // 3 without damage
1764 if(info.speed > tolerance)
1766 f32 damage_f = (info.speed - tolerance)/BS*factor;
1767 u16 damage = (u16)(damage_f+0.5);
1768 if(lplayer->hp > damage)
1769 lplayer->hp -= damage;
1773 ClientEnvEvent event;
1774 event.type = CEE_PLAYER_DAMAGE;
1775 event.player_damage.amount = damage;
1776 m_client_event_queue.push_back(event);
1782 A quick draft of lava damage
1784 if(m_lava_hurt_interval.step(dtime, 1.0))
1786 v3f pf = lplayer->getPosition();
1788 // Feet, middle and head
1789 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1790 MapNode n1 = m_map->getNodeNoEx(p1);
1791 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1792 MapNode n2 = m_map->getNodeNoEx(p2);
1793 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1794 MapNode n3 = m_map->getNodeNoEx(p2);
1796 u32 damage_per_second = 0;
1797 damage_per_second = MYMAX(damage_per_second,
1798 content_features(n1).damage_per_second);
1799 damage_per_second = MYMAX(damage_per_second,
1800 content_features(n2).damage_per_second);
1801 damage_per_second = MYMAX(damage_per_second,
1802 content_features(n3).damage_per_second);
1804 if(damage_per_second != 0)
1806 ClientEnvEvent event;
1807 event.type = CEE_PLAYER_DAMAGE;
1808 event.player_damage.amount = damage_per_second;
1809 m_client_event_queue.push_back(event);
1814 Stuff that can be done in an arbitarily large dtime
1816 for(core::list<Player*>::Iterator i = m_players.begin();
1817 i != m_players.end(); i++)
1819 Player *player = *i;
1820 v3f playerpos = player->getPosition();
1823 Handle non-local players
1825 if(player->isLocal() == false)
1828 player->move(dtime, *m_map, 100*BS);
1832 // Update lighting on all players on client
1833 u8 light = LIGHT_MAX;
1836 v3s16 p = player->getLightPosition();
1837 MapNode n = m_map->getNode(p);
1838 light = n.getLightBlend(getDayNightRatio());
1840 catch(InvalidPositionException &e) {}
1841 player->updateLight(light);
1844 Add footsteps to grass
1848 // Get node that is at BS/4 under player
1849 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1851 MapNode n = m_map->getNode(bottompos);
1852 if(n.getContent() == CONTENT_GRASS)
1854 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1855 m_map->setNode(bottompos, n);
1856 // Update mesh on client
1857 if(m_map->mapType() == MAPTYPE_CLIENT)
1859 v3s16 p_blocks = getNodeBlockPos(bottompos);
1860 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1861 //b->updateMesh(getDayNightRatio());
1862 b->setMeshExpired(true);
1866 catch(InvalidPositionException &e)
1873 Step active objects and update lighting of them
1876 for(core::map<u16, ClientActiveObject*>::Iterator
1877 i = m_active_objects.getIterator();
1878 i.atEnd()==false; i++)
1880 ClientActiveObject* obj = i.getNode()->getValue();
1882 obj->step(dtime, this);
1884 if(m_active_object_light_update_interval.step(dtime, 0.21))
1887 //u8 light = LIGHT_MAX;
1891 v3s16 p = obj->getLightPosition();
1892 MapNode n = m_map->getNode(p);
1893 light = n.getLightBlend(getDayNightRatio());
1895 catch(InvalidPositionException &e) {}
1896 obj->updateLight(light);
1901 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1903 m_map->updateMeshes(blockpos, getDayNightRatio());
1906 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1908 m_map->expireMeshes(only_daynight_diffed);
1911 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1913 core::map<u16, ClientActiveObject*>::Node *n;
1914 n = m_active_objects.find(id);
1917 return n->getValue();
1920 bool isFreeClientActiveObjectId(u16 id,
1921 core::map<u16, ClientActiveObject*> &objects)
1926 for(core::map<u16, ClientActiveObject*>::Iterator
1927 i = objects.getIterator();
1928 i.atEnd()==false; i++)
1930 if(i.getNode()->getKey() == id)
1936 u16 getFreeClientActiveObjectId(
1937 core::map<u16, ClientActiveObject*> &objects)
1942 if(isFreeClientActiveObjectId(new_id, objects))
1952 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1955 if(object->getId() == 0)
1957 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1960 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1961 <<"no free ids available"<<std::endl;
1965 object->setId(new_id);
1967 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1969 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1970 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1974 dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1975 <<"added (id="<<object->getId()<<")"<<std::endl;
1976 m_active_objects.insert(object->getId(), object);
1977 object->addToScene(m_smgr);
1978 return object->getId();
1981 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1982 const std::string &init_data)
1984 ClientActiveObject* obj = ClientActiveObject::create(type);
1987 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1988 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1995 obj->initialize(init_data);
1997 addActiveObject(obj);
2000 void ClientEnvironment::removeActiveObject(u16 id)
2002 dstream<<"ClientEnvironment::removeActiveObject(): "
2003 <<"id="<<id<<std::endl;
2004 ClientActiveObject* obj = getActiveObject(id);
2007 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
2008 <<"id="<<id<<" not found"<<std::endl;
2011 obj->removeFromScene();
2013 m_active_objects.remove(id);
2016 void ClientEnvironment::processActiveObjectMessage(u16 id,
2017 const std::string &data)
2019 ClientActiveObject* obj = getActiveObject(id);
2022 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
2023 <<" got message for id="<<id<<", which doesn't exist."
2027 obj->processMessage(data);
2031 Callbacks for activeobjects
2034 void ClientEnvironment::damageLocalPlayer(u8 damage)
2036 LocalPlayer *lplayer = getLocalPlayer();
2039 if(lplayer->hp > damage)
2040 lplayer->hp -= damage;
2044 ClientEnvEvent event;
2045 event.type = CEE_PLAYER_DAMAGE;
2046 event.player_damage.amount = damage;
2047 m_client_event_queue.push_back(event);
2051 Client likes to call these
2054 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2055 core::array<DistanceSortedActiveObject> &dest)
2057 for(core::map<u16, ClientActiveObject*>::Iterator
2058 i = m_active_objects.getIterator();
2059 i.atEnd()==false; i++)
2061 ClientActiveObject* obj = i.getNode()->getValue();
2063 f32 d = (obj->getPosition() - origin).getLength();
2068 DistanceSortedActiveObject dso(obj, d);
2070 dest.push_back(dso);
2074 ClientEnvEvent ClientEnvironment::getClientEvent()
2076 if(m_client_event_queue.size() == 0)
2078 ClientEnvEvent event;
2079 event.type = CEE_NONE;
2082 return m_client_event_queue.pop_front();
2085 #endif // #ifndef SERVER