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", 30);
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 // Remove non-peaceful mobs on peaceful mode
1043 if(g_settings->getBool("only_peaceful_mobs")){
1044 if(!obj->isPeaceful())
1045 obj->m_removed = true;
1047 // Don't step if is to be removed or stored statically
1048 if(obj->m_removed || obj->m_pending_deactivation)
1051 obj->step(dtime, send_recommended);
1052 // Read messages from object
1053 while(obj->m_messages_out.size() > 0)
1055 m_active_object_messages.push_back(
1056 obj->m_messages_out.pop_front());
1062 Manage active objects
1064 if(m_object_management_interval.step(dtime, 0.5))
1067 Remove objects that satisfy (m_removed && m_known_by_count==0)
1069 removeRemovedObjects();
1072 if(g_settings->getBool("enable_experimental"))
1079 m_random_spawn_timer -= dtime;
1080 if(m_random_spawn_timer < 0)
1082 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1083 //m_random_spawn_timer += 2.0;
1084 m_random_spawn_timer += 200.0;
1090 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1091 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1092 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1094 Player *player = getRandomConnectedPlayer();
1097 pos = player->getPosition();
1099 myrand_range(-3,3)*BS,
1101 myrand_range(-3,3)*BS
1105 Create a ServerActiveObject
1108 //TestSAO *obj = new TestSAO(this, 0, pos);
1109 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1110 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1111 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1112 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1114 dstream<<DTIME<<"INFO: Server: Spawning MobV2SAO at "
1115 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1117 Settings properties;
1118 getMob_dungeon_master(properties);
1119 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1120 addActiveObject(obj);
1124 } // enable_experimental
1127 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1129 core::map<u16, ServerActiveObject*>::Node *n;
1130 n = m_active_objects.find(id);
1133 return n->getValue();
1136 bool isFreeServerActiveObjectId(u16 id,
1137 core::map<u16, ServerActiveObject*> &objects)
1142 for(core::map<u16, ServerActiveObject*>::Iterator
1143 i = objects.getIterator();
1144 i.atEnd()==false; i++)
1146 if(i.getNode()->getKey() == id)
1152 u16 getFreeServerActiveObjectId(
1153 core::map<u16, ServerActiveObject*> &objects)
1158 if(isFreeServerActiveObjectId(new_id, objects))
1168 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1171 u16 id = addActiveObjectRaw(object, true);
1175 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1179 v3f objectpos = obj->getBasePosition();
1181 // The block in which the object resides in
1182 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1185 Update the static data
1188 // Create new static object
1189 std::string staticdata = obj->getStaticData();
1190 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1191 // Add to the block where the object is located in
1192 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1193 // Get or generate the block
1194 MapBlock *block = m_map->emergeBlock(blockpos);
1196 bool succeeded = false;
1200 block->m_static_objects.insert(0, s_obj);
1201 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1205 dstream<<"WARNING: ServerEnvironment::addActiveObjectAsStatic: "
1206 <<"Could not find or generate "
1207 <<"a block for storing static object"<<std::endl;
1217 Finds out what new objects have been added to
1218 inside a radius around a position
1220 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1221 core::map<u16, bool> ¤t_objects,
1222 core::map<u16, bool> &added_objects)
1224 v3f pos_f = intToFloat(pos, BS);
1225 f32 radius_f = radius * BS;
1227 Go through the object list,
1228 - discard m_removed objects,
1229 - discard objects that are too far away,
1230 - discard objects that are found in current_objects.
1231 - add remaining objects to added_objects
1233 for(core::map<u16, ServerActiveObject*>::Iterator
1234 i = m_active_objects.getIterator();
1235 i.atEnd()==false; i++)
1237 u16 id = i.getNode()->getKey();
1239 ServerActiveObject *object = i.getNode()->getValue();
1242 // Discard if removed
1243 if(object->m_removed)
1245 // Discard if too far
1246 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1247 if(distance_f > radius_f)
1249 // Discard if already on current_objects
1250 core::map<u16, bool>::Node *n;
1251 n = current_objects.find(id);
1254 // Add to added_objects
1255 added_objects.insert(id, false);
1260 Finds out what objects have been removed from
1261 inside a radius around a position
1263 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1264 core::map<u16, bool> ¤t_objects,
1265 core::map<u16, bool> &removed_objects)
1267 v3f pos_f = intToFloat(pos, BS);
1268 f32 radius_f = radius * BS;
1270 Go through current_objects; object is removed if:
1271 - object is not found in m_active_objects (this is actually an
1272 error condition; objects should be set m_removed=true and removed
1273 only after all clients have been informed about removal), or
1274 - object has m_removed=true, or
1275 - object is too far away
1277 for(core::map<u16, bool>::Iterator
1278 i = current_objects.getIterator();
1279 i.atEnd()==false; i++)
1281 u16 id = i.getNode()->getKey();
1282 ServerActiveObject *object = getActiveObject(id);
1285 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1286 <<" object in current_objects is NULL"<<std::endl;
1288 else if(object->m_removed == false)
1290 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1291 /*dstream<<"removed == false"
1292 <<"distance_f = "<<distance_f
1293 <<", radius_f = "<<radius_f<<std::endl;*/
1294 if(distance_f < radius_f)
1300 removed_objects.insert(id, false);
1304 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1306 if(m_active_object_messages.size() == 0)
1307 return ActiveObjectMessage(0);
1309 return m_active_object_messages.pop_front();
1313 ************ Private methods *************
1316 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1320 if(object->getId() == 0)
1322 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1325 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1326 <<"no free ids available"<<std::endl;
1330 object->setId(new_id);
1332 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1334 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1335 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1339 /*dstream<<"INFO: ServerEnvironment::addActiveObjectRaw(): "
1340 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1342 m_active_objects.insert(object->getId(), object);
1344 // Add static object to active static list of the block
1345 v3f objectpos = object->getBasePosition();
1346 std::string staticdata = object->getStaticData();
1347 StaticObject s_obj(object->getType(), objectpos, staticdata);
1348 // Add to the block where the object is located in
1349 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1350 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1353 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1354 object->m_static_exists = true;
1355 object->m_static_block = blockpos;
1358 block->setChangedFlag();
1361 dstream<<"WARNING: ServerEnv: Could not find a block for "
1362 <<"storing newly added static active object"<<std::endl;
1365 return object->getId();
1369 Remove objects that satisfy (m_removed && m_known_by_count==0)
1371 void ServerEnvironment::removeRemovedObjects()
1373 core::list<u16> objects_to_remove;
1374 for(core::map<u16, ServerActiveObject*>::Iterator
1375 i = m_active_objects.getIterator();
1376 i.atEnd()==false; i++)
1378 u16 id = i.getNode()->getKey();
1379 ServerActiveObject* obj = i.getNode()->getValue();
1380 // This shouldn't happen but check it
1383 dstream<<"WARNING: NULL object found in ServerEnvironment"
1384 <<" while finding removed objects. id="<<id<<std::endl;
1385 // Id to be removed from m_active_objects
1386 objects_to_remove.push_back(id);
1391 We will delete objects that are marked as removed or thatare
1392 waiting for deletion after deactivation
1394 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1398 Delete static data from block if is marked as removed
1400 if(obj->m_static_exists && obj->m_removed)
1402 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1405 block->m_static_objects.remove(id);
1406 block->setChangedFlag();
1410 // If m_known_by_count > 0, don't actually remove.
1411 if(obj->m_known_by_count > 0)
1416 // Id to be removed from m_active_objects
1417 objects_to_remove.push_back(id);
1419 // Remove references from m_active_objects
1420 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1421 i != objects_to_remove.end(); i++)
1423 m_active_objects.remove(*i);
1428 Convert stored objects from blocks near the players to active.
1430 void ServerEnvironment::activateObjects(MapBlock *block)
1434 // Ignore if no stored objects (to not set changed flag)
1435 if(block->m_static_objects.m_stored.size() == 0)
1437 // A list for objects that couldn't be converted to static for some
1438 // reason. They will be stored back.
1439 core::list<StaticObject> new_stored;
1440 // Loop through stored static objects
1441 for(core::list<StaticObject>::Iterator
1442 i = block->m_static_objects.m_stored.begin();
1443 i != block->m_static_objects.m_stored.end(); i++)
1445 /*dstream<<"INFO: Server: Creating an active object from "
1446 <<"static data"<<std::endl;*/
1447 StaticObject &s_obj = *i;
1448 // Create an active object from the data
1449 ServerActiveObject *obj = ServerActiveObject::create
1450 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1451 // If couldn't create object, store static data back.
1454 new_stored.push_back(s_obj);
1457 // This will also add the object to the active static list
1458 addActiveObjectRaw(obj, false);
1459 //u16 id = addActiveObjectRaw(obj, false);
1461 // Clear stored list
1462 block->m_static_objects.m_stored.clear();
1463 // Add leftover failed stuff to stored list
1464 for(core::list<StaticObject>::Iterator
1465 i = new_stored.begin();
1466 i != new_stored.end(); i++)
1468 StaticObject &s_obj = *i;
1469 block->m_static_objects.m_stored.push_back(s_obj);
1471 // Block has been modified
1472 // NOTE: No it has not really. Save I/O here.
1473 //block->setChangedFlag();
1477 Convert objects that are not in active blocks to static.
1479 If m_known_by_count != 0, active object is not deleted, but static
1480 data is still updated.
1482 If force_delete is set, active object is deleted nevertheless. It
1483 shall only be set so in the destructor of the environment.
1485 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1487 core::list<u16> objects_to_remove;
1488 for(core::map<u16, ServerActiveObject*>::Iterator
1489 i = m_active_objects.getIterator();
1490 i.atEnd()==false; i++)
1492 ServerActiveObject* obj = i.getNode()->getValue();
1494 // This shouldn't happen but check it
1497 dstream<<"WARNING: NULL object found in ServerEnvironment"
1503 u16 id = i.getNode()->getKey();
1504 v3f objectpos = obj->getBasePosition();
1506 // The block in which the object resides in
1507 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1509 // If block is active, don't remove
1510 if(m_active_blocks.contains(blockpos_o))
1514 Update the static data
1517 // Delete old static object
1518 MapBlock *oldblock = NULL;
1519 if(obj->m_static_exists)
1521 MapBlock *block = m_map->getBlockNoCreateNoEx
1522 (obj->m_static_block);
1525 block->m_static_objects.remove(id);
1529 // Create new static object
1530 std::string staticdata = obj->getStaticData();
1531 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1532 // Add to the block where the object is located in
1533 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1534 // Get or generate the block
1535 MapBlock *block = m_map->emergeBlock(blockpos);
1537 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1540 // Block not found. Is the old block still ok?
1543 // Load from disk or generate
1545 block = m_map->emergeBlock(blockpos);
1550 block->m_static_objects.insert(0, s_obj);
1551 block->setChangedFlag();
1552 obj->m_static_exists = true;
1553 obj->m_static_block = block->getPos();
1556 dstream<<"WARNING: ServerEnv: Could not find or generate "
1557 <<"a block for storing static object"<<std::endl;
1558 obj->m_static_exists = false;
1563 Delete active object if not known by some client,
1564 else set pending deactivation
1567 // If known by some client, don't delete.
1568 if(obj->m_known_by_count > 0 && force_delete == false)
1570 obj->m_pending_deactivation = true;
1574 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1576 // Delete active object
1578 // Id to be removed from m_active_objects
1579 objects_to_remove.push_back(id);
1582 // Remove references from m_active_objects
1583 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1584 i != objects_to_remove.end(); i++)
1586 m_active_objects.remove(*i);
1597 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1605 ClientEnvironment::~ClientEnvironment()
1607 // delete active objects
1608 for(core::map<u16, ClientActiveObject*>::Iterator
1609 i = m_active_objects.getIterator();
1610 i.atEnd()==false; i++)
1612 delete i.getNode()->getValue();
1619 void ClientEnvironment::addPlayer(Player *player)
1621 DSTACK(__FUNCTION_NAME);
1623 It is a failure if player is local and there already is a local
1626 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1628 Environment::addPlayer(player);
1631 LocalPlayer * ClientEnvironment::getLocalPlayer()
1633 for(core::list<Player*>::Iterator i = m_players.begin();
1634 i != m_players.end(); i++)
1636 Player *player = *i;
1637 if(player->isLocal())
1638 return (LocalPlayer*)player;
1643 void ClientEnvironment::step(float dtime)
1645 DSTACK(__FUNCTION_NAME);
1647 // Get some settings
1648 bool free_move = g_settings->getBool("free_move");
1649 bool footprints = g_settings->getBool("footprints");
1652 LocalPlayer *lplayer = getLocalPlayer();
1654 // collision info queue
1655 core::list<CollisionInfo> player_collisions;
1658 Get the speed the player is going
1660 bool is_climbing = lplayer->is_climbing;
1663 Check if the player is frozen (don't apply physics)
1665 bool is_frozen = lplayer->is_frozen;
1667 f32 player_speed = 0.001; // just some small value
1668 player_speed = lplayer->getSpeed().getLength();
1671 Maximum position increment
1673 //f32 position_max_increment = 0.05*BS;
1674 f32 position_max_increment = 0.1*BS;
1676 // Maximum time increment (for collision detection etc)
1677 // time = distance / speed
1678 f32 dtime_max_increment = position_max_increment / player_speed;
1680 // Maximum time increment is 10ms or lower
1681 if(dtime_max_increment > 0.01)
1682 dtime_max_increment = 0.01;
1684 // Don't allow overly huge dtime
1688 f32 dtime_downcount = dtime;
1691 Stuff that has a maximum time increment
1700 if(dtime_downcount > dtime_max_increment)
1702 dtime_part = dtime_max_increment;
1703 dtime_downcount -= dtime_part;
1707 dtime_part = dtime_downcount;
1709 Setting this to 0 (no -=dtime_part) disables an infinite loop
1710 when dtime_part is so small that dtime_downcount -= dtime_part
1713 dtime_downcount = 0;
1721 v3f lplayerpos = lplayer->getPosition();
1724 if(free_move == false && is_climbing == false && is_frozen == false)
1727 v3f speed = lplayer->getSpeed();
1728 if(lplayer->swimming_up == false)
1729 speed.Y -= 9.81 * BS * dtime_part * 2;
1732 if(lplayer->in_water_stable || lplayer->in_water)
1734 f32 max_down = 2.0*BS;
1735 if(speed.Y < -max_down) speed.Y = -max_down;
1738 if(speed.getLength() > max)
1740 speed = speed / speed.getLength() * max;
1744 lplayer->setSpeed(speed);
1749 This also does collision detection.
1751 lplayer->move(dtime_part, *m_map, position_max_increment,
1752 &player_collisions);
1755 while(dtime_downcount > 0.001);
1757 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1759 for(core::list<CollisionInfo>::Iterator
1760 i = player_collisions.begin();
1761 i != player_collisions.end(); i++)
1763 CollisionInfo &info = *i;
1764 if(info.t == COLLISION_FALL)
1766 //f32 tolerance = BS*10; // 2 without damage
1767 f32 tolerance = BS*12; // 3 without damage
1769 if(info.speed > tolerance)
1771 f32 damage_f = (info.speed - tolerance)/BS*factor;
1772 u16 damage = (u16)(damage_f+0.5);
1773 if(lplayer->hp > damage)
1774 lplayer->hp -= damage;
1778 ClientEnvEvent event;
1779 event.type = CEE_PLAYER_DAMAGE;
1780 event.player_damage.amount = damage;
1781 m_client_event_queue.push_back(event);
1787 A quick draft of lava damage
1789 if(m_lava_hurt_interval.step(dtime, 1.0))
1791 v3f pf = lplayer->getPosition();
1793 // Feet, middle and head
1794 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1795 MapNode n1 = m_map->getNodeNoEx(p1);
1796 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1797 MapNode n2 = m_map->getNodeNoEx(p2);
1798 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1799 MapNode n3 = m_map->getNodeNoEx(p2);
1801 u32 damage_per_second = 0;
1802 damage_per_second = MYMAX(damage_per_second,
1803 content_features(n1).damage_per_second);
1804 damage_per_second = MYMAX(damage_per_second,
1805 content_features(n2).damage_per_second);
1806 damage_per_second = MYMAX(damage_per_second,
1807 content_features(n3).damage_per_second);
1809 if(damage_per_second != 0)
1811 ClientEnvEvent event;
1812 event.type = CEE_PLAYER_DAMAGE;
1813 event.player_damage.amount = damage_per_second;
1814 m_client_event_queue.push_back(event);
1819 Stuff that can be done in an arbitarily large dtime
1821 for(core::list<Player*>::Iterator i = m_players.begin();
1822 i != m_players.end(); i++)
1824 Player *player = *i;
1825 v3f playerpos = player->getPosition();
1828 Handle non-local players
1830 if(player->isLocal() == false)
1833 player->move(dtime, *m_map, 100*BS);
1837 // Update lighting on all players on client
1838 u8 light = LIGHT_MAX;
1841 v3s16 p = player->getLightPosition();
1842 MapNode n = m_map->getNode(p);
1843 light = n.getLightBlend(getDayNightRatio());
1845 catch(InvalidPositionException &e) {}
1846 player->updateLight(light);
1849 Add footsteps to grass
1853 // Get node that is at BS/4 under player
1854 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1856 MapNode n = m_map->getNode(bottompos);
1857 if(n.getContent() == CONTENT_GRASS)
1859 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1860 m_map->setNode(bottompos, n);
1861 // Update mesh on client
1862 if(m_map->mapType() == MAPTYPE_CLIENT)
1864 v3s16 p_blocks = getNodeBlockPos(bottompos);
1865 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1866 //b->updateMesh(getDayNightRatio());
1867 b->setMeshExpired(true);
1871 catch(InvalidPositionException &e)
1878 Step active objects and update lighting of them
1881 for(core::map<u16, ClientActiveObject*>::Iterator
1882 i = m_active_objects.getIterator();
1883 i.atEnd()==false; i++)
1885 ClientActiveObject* obj = i.getNode()->getValue();
1887 obj->step(dtime, this);
1889 if(m_active_object_light_update_interval.step(dtime, 0.21))
1892 //u8 light = LIGHT_MAX;
1896 v3s16 p = obj->getLightPosition();
1897 MapNode n = m_map->getNode(p);
1898 light = n.getLightBlend(getDayNightRatio());
1900 catch(InvalidPositionException &e) {}
1901 obj->updateLight(light);
1906 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1908 m_map->updateMeshes(blockpos, getDayNightRatio());
1911 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1913 m_map->expireMeshes(only_daynight_diffed);
1916 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1918 core::map<u16, ClientActiveObject*>::Node *n;
1919 n = m_active_objects.find(id);
1922 return n->getValue();
1925 bool isFreeClientActiveObjectId(u16 id,
1926 core::map<u16, ClientActiveObject*> &objects)
1931 for(core::map<u16, ClientActiveObject*>::Iterator
1932 i = objects.getIterator();
1933 i.atEnd()==false; i++)
1935 if(i.getNode()->getKey() == id)
1941 u16 getFreeClientActiveObjectId(
1942 core::map<u16, ClientActiveObject*> &objects)
1947 if(isFreeClientActiveObjectId(new_id, objects))
1957 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1960 if(object->getId() == 0)
1962 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1965 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1966 <<"no free ids available"<<std::endl;
1970 object->setId(new_id);
1972 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1974 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1975 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1979 dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1980 <<"added (id="<<object->getId()<<")"<<std::endl;
1981 m_active_objects.insert(object->getId(), object);
1982 object->addToScene(m_smgr);
1983 return object->getId();
1986 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1987 const std::string &init_data)
1989 ClientActiveObject* obj = ClientActiveObject::create(type);
1992 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1993 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2000 obj->initialize(init_data);
2002 addActiveObject(obj);
2005 void ClientEnvironment::removeActiveObject(u16 id)
2007 dstream<<"ClientEnvironment::removeActiveObject(): "
2008 <<"id="<<id<<std::endl;
2009 ClientActiveObject* obj = getActiveObject(id);
2012 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
2013 <<"id="<<id<<" not found"<<std::endl;
2016 obj->removeFromScene();
2018 m_active_objects.remove(id);
2021 void ClientEnvironment::processActiveObjectMessage(u16 id,
2022 const std::string &data)
2024 ClientActiveObject* obj = getActiveObject(id);
2027 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
2028 <<" got message for id="<<id<<", which doesn't exist."
2032 obj->processMessage(data);
2036 Callbacks for activeobjects
2039 void ClientEnvironment::damageLocalPlayer(u8 damage)
2041 LocalPlayer *lplayer = getLocalPlayer();
2044 if(lplayer->hp > damage)
2045 lplayer->hp -= damage;
2049 ClientEnvEvent event;
2050 event.type = CEE_PLAYER_DAMAGE;
2051 event.player_damage.amount = damage;
2052 m_client_event_queue.push_back(event);
2056 Client likes to call these
2059 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2060 core::array<DistanceSortedActiveObject> &dest)
2062 for(core::map<u16, ClientActiveObject*>::Iterator
2063 i = m_active_objects.getIterator();
2064 i.atEnd()==false; i++)
2066 ClientActiveObject* obj = i.getNode()->getValue();
2068 f32 d = (obj->getPosition() - origin).getLength();
2073 DistanceSortedActiveObject dso(obj, d);
2075 dest.push_back(dso);
2079 ClientEnvEvent ClientEnvironment::getClientEvent()
2081 if(m_client_event_queue.size() == 0)
2083 ClientEnvEvent event;
2084 event.type = CEE_NONE;
2087 return m_client_event_queue.pop_front();
2090 #endif // #ifndef SERVER