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"
32 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
34 Environment::Environment():
39 Environment::~Environment()
42 for(core::list<Player*>::Iterator i = m_players.begin();
43 i != m_players.end(); i++)
49 void Environment::addPlayer(Player *player)
51 DSTACK(__FUNCTION_NAME);
53 Check that peer_ids are unique.
54 Also check that names are unique.
55 Exception: there can be multiple players with peer_id=0
57 // If peer id is non-zero, it has to be unique.
58 if(player->peer_id != 0)
59 assert(getPlayer(player->peer_id) == NULL);
60 // Name has to be unique.
61 assert(getPlayer(player->getName()) == NULL);
63 m_players.push_back(player);
66 void Environment::removePlayer(u16 peer_id)
68 DSTACK(__FUNCTION_NAME);
70 for(core::list<Player*>::Iterator i = m_players.begin();
71 i != m_players.end(); i++)
74 if(player->peer_id != peer_id)
79 // See if there is an another one
80 // (shouldn't be, but just to be sure)
85 Player * Environment::getPlayer(u16 peer_id)
87 for(core::list<Player*>::Iterator i = m_players.begin();
88 i != m_players.end(); i++)
91 if(player->peer_id == peer_id)
97 Player * Environment::getPlayer(const char *name)
99 for(core::list<Player*>::Iterator i = m_players.begin();
100 i != m_players.end(); i++)
103 if(strcmp(player->getName(), name) == 0)
109 Player * Environment::getRandomConnectedPlayer()
111 core::list<Player*> connected_players = getPlayers(true);
112 u32 chosen_one = myrand() % connected_players.size();
114 for(core::list<Player*>::Iterator
115 i = connected_players.begin();
116 i != connected_players.end(); i++)
128 Player * Environment::getNearestConnectedPlayer(v3f pos)
130 core::list<Player*> connected_players = getPlayers(true);
132 Player *nearest_player = NULL;
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
138 f32 d = player->getPosition().getDistanceFrom(pos);
139 if(d < nearest_d || nearest_player == NULL)
142 nearest_player = player;
145 return nearest_player;
148 core::list<Player*> Environment::getPlayers()
153 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
155 core::list<Player*> newlist;
156 for(core::list<Player*>::Iterator
157 i = m_players.begin();
158 i != m_players.end(); i++)
162 if(ignore_disconnected)
164 // Ignore disconnected players
165 if(player->peer_id == 0)
169 newlist.push_back(player);
174 void Environment::printPlayers(std::ostream &o)
176 o<<"Players in environment:"<<std::endl;
177 for(core::list<Player*>::Iterator i = m_players.begin();
178 i != m_players.end(); i++)
181 o<<"Player peer_id="<<player->peer_id<<std::endl;
185 /*void Environment::setDayNightRatio(u32 r)
187 getDayNightRatio() = r;
190 u32 Environment::getDayNightRatio()
192 //return getDayNightRatio();
193 return time_to_daynight_ratio(m_time_of_day);
200 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
203 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
204 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
205 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
212 void ActiveBlockList::update(core::list<v3s16> &active_positions,
214 core::map<v3s16, bool> &blocks_removed,
215 core::map<v3s16, bool> &blocks_added)
220 core::map<v3s16, bool> newlist;
221 for(core::list<v3s16>::Iterator i = active_positions.begin();
222 i != active_positions.end(); i++)
224 fillRadiusBlock(*i, radius, newlist);
228 Find out which blocks on the old list are not on the new list
230 // Go through old list
231 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
232 i.atEnd()==false; i++)
234 v3s16 p = i.getNode()->getKey();
235 // If not on new list, it's been removed
236 if(newlist.find(p) == NULL)
237 blocks_removed.insert(p, true);
241 Find out which blocks on the new list are not on the old list
243 // Go through new list
244 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
245 i.atEnd()==false; i++)
247 v3s16 p = i.getNode()->getKey();
248 // If not on old list, it's been added
249 if(m_list.find(p) == NULL)
250 blocks_added.insert(p, true);
257 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
258 i.atEnd()==false; i++)
260 v3s16 p = i.getNode()->getKey();
261 m_list.insert(p, true);
269 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
272 m_random_spawn_timer(3),
273 m_send_recommended_timer(0),
275 m_game_time_fraction_counter(0)
279 ServerEnvironment::~ServerEnvironment()
281 // Clear active block list.
282 // This makes the next one delete all active objects.
283 m_active_blocks.clear();
285 // Convert all objects to static and delete the active objects
286 deactivateFarObjects(true);
292 void ServerEnvironment::serializePlayers(const std::string &savedir)
294 std::string players_path = savedir + "/players";
295 fs::CreateDir(players_path);
297 core::map<Player*, bool> saved_players;
299 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
300 for(u32 i=0; i<player_files.size(); i++)
302 if(player_files[i].dir)
305 // Full path to this file
306 std::string path = players_path + "/" + player_files[i].name;
308 //infostream<<"Checking player file "<<path<<std::endl;
310 // Load player to see what is its name
311 ServerRemotePlayer testplayer;
313 // Open file and deserialize
314 std::ifstream is(path.c_str(), std::ios_base::binary);
315 if(is.good() == false)
317 infostream<<"Failed to read "<<path<<std::endl;
320 testplayer.deSerialize(is);
323 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
325 // Search for the player
326 std::string playername = testplayer.getName();
327 Player *player = getPlayer(playername.c_str());
330 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
334 //infostream<<"Found matching player, overwriting."<<std::endl;
336 // OK, found. Save player there.
338 // Open file and serialize
339 std::ofstream os(path.c_str(), std::ios_base::binary);
340 if(os.good() == false)
342 infostream<<"Failed to overwrite "<<path<<std::endl;
345 player->serialize(os);
346 saved_players.insert(player, true);
350 for(core::list<Player*>::Iterator i = m_players.begin();
351 i != m_players.end(); i++)
354 if(saved_players.find(player) != NULL)
356 /*infostream<<"Player "<<player->getName()
357 <<" was already saved."<<std::endl;*/
360 std::string playername = player->getName();
361 // Don't save unnamed player
364 //infostream<<"Not saving unnamed player."<<std::endl;
370 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
371 playername = "player";
372 std::string path = players_path + "/" + playername;
374 for(u32 i=0; i<1000; i++)
376 if(fs::PathExists(path) == false)
381 path = players_path + "/" + playername + itos(i);
385 infostream<<"Didn't find free file for player"<<std::endl;
390 /*infostream<<"Saving player "<<player->getName()<<" to "
392 // Open file and serialize
393 std::ofstream os(path.c_str(), std::ios_base::binary);
394 if(os.good() == false)
396 infostream<<"Failed to overwrite "<<path<<std::endl;
399 player->serialize(os);
400 saved_players.insert(player, true);
404 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
407 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
409 std::string players_path = savedir + "/players";
411 core::map<Player*, bool> saved_players;
413 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
414 for(u32 i=0; i<player_files.size(); i++)
416 if(player_files[i].dir)
419 // Full path to this file
420 std::string path = players_path + "/" + player_files[i].name;
422 infostream<<"Checking player file "<<path<<std::endl;
424 // Load player to see what is its name
425 ServerRemotePlayer testplayer;
427 // Open file and deserialize
428 std::ifstream is(path.c_str(), std::ios_base::binary);
429 if(is.good() == false)
431 infostream<<"Failed to read "<<path<<std::endl;
434 testplayer.deSerialize(is);
437 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
439 infostream<<"Not loading player with invalid name: "
440 <<testplayer.getName()<<std::endl;
443 infostream<<"Loaded test player with name "<<testplayer.getName()
446 // Search for the player
447 std::string playername = testplayer.getName();
448 Player *player = getPlayer(playername.c_str());
449 bool newplayer = false;
452 infostream<<"Is a new player"<<std::endl;
453 player = new ServerRemotePlayer();
459 infostream<<"Reading player "<<testplayer.getName()<<" from "
461 // Open file and deserialize
462 std::ifstream is(path.c_str(), std::ios_base::binary);
463 if(is.good() == false)
465 infostream<<"Failed to read "<<path<<std::endl;
468 player->deSerialize(is);
476 void ServerEnvironment::saveMeta(const std::string &savedir)
478 std::string path = savedir + "/env_meta.txt";
480 // Open file and serialize
481 std::ofstream os(path.c_str(), std::ios_base::binary);
482 if(os.good() == false)
484 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
486 throw SerializationError("Couldn't save env meta");
490 args.setU64("game_time", m_game_time);
491 args.setU64("time_of_day", getTimeOfDay());
496 void ServerEnvironment::loadMeta(const std::string &savedir)
498 std::string path = savedir + "/env_meta.txt";
500 // Open file and deserialize
501 std::ifstream is(path.c_str(), std::ios_base::binary);
502 if(is.good() == false)
504 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
506 throw SerializationError("Couldn't load env meta");
514 throw SerializationError
515 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
517 std::getline(is, line);
518 std::string trimmedline = trim(line);
519 if(trimmedline == "EnvArgsEnd")
521 args.parseConfigLine(line);
525 m_game_time = args.getU64("game_time");
526 }catch(SettingNotFoundException &e){
527 // Getting this is crucial, otherwise timestamps are useless
528 throw SerializationError("Couldn't load env meta game_time");
532 m_time_of_day = args.getU64("time_of_day");
533 }catch(SettingNotFoundException &e){
534 // This is not as important
535 m_time_of_day = 9000;
540 // This is probably very useless
541 void spawnRandomObjects(MapBlock *block)
543 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
544 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
546 bool last_node_walkable = false;
547 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
550 MapNode n = block->getNodeNoEx(p);
551 if(n.getContent() == CONTENT_IGNORE)
553 if(content_features(n).liquid_type != LIQUID_NONE)
555 if(content_features(n).walkable)
557 last_node_walkable = true;
560 if(last_node_walkable)
562 // If block contains light information
563 if(content_features(n).param_type == CPT_LIGHT)
565 if(n.getLight(LIGHTBANK_DAY) <= 5)
567 if(myrand() % 1000 == 0)
569 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
571 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
572 std::string data = obj->getStaticData();
573 StaticObject s_obj(obj->getType(),
574 obj->getBasePosition(), data);
576 block->m_static_objects.insert(0, s_obj);
578 block->setChangedFlag();
583 last_node_walkable = false;
589 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
591 // Get time difference
593 u32 stamp = block->getTimestamp();
594 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
595 dtime_s = m_game_time - block->getTimestamp();
596 dtime_s += additional_dtime;
598 // Set current time as timestamp (and let it set ChangedFlag)
599 block->setTimestamp(m_game_time);
601 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
603 // Activate stored objects
604 activateObjects(block);
607 bool changed = block->m_node_metadata.step((float)dtime_s);
611 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
612 event.p = block->getPos();
613 m_map->dispatchEvent(&event);
615 block->setChangedFlag();
618 // TODO: Do something
619 // TODO: Implement usage of ActiveBlockModifier
621 // Here's a quick demonstration
623 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
624 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
625 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
627 v3s16 p = p0 + block->getPosRelative();
628 MapNode n = block->getNodeNoEx(p0);
631 // Convert all mud under proper day lighting to grass
632 if(n.getContent() == CONTENT_MUD)
636 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
637 if(content_features(n_top).air_equivalent &&
638 n_top.getLight(LIGHTBANK_DAY) >= 13)
640 n.setContent(CONTENT_GRASS);
641 m_map->addNodeWithEvent(p, n);
649 static void getMob_dungeon_master(Settings &properties)
651 properties.set("looks", "dungeon_master");
652 properties.setFloat("yaw", 1.57);
653 properties.setFloat("hp", 30);
654 properties.setBool("bright_shooting", true);
655 properties.set("shoot_type", "fireball");
656 properties.set("shoot_y", "0.7");
657 properties.set("player_hit_damage", "1");
658 properties.set("player_hit_distance", "1.0");
659 properties.set("player_hit_interval", "0.5");
660 properties.setBool("mindless_rage", myrand_range(0,100)==0);
663 void ServerEnvironment::step(float dtime)
665 DSTACK(__FUNCTION_NAME);
667 //TimeTaker timer("ServerEnv step");
670 bool footprints = g_settings->getBool("footprints");
676 m_game_time_fraction_counter += dtime;
677 u32 inc_i = (u32)m_game_time_fraction_counter;
678 m_game_time += inc_i;
679 m_game_time_fraction_counter -= (float)inc_i;
685 for(core::list<Player*>::Iterator i = m_players.begin();
686 i != m_players.end(); i++)
690 // Ignore disconnected players
691 if(player->peer_id == 0)
694 v3f playerpos = player->getPosition();
697 player->move(dtime, *m_map, 100*BS);
700 Add footsteps to grass
704 // Get node that is at BS/4 under player
705 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
707 MapNode n = m_map->getNode(bottompos);
708 if(n.getContent() == CONTENT_GRASS)
710 n.setContent(CONTENT_GRASS_FOOTSTEPS);
711 m_map->setNode(bottompos, n);
714 catch(InvalidPositionException &e)
721 Manage active block list
723 if(m_active_blocks_management_interval.step(dtime, 2.0))
726 Get player block positions
728 core::list<v3s16> players_blockpos;
729 for(core::list<Player*>::Iterator
730 i = m_players.begin();
731 i != m_players.end(); i++)
734 // Ignore disconnected players
735 if(player->peer_id == 0)
737 v3s16 blockpos = getNodeBlockPos(
738 floatToInt(player->getPosition(), BS));
739 players_blockpos.push_back(blockpos);
743 Update list of active blocks, collecting changes
745 const s16 active_block_range = g_settings->getS16("active_block_range");
746 core::map<v3s16, bool> blocks_removed;
747 core::map<v3s16, bool> blocks_added;
748 m_active_blocks.update(players_blockpos, active_block_range,
749 blocks_removed, blocks_added);
752 Handle removed blocks
755 // Convert active objects that are no more in active blocks to static
756 deactivateFarObjects(false);
758 for(core::map<v3s16, bool>::Iterator
759 i = blocks_removed.getIterator();
760 i.atEnd()==false; i++)
762 v3s16 p = i.getNode()->getKey();
764 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
765 <<") became inactive"<<std::endl;*/
767 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
771 // Set current time as timestamp (and let it set ChangedFlag)
772 block->setTimestamp(m_game_time);
779 for(core::map<v3s16, bool>::Iterator
780 i = blocks_added.getIterator();
781 i.atEnd()==false; i++)
783 v3s16 p = i.getNode()->getKey();
785 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
786 <<") became active"<<std::endl;*/
788 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
792 activateBlock(block);
797 Mess around in active blocks
799 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
803 for(core::map<v3s16, bool>::Iterator
804 i = m_active_blocks.m_list.getIterator();
805 i.atEnd()==false; i++)
807 v3s16 p = i.getNode()->getKey();
809 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
810 <<") being handled"<<std::endl;*/
812 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
816 // Reset block usage timer
817 block->resetUsageTimer();
819 // Set current time as timestamp
820 block->setTimestampNoChangedFlag(m_game_time);
823 bool changed = block->m_node_metadata.step(dtime);
827 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
829 m_map->dispatchEvent(&event);
831 block->setChangedFlag();
835 if(m_active_blocks_test_interval.step(dtime, 10.0))
837 //float dtime = 10.0;
839 for(core::map<v3s16, bool>::Iterator
840 i = m_active_blocks.m_list.getIterator();
841 i.atEnd()==false; i++)
843 v3s16 p = i.getNode()->getKey();
845 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
846 <<") being handled"<<std::endl;*/
848 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
852 // Set current time as timestamp
853 block->setTimestampNoChangedFlag(m_game_time);
858 Note that map modifications should be done using the event-
859 making map methods so that the server gets information
862 Reading can be done quickly directly from the block.
864 Everything should bind to inside this single content
865 searching loop to keep things fast.
867 // TODO: Implement usage of ActiveBlockModifier
869 // Find out how many objects the block contains
870 //u32 active_object_count = block->m_static_objects.m_active.size();
871 // Find out how many objects this and all the neighbors contain
872 u32 active_object_count_wider = 0;
873 for(s16 x=-1; x<=1; x++)
874 for(s16 y=-1; y<=1; y++)
875 for(s16 z=-1; z<=1; z++)
877 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
880 active_object_count_wider +=
881 block->m_static_objects.m_active.size();
885 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
886 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
887 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
889 v3s16 p = p0 + block->getPosRelative();
890 MapNode n = block->getNodeNoEx(p0);
894 Convert mud under proper lighting to grass
896 if(n.getContent() == CONTENT_MUD)
900 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
901 if(content_features(n_top).air_equivalent &&
902 n_top.getLightBlend(getDayNightRatio()) >= 13)
904 n.setContent(CONTENT_GRASS);
905 m_map->addNodeWithEvent(p, n);
910 Convert grass into mud if under something else than air
912 if(n.getContent() == CONTENT_GRASS)
914 //if(myrand()%20 == 0)
916 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
917 if(content_features(n_top).air_equivalent == false)
919 n.setContent(CONTENT_MUD);
920 m_map->addNodeWithEvent(p, n);
925 Rats spawn around regular trees
927 if(n.getContent() == CONTENT_TREE ||
928 n.getContent() == CONTENT_JUNGLETREE)
930 if(myrand()%200 == 0 && active_object_count_wider == 0)
932 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
933 0, myrand_range(-2, 2));
934 MapNode n1 = m_map->getNodeNoEx(p1);
935 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
936 if(n1b.getContent() == CONTENT_GRASS &&
937 n1.getContent() == CONTENT_AIR)
939 v3f pos = intToFloat(p1, BS);
940 ServerActiveObject *obj = new RatSAO(this, 0, pos);
941 addActiveObject(obj);
946 Fun things spawn in caves and dungeons
948 if(n.getContent() == CONTENT_STONE ||
949 n.getContent() == CONTENT_MOSSYCOBBLE)
951 if(myrand()%200 == 0 && active_object_count_wider == 0)
953 v3s16 p1 = p + v3s16(0,1,0);
954 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
955 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
956 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
957 if(n1a.getContent() == CONTENT_AIR &&
958 n1b.getContent() == CONTENT_AIR)
960 v3f pos = intToFloat(p1, BS);
962 if(i == 0 || i == 1){
963 actionstream<<"A dungeon master spawns at "
966 getMob_dungeon_master(properties);
967 ServerActiveObject *obj = new MobV2SAO(
968 this, 0, pos, &properties);
969 addActiveObject(obj);
970 } else if(i == 2 || i == 3){
971 actionstream<<"Rats spawn at "
973 for(int j=0; j<3; j++){
974 ServerActiveObject *obj = new RatSAO(
976 addActiveObject(obj);
979 actionstream<<"An oerkki spawns at "
981 ServerActiveObject *obj = new Oerkki1SAO(
983 addActiveObject(obj);
990 Make trees from saplings!
992 if(n.getContent() == CONTENT_SAPLING)
996 actionstream<<"A sapling grows into a tree at "
999 core::map<v3s16, MapBlock*> modified_blocks;
1001 ManualMapVoxelManipulator vmanip(m_map);
1002 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1003 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1004 bool is_apple_tree = myrand()%4 == 0;
1005 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1006 vmanip.blitBackAll(&modified_blocks);
1009 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1010 for(core::map<v3s16, MapBlock*>::Iterator
1011 i = modified_blocks.getIterator();
1012 i.atEnd() == false; i++)
1014 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1016 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1018 // Send a MEET_OTHER event
1020 event.type = MEET_OTHER;
1021 for(core::map<v3s16, MapBlock*>::Iterator
1022 i = modified_blocks.getIterator();
1023 i.atEnd() == false; i++)
1025 v3s16 p = i.getNode()->getKey();
1026 event.modified_blocks.insert(p, true);
1028 m_map->dispatchEvent(&event);
1039 //TimeTaker timer("Step active objects");
1041 // This helps the objects to send data at the same time
1042 bool send_recommended = false;
1043 m_send_recommended_timer += dtime;
1044 if(m_send_recommended_timer > 0.10)
1046 m_send_recommended_timer = 0;
1047 send_recommended = true;
1050 for(core::map<u16, ServerActiveObject*>::Iterator
1051 i = m_active_objects.getIterator();
1052 i.atEnd()==false; i++)
1054 ServerActiveObject* obj = i.getNode()->getValue();
1055 // Remove non-peaceful mobs on peaceful mode
1056 if(g_settings->getBool("only_peaceful_mobs")){
1057 if(!obj->isPeaceful())
1058 obj->m_removed = true;
1060 // Don't step if is to be removed or stored statically
1061 if(obj->m_removed || obj->m_pending_deactivation)
1064 obj->step(dtime, send_recommended);
1065 // Read messages from object
1066 while(obj->m_messages_out.size() > 0)
1068 m_active_object_messages.push_back(
1069 obj->m_messages_out.pop_front());
1075 Manage active objects
1077 if(m_object_management_interval.step(dtime, 0.5))
1080 Remove objects that satisfy (m_removed && m_known_by_count==0)
1082 removeRemovedObjects();
1085 if(g_settings->getBool("enable_experimental"))
1092 m_random_spawn_timer -= dtime;
1093 if(m_random_spawn_timer < 0)
1095 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1096 //m_random_spawn_timer += 2.0;
1097 m_random_spawn_timer += 200.0;
1103 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1104 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1105 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1107 Player *player = getRandomConnectedPlayer();
1110 pos = player->getPosition();
1112 myrand_range(-3,3)*BS,
1114 myrand_range(-3,3)*BS
1118 Create a ServerActiveObject
1121 //TestSAO *obj = new TestSAO(this, 0, pos);
1122 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1123 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1124 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1125 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1127 infostream<<"Server: Spawning MobV2SAO at "
1128 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1130 Settings properties;
1131 getMob_dungeon_master(properties);
1132 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1133 addActiveObject(obj);
1137 } // enable_experimental
1140 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1142 core::map<u16, ServerActiveObject*>::Node *n;
1143 n = m_active_objects.find(id);
1146 return n->getValue();
1149 bool isFreeServerActiveObjectId(u16 id,
1150 core::map<u16, ServerActiveObject*> &objects)
1155 for(core::map<u16, ServerActiveObject*>::Iterator
1156 i = objects.getIterator();
1157 i.atEnd()==false; i++)
1159 if(i.getNode()->getKey() == id)
1165 u16 getFreeServerActiveObjectId(
1166 core::map<u16, ServerActiveObject*> &objects)
1171 if(isFreeServerActiveObjectId(new_id, objects))
1181 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1184 u16 id = addActiveObjectRaw(object, true);
1188 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1192 v3f objectpos = obj->getBasePosition();
1194 // The block in which the object resides in
1195 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1198 Update the static data
1201 // Create new static object
1202 std::string staticdata = obj->getStaticData();
1203 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1204 // Add to the block where the object is located in
1205 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1206 // Get or generate the block
1207 MapBlock *block = m_map->emergeBlock(blockpos);
1209 bool succeeded = false;
1213 block->m_static_objects.insert(0, s_obj);
1214 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1218 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1219 <<"Could not find or generate "
1220 <<"a block for storing static object"<<std::endl;
1230 Finds out what new objects have been added to
1231 inside a radius around a position
1233 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1234 core::map<u16, bool> ¤t_objects,
1235 core::map<u16, bool> &added_objects)
1237 v3f pos_f = intToFloat(pos, BS);
1238 f32 radius_f = radius * BS;
1240 Go through the object list,
1241 - discard m_removed objects,
1242 - discard objects that are too far away,
1243 - discard objects that are found in current_objects.
1244 - add remaining objects to added_objects
1246 for(core::map<u16, ServerActiveObject*>::Iterator
1247 i = m_active_objects.getIterator();
1248 i.atEnd()==false; i++)
1250 u16 id = i.getNode()->getKey();
1252 ServerActiveObject *object = i.getNode()->getValue();
1255 // Discard if removed
1256 if(object->m_removed)
1258 // Discard if too far
1259 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1260 if(distance_f > radius_f)
1262 // Discard if already on current_objects
1263 core::map<u16, bool>::Node *n;
1264 n = current_objects.find(id);
1267 // Add to added_objects
1268 added_objects.insert(id, false);
1273 Finds out what objects have been removed from
1274 inside a radius around a position
1276 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1277 core::map<u16, bool> ¤t_objects,
1278 core::map<u16, bool> &removed_objects)
1280 v3f pos_f = intToFloat(pos, BS);
1281 f32 radius_f = radius * BS;
1283 Go through current_objects; object is removed if:
1284 - object is not found in m_active_objects (this is actually an
1285 error condition; objects should be set m_removed=true and removed
1286 only after all clients have been informed about removal), or
1287 - object has m_removed=true, or
1288 - object is too far away
1290 for(core::map<u16, bool>::Iterator
1291 i = current_objects.getIterator();
1292 i.atEnd()==false; i++)
1294 u16 id = i.getNode()->getKey();
1295 ServerActiveObject *object = getActiveObject(id);
1298 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1299 <<" object in current_objects is NULL"<<std::endl;
1301 else if(object->m_removed == false)
1303 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1304 /*infostream<<"removed == false"
1305 <<"distance_f = "<<distance_f
1306 <<", radius_f = "<<radius_f<<std::endl;*/
1307 if(distance_f < radius_f)
1313 removed_objects.insert(id, false);
1317 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1319 if(m_active_object_messages.size() == 0)
1320 return ActiveObjectMessage(0);
1322 return m_active_object_messages.pop_front();
1326 ************ Private methods *************
1329 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1333 if(object->getId() == 0)
1335 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1338 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1339 <<"no free ids available"<<std::endl;
1343 object->setId(new_id);
1345 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1347 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1348 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1352 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1353 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1355 m_active_objects.insert(object->getId(), object);
1357 // Add static object to active static list of the block
1358 v3f objectpos = object->getBasePosition();
1359 std::string staticdata = object->getStaticData();
1360 StaticObject s_obj(object->getType(), objectpos, staticdata);
1361 // Add to the block where the object is located in
1362 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1363 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1366 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1367 object->m_static_exists = true;
1368 object->m_static_block = blockpos;
1371 block->setChangedFlag();
1374 infostream<<"ServerEnv: Could not find a block for "
1375 <<"storing newly added static active object"<<std::endl;
1378 return object->getId();
1382 Remove objects that satisfy (m_removed && m_known_by_count==0)
1384 void ServerEnvironment::removeRemovedObjects()
1386 core::list<u16> objects_to_remove;
1387 for(core::map<u16, ServerActiveObject*>::Iterator
1388 i = m_active_objects.getIterator();
1389 i.atEnd()==false; i++)
1391 u16 id = i.getNode()->getKey();
1392 ServerActiveObject* obj = i.getNode()->getValue();
1393 // This shouldn't happen but check it
1396 infostream<<"NULL object found in ServerEnvironment"
1397 <<" while finding removed objects. id="<<id<<std::endl;
1398 // Id to be removed from m_active_objects
1399 objects_to_remove.push_back(id);
1404 We will delete objects that are marked as removed or thatare
1405 waiting for deletion after deactivation
1407 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1411 Delete static data from block if is marked as removed
1413 if(obj->m_static_exists && obj->m_removed)
1415 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1418 block->m_static_objects.remove(id);
1419 block->setChangedFlag();
1423 // If m_known_by_count > 0, don't actually remove.
1424 if(obj->m_known_by_count > 0)
1429 // Id to be removed from m_active_objects
1430 objects_to_remove.push_back(id);
1432 // Remove references from m_active_objects
1433 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1434 i != objects_to_remove.end(); i++)
1436 m_active_objects.remove(*i);
1441 Convert stored objects from blocks near the players to active.
1443 void ServerEnvironment::activateObjects(MapBlock *block)
1447 // Ignore if no stored objects (to not set changed flag)
1448 if(block->m_static_objects.m_stored.size() == 0)
1450 // A list for objects that couldn't be converted to static for some
1451 // reason. They will be stored back.
1452 core::list<StaticObject> new_stored;
1453 // Loop through stored static objects
1454 for(core::list<StaticObject>::Iterator
1455 i = block->m_static_objects.m_stored.begin();
1456 i != block->m_static_objects.m_stored.end(); i++)
1458 /*infostream<<"Server: Creating an active object from "
1459 <<"static data"<<std::endl;*/
1460 StaticObject &s_obj = *i;
1461 // Create an active object from the data
1462 ServerActiveObject *obj = ServerActiveObject::create
1463 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1464 // If couldn't create object, store static data back.
1467 new_stored.push_back(s_obj);
1470 // This will also add the object to the active static list
1471 addActiveObjectRaw(obj, false);
1472 //u16 id = addActiveObjectRaw(obj, false);
1474 // Clear stored list
1475 block->m_static_objects.m_stored.clear();
1476 // Add leftover failed stuff to stored list
1477 for(core::list<StaticObject>::Iterator
1478 i = new_stored.begin();
1479 i != new_stored.end(); i++)
1481 StaticObject &s_obj = *i;
1482 block->m_static_objects.m_stored.push_back(s_obj);
1484 // Block has been modified
1485 // NOTE: No it has not really. Save I/O here.
1486 //block->setChangedFlag();
1490 Convert objects that are not in active blocks to static.
1492 If m_known_by_count != 0, active object is not deleted, but static
1493 data is still updated.
1495 If force_delete is set, active object is deleted nevertheless. It
1496 shall only be set so in the destructor of the environment.
1498 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1500 core::list<u16> objects_to_remove;
1501 for(core::map<u16, ServerActiveObject*>::Iterator
1502 i = m_active_objects.getIterator();
1503 i.atEnd()==false; i++)
1505 ServerActiveObject* obj = i.getNode()->getValue();
1507 // This shouldn't happen but check it
1510 infostream<<"NULL object found in ServerEnvironment"
1516 u16 id = i.getNode()->getKey();
1517 v3f objectpos = obj->getBasePosition();
1519 // The block in which the object resides in
1520 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1522 // If block is active, don't remove
1523 if(m_active_blocks.contains(blockpos_o))
1527 Update the static data
1530 // Delete old static object
1531 MapBlock *oldblock = NULL;
1532 if(obj->m_static_exists)
1534 MapBlock *block = m_map->getBlockNoCreateNoEx
1535 (obj->m_static_block);
1538 block->m_static_objects.remove(id);
1542 // Create new static object
1543 std::string staticdata = obj->getStaticData();
1544 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1545 // Add to the block where the object is located in
1546 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1547 // Get or generate the block
1548 MapBlock *block = m_map->emergeBlock(blockpos);
1550 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1553 // Block not found. Is the old block still ok?
1556 // Load from disk or generate
1558 block = m_map->emergeBlock(blockpos);
1563 block->m_static_objects.insert(0, s_obj);
1564 block->setChangedFlag();
1565 obj->m_static_exists = true;
1566 obj->m_static_block = block->getPos();
1569 infostream<<"ServerEnv: Could not find or generate "
1570 <<"a block for storing static object"<<std::endl;
1571 obj->m_static_exists = false;
1576 Delete active object if not known by some client,
1577 else set pending deactivation
1580 // If known by some client, don't delete.
1581 if(obj->m_known_by_count > 0 && force_delete == false)
1583 obj->m_pending_deactivation = true;
1587 /*infostream<<"Server: Stored static data. Deleting object."
1589 // Delete active object
1591 // Id to be removed from m_active_objects
1592 objects_to_remove.push_back(id);
1595 // Remove references from m_active_objects
1596 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1597 i != objects_to_remove.end(); i++)
1599 m_active_objects.remove(*i);
1610 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1618 ClientEnvironment::~ClientEnvironment()
1620 // delete active objects
1621 for(core::map<u16, ClientActiveObject*>::Iterator
1622 i = m_active_objects.getIterator();
1623 i.atEnd()==false; i++)
1625 delete i.getNode()->getValue();
1632 void ClientEnvironment::addPlayer(Player *player)
1634 DSTACK(__FUNCTION_NAME);
1636 It is a failure if player is local and there already is a local
1639 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1641 Environment::addPlayer(player);
1644 LocalPlayer * ClientEnvironment::getLocalPlayer()
1646 for(core::list<Player*>::Iterator i = m_players.begin();
1647 i != m_players.end(); i++)
1649 Player *player = *i;
1650 if(player->isLocal())
1651 return (LocalPlayer*)player;
1656 void ClientEnvironment::step(float dtime)
1658 DSTACK(__FUNCTION_NAME);
1660 // Get some settings
1661 bool free_move = g_settings->getBool("free_move");
1662 bool footprints = g_settings->getBool("footprints");
1665 LocalPlayer *lplayer = getLocalPlayer();
1667 // collision info queue
1668 core::list<CollisionInfo> player_collisions;
1671 Get the speed the player is going
1673 bool is_climbing = lplayer->is_climbing;
1675 f32 player_speed = 0.001; // just some small value
1676 player_speed = lplayer->getSpeed().getLength();
1679 Maximum position increment
1681 //f32 position_max_increment = 0.05*BS;
1682 f32 position_max_increment = 0.1*BS;
1684 // Maximum time increment (for collision detection etc)
1685 // time = distance / speed
1686 f32 dtime_max_increment = position_max_increment / player_speed;
1688 // Maximum time increment is 10ms or lower
1689 if(dtime_max_increment > 0.01)
1690 dtime_max_increment = 0.01;
1692 // Don't allow overly huge dtime
1696 f32 dtime_downcount = dtime;
1699 Stuff that has a maximum time increment
1708 if(dtime_downcount > dtime_max_increment)
1710 dtime_part = dtime_max_increment;
1711 dtime_downcount -= dtime_part;
1715 dtime_part = dtime_downcount;
1717 Setting this to 0 (no -=dtime_part) disables an infinite loop
1718 when dtime_part is so small that dtime_downcount -= dtime_part
1721 dtime_downcount = 0;
1729 v3f lplayerpos = lplayer->getPosition();
1732 if(free_move == false && is_climbing == false)
1735 v3f speed = lplayer->getSpeed();
1736 if(lplayer->swimming_up == false)
1737 speed.Y -= 9.81 * BS * dtime_part * 2;
1740 if(lplayer->in_water_stable || lplayer->in_water)
1742 f32 max_down = 2.0*BS;
1743 if(speed.Y < -max_down) speed.Y = -max_down;
1746 if(speed.getLength() > max)
1748 speed = speed / speed.getLength() * max;
1752 lplayer->setSpeed(speed);
1757 This also does collision detection.
1759 lplayer->move(dtime_part, *m_map, position_max_increment,
1760 &player_collisions);
1763 while(dtime_downcount > 0.001);
1765 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1767 for(core::list<CollisionInfo>::Iterator
1768 i = player_collisions.begin();
1769 i != player_collisions.end(); i++)
1771 CollisionInfo &info = *i;
1772 if(info.t == COLLISION_FALL)
1774 //f32 tolerance = BS*10; // 2 without damage
1775 f32 tolerance = BS*12; // 3 without damage
1777 if(info.speed > tolerance)
1779 f32 damage_f = (info.speed - tolerance)/BS*factor;
1780 u16 damage = (u16)(damage_f+0.5);
1781 if(lplayer->hp > damage)
1782 lplayer->hp -= damage;
1786 ClientEnvEvent event;
1787 event.type = CEE_PLAYER_DAMAGE;
1788 event.player_damage.amount = damage;
1789 m_client_event_queue.push_back(event);
1795 A quick draft of lava damage
1797 if(m_lava_hurt_interval.step(dtime, 1.0))
1799 v3f pf = lplayer->getPosition();
1801 // Feet, middle and head
1802 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1803 MapNode n1 = m_map->getNodeNoEx(p1);
1804 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1805 MapNode n2 = m_map->getNodeNoEx(p2);
1806 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1807 MapNode n3 = m_map->getNodeNoEx(p2);
1809 u32 damage_per_second = 0;
1810 damage_per_second = MYMAX(damage_per_second,
1811 content_features(n1).damage_per_second);
1812 damage_per_second = MYMAX(damage_per_second,
1813 content_features(n2).damage_per_second);
1814 damage_per_second = MYMAX(damage_per_second,
1815 content_features(n3).damage_per_second);
1817 if(damage_per_second != 0)
1819 ClientEnvEvent event;
1820 event.type = CEE_PLAYER_DAMAGE;
1821 event.player_damage.amount = damage_per_second;
1822 m_client_event_queue.push_back(event);
1827 Stuff that can be done in an arbitarily large dtime
1829 for(core::list<Player*>::Iterator i = m_players.begin();
1830 i != m_players.end(); i++)
1832 Player *player = *i;
1833 v3f playerpos = player->getPosition();
1836 Handle non-local players
1838 if(player->isLocal() == false)
1841 player->move(dtime, *m_map, 100*BS);
1845 // Update lighting on all players on client
1846 u8 light = LIGHT_MAX;
1849 v3s16 p = player->getLightPosition();
1850 MapNode n = m_map->getNode(p);
1851 light = n.getLightBlend(getDayNightRatio());
1853 catch(InvalidPositionException &e) {}
1854 player->updateLight(light);
1857 Add footsteps to grass
1861 // Get node that is at BS/4 under player
1862 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1864 MapNode n = m_map->getNode(bottompos);
1865 if(n.getContent() == CONTENT_GRASS)
1867 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1868 m_map->setNode(bottompos, n);
1869 // Update mesh on client
1870 if(m_map->mapType() == MAPTYPE_CLIENT)
1872 v3s16 p_blocks = getNodeBlockPos(bottompos);
1873 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1874 //b->updateMesh(getDayNightRatio());
1875 b->setMeshExpired(true);
1879 catch(InvalidPositionException &e)
1886 Step active objects and update lighting of them
1889 for(core::map<u16, ClientActiveObject*>::Iterator
1890 i = m_active_objects.getIterator();
1891 i.atEnd()==false; i++)
1893 ClientActiveObject* obj = i.getNode()->getValue();
1895 obj->step(dtime, this);
1897 if(m_active_object_light_update_interval.step(dtime, 0.21))
1900 //u8 light = LIGHT_MAX;
1904 v3s16 p = obj->getLightPosition();
1905 MapNode n = m_map->getNode(p);
1906 light = n.getLightBlend(getDayNightRatio());
1908 catch(InvalidPositionException &e) {}
1909 obj->updateLight(light);
1914 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1916 m_map->updateMeshes(blockpos, getDayNightRatio());
1919 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1921 m_map->expireMeshes(only_daynight_diffed);
1924 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1926 core::map<u16, ClientActiveObject*>::Node *n;
1927 n = m_active_objects.find(id);
1930 return n->getValue();
1933 bool isFreeClientActiveObjectId(u16 id,
1934 core::map<u16, ClientActiveObject*> &objects)
1939 for(core::map<u16, ClientActiveObject*>::Iterator
1940 i = objects.getIterator();
1941 i.atEnd()==false; i++)
1943 if(i.getNode()->getKey() == id)
1949 u16 getFreeClientActiveObjectId(
1950 core::map<u16, ClientActiveObject*> &objects)
1955 if(isFreeClientActiveObjectId(new_id, objects))
1965 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1968 if(object->getId() == 0)
1970 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1973 infostream<<"ClientEnvironment::addActiveObject(): "
1974 <<"no free ids available"<<std::endl;
1978 object->setId(new_id);
1980 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1982 infostream<<"ClientEnvironment::addActiveObject(): "
1983 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1987 infostream<<"ClientEnvironment::addActiveObject(): "
1988 <<"added (id="<<object->getId()<<")"<<std::endl;
1989 m_active_objects.insert(object->getId(), object);
1990 object->addToScene(m_smgr);
1991 return object->getId();
1994 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1995 const std::string &init_data)
1997 ClientActiveObject* obj = ClientActiveObject::create(type);
2000 infostream<<"ClientEnvironment::addActiveObject(): "
2001 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2008 obj->initialize(init_data);
2010 addActiveObject(obj);
2013 void ClientEnvironment::removeActiveObject(u16 id)
2015 infostream<<"ClientEnvironment::removeActiveObject(): "
2016 <<"id="<<id<<std::endl;
2017 ClientActiveObject* obj = getActiveObject(id);
2020 infostream<<"ClientEnvironment::removeActiveObject(): "
2021 <<"id="<<id<<" not found"<<std::endl;
2024 obj->removeFromScene();
2026 m_active_objects.remove(id);
2029 void ClientEnvironment::processActiveObjectMessage(u16 id,
2030 const std::string &data)
2032 ClientActiveObject* obj = getActiveObject(id);
2035 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2036 <<" got message for id="<<id<<", which doesn't exist."
2040 obj->processMessage(data);
2044 Callbacks for activeobjects
2047 void ClientEnvironment::damageLocalPlayer(u8 damage)
2049 LocalPlayer *lplayer = getLocalPlayer();
2052 if(lplayer->hp > damage)
2053 lplayer->hp -= damage;
2057 ClientEnvEvent event;
2058 event.type = CEE_PLAYER_DAMAGE;
2059 event.player_damage.amount = damage;
2060 m_client_event_queue.push_back(event);
2064 Client likes to call these
2067 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2068 core::array<DistanceSortedActiveObject> &dest)
2070 for(core::map<u16, ClientActiveObject*>::Iterator
2071 i = m_active_objects.getIterator();
2072 i.atEnd()==false; i++)
2074 ClientActiveObject* obj = i.getNode()->getValue();
2076 f32 d = (obj->getPosition() - origin).getLength();
2081 DistanceSortedActiveObject dso(obj, d);
2083 dest.push_back(dso);
2087 ClientEnvEvent ClientEnvironment::getClientEvent()
2089 if(m_client_event_queue.size() == 0)
2091 ClientEnvEvent event;
2092 event.type = CEE_NONE;
2095 return m_client_event_queue.pop_front();
2098 #endif // #ifndef SERVER