3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
33 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
35 Environment::Environment():
40 Environment::~Environment()
43 for(core::list<Player*>::Iterator i = m_players.begin();
44 i != m_players.end(); i++)
50 void Environment::addPlayer(Player *player)
52 DSTACK(__FUNCTION_NAME);
54 Check that peer_ids are unique.
55 Also check that names are unique.
56 Exception: there can be multiple players with peer_id=0
58 // If peer id is non-zero, it has to be unique.
59 if(player->peer_id != 0)
60 assert(getPlayer(player->peer_id) == NULL);
61 // Name has to be unique.
62 assert(getPlayer(player->getName()) == NULL);
64 m_players.push_back(player);
67 void Environment::removePlayer(u16 peer_id)
69 DSTACK(__FUNCTION_NAME);
71 for(core::list<Player*>::Iterator i = m_players.begin();
72 i != m_players.end(); i++)
75 if(player->peer_id != peer_id)
80 // See if there is an another one
81 // (shouldn't be, but just to be sure)
86 Player * Environment::getPlayer(u16 peer_id)
88 for(core::list<Player*>::Iterator i = m_players.begin();
89 i != m_players.end(); i++)
92 if(player->peer_id == peer_id)
98 Player * Environment::getPlayer(const char *name)
100 for(core::list<Player*>::Iterator i = m_players.begin();
101 i != m_players.end(); i++)
104 if(strcmp(player->getName(), name) == 0)
110 Player * Environment::getRandomConnectedPlayer()
112 core::list<Player*> connected_players = getPlayers(true);
113 u32 chosen_one = myrand() % connected_players.size();
115 for(core::list<Player*>::Iterator
116 i = connected_players.begin();
117 i != connected_players.end(); i++)
129 Player * Environment::getNearestConnectedPlayer(v3f pos)
131 core::list<Player*> connected_players = getPlayers(true);
133 Player *nearest_player = NULL;
134 for(core::list<Player*>::Iterator
135 i = connected_players.begin();
136 i != connected_players.end(); i++)
139 f32 d = player->getPosition().getDistanceFrom(pos);
140 if(d < nearest_d || nearest_player == NULL)
143 nearest_player = player;
146 return nearest_player;
149 core::list<Player*> Environment::getPlayers()
154 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
156 core::list<Player*> newlist;
157 for(core::list<Player*>::Iterator
158 i = m_players.begin();
159 i != m_players.end(); i++)
163 if(ignore_disconnected)
165 // Ignore disconnected players
166 if(player->peer_id == 0)
170 newlist.push_back(player);
175 void Environment::printPlayers(std::ostream &o)
177 o<<"Players in environment:"<<std::endl;
178 for(core::list<Player*>::Iterator i = m_players.begin();
179 i != m_players.end(); i++)
182 o<<"Player peer_id="<<player->peer_id<<std::endl;
186 /*void Environment::setDayNightRatio(u32 r)
188 getDayNightRatio() = r;
191 u32 Environment::getDayNightRatio()
193 //return getDayNightRatio();
194 return time_to_daynight_ratio(m_time_of_day);
201 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
204 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
205 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
206 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
213 void ActiveBlockList::update(core::list<v3s16> &active_positions,
215 core::map<v3s16, bool> &blocks_removed,
216 core::map<v3s16, bool> &blocks_added)
221 core::map<v3s16, bool> newlist;
222 for(core::list<v3s16>::Iterator i = active_positions.begin();
223 i != active_positions.end(); i++)
225 fillRadiusBlock(*i, radius, newlist);
229 Find out which blocks on the old list are not on the new list
231 // Go through old list
232 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
233 i.atEnd()==false; i++)
235 v3s16 p = i.getNode()->getKey();
236 // If not on new list, it's been removed
237 if(newlist.find(p) == NULL)
238 blocks_removed.insert(p, true);
242 Find out which blocks on the new list are not on the old list
244 // Go through new list
245 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
246 i.atEnd()==false; i++)
248 v3s16 p = i.getNode()->getKey();
249 // If not on old list, it's been added
250 if(m_list.find(p) == NULL)
251 blocks_added.insert(p, true);
258 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
259 i.atEnd()==false; i++)
261 v3s16 p = i.getNode()->getKey();
262 m_list.insert(p, true);
270 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
273 m_random_spawn_timer(3),
274 m_send_recommended_timer(0),
276 m_game_time_fraction_counter(0)
280 ServerEnvironment::~ServerEnvironment()
282 // Clear active block list.
283 // This makes the next one delete all active objects.
284 m_active_blocks.clear();
286 // Convert all objects to static and delete the active objects
287 deactivateFarObjects(true);
293 void ServerEnvironment::serializePlayers(const std::string &savedir)
295 std::string players_path = savedir + "/players";
296 fs::CreateDir(players_path);
298 core::map<Player*, bool> saved_players;
300 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
301 for(u32 i=0; i<player_files.size(); i++)
303 if(player_files[i].dir)
306 // Full path to this file
307 std::string path = players_path + "/" + player_files[i].name;
309 //infostream<<"Checking player file "<<path<<std::endl;
311 // Load player to see what is its name
312 ServerRemotePlayer testplayer;
314 // Open file and deserialize
315 std::ifstream is(path.c_str(), std::ios_base::binary);
316 if(is.good() == false)
318 infostream<<"Failed to read "<<path<<std::endl;
321 testplayer.deSerialize(is);
324 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
326 // Search for the player
327 std::string playername = testplayer.getName();
328 Player *player = getPlayer(playername.c_str());
331 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
335 //infostream<<"Found matching player, overwriting."<<std::endl;
337 // OK, found. Save player there.
339 // Open file and serialize
340 std::ofstream os(path.c_str(), std::ios_base::binary);
341 if(os.good() == false)
343 infostream<<"Failed to overwrite "<<path<<std::endl;
346 player->serialize(os);
347 saved_players.insert(player, true);
351 for(core::list<Player*>::Iterator i = m_players.begin();
352 i != m_players.end(); i++)
355 if(saved_players.find(player) != NULL)
357 /*infostream<<"Player "<<player->getName()
358 <<" was already saved."<<std::endl;*/
361 std::string playername = player->getName();
362 // Don't save unnamed player
365 //infostream<<"Not saving unnamed player."<<std::endl;
371 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
372 playername = "player";
373 std::string path = players_path + "/" + playername;
375 for(u32 i=0; i<1000; i++)
377 if(fs::PathExists(path) == false)
382 path = players_path + "/" + playername + itos(i);
386 infostream<<"Didn't find free file for player"<<std::endl;
391 /*infostream<<"Saving player "<<player->getName()<<" to "
393 // Open file and serialize
394 std::ofstream os(path.c_str(), std::ios_base::binary);
395 if(os.good() == false)
397 infostream<<"Failed to overwrite "<<path<<std::endl;
400 player->serialize(os);
401 saved_players.insert(player, true);
405 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
408 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
410 std::string players_path = savedir + "/players";
412 core::map<Player*, bool> saved_players;
414 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
415 for(u32 i=0; i<player_files.size(); i++)
417 if(player_files[i].dir)
420 // Full path to this file
421 std::string path = players_path + "/" + player_files[i].name;
423 infostream<<"Checking player file "<<path<<std::endl;
425 // Load player to see what is its name
426 ServerRemotePlayer testplayer;
428 // Open file and deserialize
429 std::ifstream is(path.c_str(), std::ios_base::binary);
430 if(is.good() == false)
432 infostream<<"Failed to read "<<path<<std::endl;
435 testplayer.deSerialize(is);
438 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
440 infostream<<"Not loading player with invalid name: "
441 <<testplayer.getName()<<std::endl;
444 infostream<<"Loaded test player with name "<<testplayer.getName()
447 // Search for the player
448 std::string playername = testplayer.getName();
449 Player *player = getPlayer(playername.c_str());
450 bool newplayer = false;
453 infostream<<"Is a new player"<<std::endl;
454 player = new ServerRemotePlayer();
460 infostream<<"Reading player "<<testplayer.getName()<<" from "
462 // Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
464 if(is.good() == false)
466 infostream<<"Failed to read "<<path<<std::endl;
469 player->deSerialize(is);
477 void ServerEnvironment::saveMeta(const std::string &savedir)
479 std::string path = savedir + "/env_meta.txt";
481 // Open file and serialize
482 std::ofstream os(path.c_str(), std::ios_base::binary);
483 if(os.good() == false)
485 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
487 throw SerializationError("Couldn't save env meta");
491 args.setU64("game_time", m_game_time);
492 args.setU64("time_of_day", getTimeOfDay());
497 void ServerEnvironment::loadMeta(const std::string &savedir)
499 std::string path = savedir + "/env_meta.txt";
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
507 throw SerializationError("Couldn't load env meta");
515 throw SerializationError
516 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
518 std::getline(is, line);
519 std::string trimmedline = trim(line);
520 if(trimmedline == "EnvArgsEnd")
522 args.parseConfigLine(line);
526 m_game_time = args.getU64("game_time");
527 }catch(SettingNotFoundException &e){
528 // Getting this is crucial, otherwise timestamps are useless
529 throw SerializationError("Couldn't load env meta game_time");
533 m_time_of_day = args.getU64("time_of_day");
534 }catch(SettingNotFoundException &e){
535 // This is not as important
536 m_time_of_day = 9000;
541 // This is probably very useless
542 void spawnRandomObjects(MapBlock *block)
544 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
545 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
547 bool last_node_walkable = false;
548 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
551 MapNode n = block->getNodeNoEx(p);
552 if(n.getContent() == CONTENT_IGNORE)
554 if(content_features(n).liquid_type != LIQUID_NONE)
556 if(content_features(n).walkable)
558 last_node_walkable = true;
561 if(last_node_walkable)
563 // If block contains light information
564 if(content_features(n).param_type == CPT_LIGHT)
566 if(n.getLight(LIGHTBANK_DAY) <= 5)
568 if(myrand() % 1000 == 0)
570 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
572 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
573 std::string data = obj->getStaticData();
574 StaticObject s_obj(obj->getType(),
575 obj->getBasePosition(), data);
577 block->m_static_objects.insert(0, s_obj);
579 block->setChangedFlag();
584 last_node_walkable = false;
590 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
592 // Get time difference
594 u32 stamp = block->getTimestamp();
595 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
596 dtime_s = m_game_time - block->getTimestamp();
597 dtime_s += additional_dtime;
599 // Set current time as timestamp (and let it set ChangedFlag)
600 block->setTimestamp(m_game_time);
602 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
604 // Activate stored objects
605 activateObjects(block);
608 bool changed = block->m_node_metadata.step((float)dtime_s);
612 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
613 event.p = block->getPos();
614 m_map->dispatchEvent(&event);
616 block->setChangedFlag();
619 // TODO: Do something
620 // TODO: Implement usage of ActiveBlockModifier
622 // Here's a quick demonstration
624 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
625 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
626 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
628 v3s16 p = p0 + block->getPosRelative();
629 MapNode n = block->getNodeNoEx(p0);
632 // Convert all mud under proper day lighting to grass
633 if(n.getContent() == CONTENT_MUD)
637 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
638 if(content_features(n_top).air_equivalent &&
639 n_top.getLight(LIGHTBANK_DAY) >= 13)
641 n.setContent(CONTENT_GRASS);
642 m_map->addNodeWithEvent(p, n);
650 static void getMob_dungeon_master(Settings &properties)
652 properties.set("looks", "dungeon_master");
653 properties.setFloat("yaw", 1.57);
654 properties.setFloat("hp", 30);
655 properties.setBool("bright_shooting", true);
656 properties.set("shoot_type", "fireball");
657 properties.set("shoot_y", "0.7");
658 properties.set("player_hit_damage", "1");
659 properties.set("player_hit_distance", "1.0");
660 properties.set("player_hit_interval", "0.5");
661 properties.setBool("mindless_rage", myrand_range(0,100)==0);
664 void ServerEnvironment::step(float dtime)
666 DSTACK(__FUNCTION_NAME);
668 //TimeTaker timer("ServerEnv step");
671 bool footprints = g_settings->getBool("footprints");
677 m_game_time_fraction_counter += dtime;
678 u32 inc_i = (u32)m_game_time_fraction_counter;
679 m_game_time += inc_i;
680 m_game_time_fraction_counter -= (float)inc_i;
687 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
688 for(core::list<Player*>::Iterator i = m_players.begin();
689 i != m_players.end(); i++)
693 // Ignore disconnected players
694 if(player->peer_id == 0)
697 v3f playerpos = player->getPosition();
700 player->move(dtime, *m_map, 100*BS);
703 Add footsteps to grass
707 // Get node that is at BS/4 under player
708 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
710 MapNode n = m_map->getNode(bottompos);
711 if(n.getContent() == CONTENT_GRASS)
713 n.setContent(CONTENT_GRASS_FOOTSTEPS);
714 m_map->setNode(bottompos, n);
717 catch(InvalidPositionException &e)
725 Manage active block list
727 if(m_active_blocks_management_interval.step(dtime, 2.0))
729 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
731 Get player block positions
733 core::list<v3s16> players_blockpos;
734 for(core::list<Player*>::Iterator
735 i = m_players.begin();
736 i != m_players.end(); i++)
739 // Ignore disconnected players
740 if(player->peer_id == 0)
742 v3s16 blockpos = getNodeBlockPos(
743 floatToInt(player->getPosition(), BS));
744 players_blockpos.push_back(blockpos);
748 Update list of active blocks, collecting changes
750 const s16 active_block_range = g_settings->getS16("active_block_range");
751 core::map<v3s16, bool> blocks_removed;
752 core::map<v3s16, bool> blocks_added;
753 m_active_blocks.update(players_blockpos, active_block_range,
754 blocks_removed, blocks_added);
757 Handle removed blocks
760 // Convert active objects that are no more in active blocks to static
761 deactivateFarObjects(false);
763 for(core::map<v3s16, bool>::Iterator
764 i = blocks_removed.getIterator();
765 i.atEnd()==false; i++)
767 v3s16 p = i.getNode()->getKey();
769 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
770 <<") became inactive"<<std::endl;*/
772 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
776 // Set current time as timestamp (and let it set ChangedFlag)
777 block->setTimestamp(m_game_time);
784 for(core::map<v3s16, bool>::Iterator
785 i = blocks_added.getIterator();
786 i.atEnd()==false; i++)
788 v3s16 p = i.getNode()->getKey();
790 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
791 <<") became active"<<std::endl;*/
793 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
797 activateBlock(block);
802 Mess around in active blocks
804 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
806 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
810 for(core::map<v3s16, bool>::Iterator
811 i = m_active_blocks.m_list.getIterator();
812 i.atEnd()==false; i++)
814 v3s16 p = i.getNode()->getKey();
816 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
817 <<") being handled"<<std::endl;*/
819 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
823 // Reset block usage timer
824 block->resetUsageTimer();
826 // Set current time as timestamp
827 block->setTimestampNoChangedFlag(m_game_time);
830 bool changed = block->m_node_metadata.step(dtime);
834 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
836 m_map->dispatchEvent(&event);
838 block->setChangedFlag();
843 if(m_active_blocks_test_interval.step(dtime, 10.0))
845 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
846 //float dtime = 10.0;
848 for(core::map<v3s16, bool>::Iterator
849 i = m_active_blocks.m_list.getIterator();
850 i.atEnd()==false; i++)
852 v3s16 p = i.getNode()->getKey();
854 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
855 <<") being handled"<<std::endl;*/
857 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
861 // Set current time as timestamp
862 block->setTimestampNoChangedFlag(m_game_time);
867 Note that map modifications should be done using the event-
868 making map methods so that the server gets information
871 Reading can be done quickly directly from the block.
873 Everything should bind to inside this single content
874 searching loop to keep things fast.
876 // TODO: Implement usage of ActiveBlockModifier
878 // Find out how many objects the block contains
879 //u32 active_object_count = block->m_static_objects.m_active.size();
880 // Find out how many objects this and all the neighbors contain
881 u32 active_object_count_wider = 0;
882 for(s16 x=-1; x<=1; x++)
883 for(s16 y=-1; y<=1; y++)
884 for(s16 z=-1; z<=1; z++)
886 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
889 active_object_count_wider +=
890 block->m_static_objects.m_active.size();
894 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
895 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
896 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
898 v3s16 p = p0 + block->getPosRelative();
899 MapNode n = block->getNodeNoEx(p0);
903 Convert mud under proper lighting to grass
905 if(n.getContent() == CONTENT_MUD)
909 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
910 if(content_features(n_top).air_equivalent &&
911 n_top.getLightBlend(getDayNightRatio()) >= 13)
913 n.setContent(CONTENT_GRASS);
914 m_map->addNodeWithEvent(p, n);
919 Convert grass into mud if under something else than air
921 if(n.getContent() == CONTENT_GRASS)
923 //if(myrand()%20 == 0)
925 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
926 if(content_features(n_top).air_equivalent == false)
928 n.setContent(CONTENT_MUD);
929 m_map->addNodeWithEvent(p, n);
934 Rats spawn around regular trees
936 if(n.getContent() == CONTENT_TREE ||
937 n.getContent() == CONTENT_JUNGLETREE)
939 if(myrand()%200 == 0 && active_object_count_wider == 0)
941 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
942 0, myrand_range(-2, 2));
943 MapNode n1 = m_map->getNodeNoEx(p1);
944 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
945 if(n1b.getContent() == CONTENT_GRASS &&
946 n1.getContent() == CONTENT_AIR)
948 v3f pos = intToFloat(p1, BS);
949 ServerActiveObject *obj = new RatSAO(this, 0, pos);
950 addActiveObject(obj);
955 Fun things spawn in caves and dungeons
957 if(n.getContent() == CONTENT_STONE ||
958 n.getContent() == CONTENT_MOSSYCOBBLE)
960 if(myrand()%200 == 0 && active_object_count_wider == 0)
962 v3s16 p1 = p + v3s16(0,1,0);
963 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
964 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
965 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
966 if(n1a.getContent() == CONTENT_AIR &&
967 n1b.getContent() == CONTENT_AIR)
969 v3f pos = intToFloat(p1, BS);
971 if(i == 0 || i == 1){
972 actionstream<<"A dungeon master spawns at "
975 getMob_dungeon_master(properties);
976 ServerActiveObject *obj = new MobV2SAO(
977 this, 0, pos, &properties);
978 addActiveObject(obj);
979 } else if(i == 2 || i == 3){
980 actionstream<<"Rats spawn at "
982 for(int j=0; j<3; j++){
983 ServerActiveObject *obj = new RatSAO(
985 addActiveObject(obj);
988 actionstream<<"An oerkki spawns at "
990 ServerActiveObject *obj = new Oerkki1SAO(
992 addActiveObject(obj);
999 Make trees from saplings!
1001 if(n.getContent() == CONTENT_SAPLING)
1003 if(myrand()%50 == 0)
1005 actionstream<<"A sapling grows into a tree at "
1008 core::map<v3s16, MapBlock*> modified_blocks;
1010 ManualMapVoxelManipulator vmanip(m_map);
1011 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1012 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1013 bool is_apple_tree = myrand()%4 == 0;
1014 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1015 vmanip.blitBackAll(&modified_blocks);
1018 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1025 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1027 // Send a MEET_OTHER event
1029 event.type = MEET_OTHER;
1030 for(core::map<v3s16, MapBlock*>::Iterator
1031 i = modified_blocks.getIterator();
1032 i.atEnd() == false; i++)
1034 v3s16 p = i.getNode()->getKey();
1035 event.modified_blocks.insert(p, true);
1037 m_map->dispatchEvent(&event);
1048 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1049 //TimeTaker timer("Step active objects");
1051 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1053 // This helps the objects to send data at the same time
1054 bool send_recommended = false;
1055 m_send_recommended_timer += dtime;
1056 if(m_send_recommended_timer > 0.10)
1058 m_send_recommended_timer = 0;
1059 send_recommended = true;
1062 for(core::map<u16, ServerActiveObject*>::Iterator
1063 i = m_active_objects.getIterator();
1064 i.atEnd()==false; i++)
1066 ServerActiveObject* obj = i.getNode()->getValue();
1067 // Remove non-peaceful mobs on peaceful mode
1068 if(g_settings->getBool("only_peaceful_mobs")){
1069 if(!obj->isPeaceful())
1070 obj->m_removed = true;
1072 // Don't step if is to be removed or stored statically
1073 if(obj->m_removed || obj->m_pending_deactivation)
1076 obj->step(dtime, send_recommended);
1077 // Read messages from object
1078 while(obj->m_messages_out.size() > 0)
1080 m_active_object_messages.push_back(
1081 obj->m_messages_out.pop_front());
1087 Manage active objects
1089 if(m_object_management_interval.step(dtime, 0.5))
1091 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1093 Remove objects that satisfy (m_removed && m_known_by_count==0)
1095 removeRemovedObjects();
1098 if(g_settings->getBool("enable_experimental"))
1105 m_random_spawn_timer -= dtime;
1106 if(m_random_spawn_timer < 0)
1108 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1109 //m_random_spawn_timer += 2.0;
1110 m_random_spawn_timer += 200.0;
1116 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1117 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1118 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1120 Player *player = getRandomConnectedPlayer();
1123 pos = player->getPosition();
1125 myrand_range(-3,3)*BS,
1127 myrand_range(-3,3)*BS
1131 Create a ServerActiveObject
1134 //TestSAO *obj = new TestSAO(this, 0, pos);
1135 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1136 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1137 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1138 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1140 infostream<<"Server: Spawning MobV2SAO at "
1141 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1143 Settings properties;
1144 getMob_dungeon_master(properties);
1145 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1146 addActiveObject(obj);
1150 } // enable_experimental
1153 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1155 core::map<u16, ServerActiveObject*>::Node *n;
1156 n = m_active_objects.find(id);
1159 return n->getValue();
1162 bool isFreeServerActiveObjectId(u16 id,
1163 core::map<u16, ServerActiveObject*> &objects)
1168 for(core::map<u16, ServerActiveObject*>::Iterator
1169 i = objects.getIterator();
1170 i.atEnd()==false; i++)
1172 if(i.getNode()->getKey() == id)
1178 u16 getFreeServerActiveObjectId(
1179 core::map<u16, ServerActiveObject*> &objects)
1184 if(isFreeServerActiveObjectId(new_id, objects))
1194 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1197 u16 id = addActiveObjectRaw(object, true);
1201 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1205 v3f objectpos = obj->getBasePosition();
1207 // The block in which the object resides in
1208 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1211 Update the static data
1214 // Create new static object
1215 std::string staticdata = obj->getStaticData();
1216 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1217 // Add to the block where the object is located in
1218 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1219 // Get or generate the block
1220 MapBlock *block = m_map->emergeBlock(blockpos);
1222 bool succeeded = false;
1226 block->m_static_objects.insert(0, s_obj);
1227 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1231 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1232 <<"Could not find or generate "
1233 <<"a block for storing static object"<<std::endl;
1243 Finds out what new objects have been added to
1244 inside a radius around a position
1246 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1247 core::map<u16, bool> ¤t_objects,
1248 core::map<u16, bool> &added_objects)
1250 v3f pos_f = intToFloat(pos, BS);
1251 f32 radius_f = radius * BS;
1253 Go through the object list,
1254 - discard m_removed objects,
1255 - discard objects that are too far away,
1256 - discard objects that are found in current_objects.
1257 - add remaining objects to added_objects
1259 for(core::map<u16, ServerActiveObject*>::Iterator
1260 i = m_active_objects.getIterator();
1261 i.atEnd()==false; i++)
1263 u16 id = i.getNode()->getKey();
1265 ServerActiveObject *object = i.getNode()->getValue();
1268 // Discard if removed
1269 if(object->m_removed)
1271 // Discard if too far
1272 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1273 if(distance_f > radius_f)
1275 // Discard if already on current_objects
1276 core::map<u16, bool>::Node *n;
1277 n = current_objects.find(id);
1280 // Add to added_objects
1281 added_objects.insert(id, false);
1286 Finds out what objects have been removed from
1287 inside a radius around a position
1289 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1290 core::map<u16, bool> ¤t_objects,
1291 core::map<u16, bool> &removed_objects)
1293 v3f pos_f = intToFloat(pos, BS);
1294 f32 radius_f = radius * BS;
1296 Go through current_objects; object is removed if:
1297 - object is not found in m_active_objects (this is actually an
1298 error condition; objects should be set m_removed=true and removed
1299 only after all clients have been informed about removal), or
1300 - object has m_removed=true, or
1301 - object is too far away
1303 for(core::map<u16, bool>::Iterator
1304 i = current_objects.getIterator();
1305 i.atEnd()==false; i++)
1307 u16 id = i.getNode()->getKey();
1308 ServerActiveObject *object = getActiveObject(id);
1311 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1312 <<" object in current_objects is NULL"<<std::endl;
1314 else if(object->m_removed == false)
1316 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1317 /*infostream<<"removed == false"
1318 <<"distance_f = "<<distance_f
1319 <<", radius_f = "<<radius_f<<std::endl;*/
1320 if(distance_f < radius_f)
1326 removed_objects.insert(id, false);
1330 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1332 if(m_active_object_messages.size() == 0)
1333 return ActiveObjectMessage(0);
1335 return m_active_object_messages.pop_front();
1339 ************ Private methods *************
1342 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1346 if(object->getId() == 0)
1348 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1351 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1352 <<"no free ids available"<<std::endl;
1356 object->setId(new_id);
1358 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1360 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1361 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1365 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1366 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1368 m_active_objects.insert(object->getId(), object);
1370 // Add static object to active static list of the block
1371 v3f objectpos = object->getBasePosition();
1372 std::string staticdata = object->getStaticData();
1373 StaticObject s_obj(object->getType(), objectpos, staticdata);
1374 // Add to the block where the object is located in
1375 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1376 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1379 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1380 object->m_static_exists = true;
1381 object->m_static_block = blockpos;
1384 block->setChangedFlag();
1387 infostream<<"ServerEnv: Could not find a block for "
1388 <<"storing newly added static active object"<<std::endl;
1391 return object->getId();
1395 Remove objects that satisfy (m_removed && m_known_by_count==0)
1397 void ServerEnvironment::removeRemovedObjects()
1399 core::list<u16> objects_to_remove;
1400 for(core::map<u16, ServerActiveObject*>::Iterator
1401 i = m_active_objects.getIterator();
1402 i.atEnd()==false; i++)
1404 u16 id = i.getNode()->getKey();
1405 ServerActiveObject* obj = i.getNode()->getValue();
1406 // This shouldn't happen but check it
1409 infostream<<"NULL object found in ServerEnvironment"
1410 <<" while finding removed objects. id="<<id<<std::endl;
1411 // Id to be removed from m_active_objects
1412 objects_to_remove.push_back(id);
1417 We will delete objects that are marked as removed or thatare
1418 waiting for deletion after deactivation
1420 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1424 Delete static data from block if is marked as removed
1426 if(obj->m_static_exists && obj->m_removed)
1428 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1431 block->m_static_objects.remove(id);
1432 block->setChangedFlag();
1436 // If m_known_by_count > 0, don't actually remove.
1437 if(obj->m_known_by_count > 0)
1442 // Id to be removed from m_active_objects
1443 objects_to_remove.push_back(id);
1445 // Remove references from m_active_objects
1446 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1447 i != objects_to_remove.end(); i++)
1449 m_active_objects.remove(*i);
1454 Convert stored objects from blocks near the players to active.
1456 void ServerEnvironment::activateObjects(MapBlock *block)
1460 // Ignore if no stored objects (to not set changed flag)
1461 if(block->m_static_objects.m_stored.size() == 0)
1463 // A list for objects that couldn't be converted to static for some
1464 // reason. They will be stored back.
1465 core::list<StaticObject> new_stored;
1466 // Loop through stored static objects
1467 for(core::list<StaticObject>::Iterator
1468 i = block->m_static_objects.m_stored.begin();
1469 i != block->m_static_objects.m_stored.end(); i++)
1471 /*infostream<<"Server: Creating an active object from "
1472 <<"static data"<<std::endl;*/
1473 StaticObject &s_obj = *i;
1474 // Create an active object from the data
1475 ServerActiveObject *obj = ServerActiveObject::create
1476 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1477 // If couldn't create object, store static data back.
1480 new_stored.push_back(s_obj);
1483 // This will also add the object to the active static list
1484 addActiveObjectRaw(obj, false);
1485 //u16 id = addActiveObjectRaw(obj, false);
1487 // Clear stored list
1488 block->m_static_objects.m_stored.clear();
1489 // Add leftover failed stuff to stored list
1490 for(core::list<StaticObject>::Iterator
1491 i = new_stored.begin();
1492 i != new_stored.end(); i++)
1494 StaticObject &s_obj = *i;
1495 block->m_static_objects.m_stored.push_back(s_obj);
1497 // Block has been modified
1498 // NOTE: No it has not really. Save I/O here.
1499 //block->setChangedFlag();
1503 Convert objects that are not in active blocks to static.
1505 If m_known_by_count != 0, active object is not deleted, but static
1506 data is still updated.
1508 If force_delete is set, active object is deleted nevertheless. It
1509 shall only be set so in the destructor of the environment.
1511 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1513 core::list<u16> objects_to_remove;
1514 for(core::map<u16, ServerActiveObject*>::Iterator
1515 i = m_active_objects.getIterator();
1516 i.atEnd()==false; i++)
1518 ServerActiveObject* obj = i.getNode()->getValue();
1520 // This shouldn't happen but check it
1523 infostream<<"NULL object found in ServerEnvironment"
1529 u16 id = i.getNode()->getKey();
1530 v3f objectpos = obj->getBasePosition();
1532 // The block in which the object resides in
1533 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1535 // If block is active, don't remove
1536 if(m_active_blocks.contains(blockpos_o))
1540 Update the static data
1543 // Delete old static object
1544 MapBlock *oldblock = NULL;
1545 if(obj->m_static_exists)
1547 MapBlock *block = m_map->getBlockNoCreateNoEx
1548 (obj->m_static_block);
1551 block->m_static_objects.remove(id);
1555 // Create new static object
1556 std::string staticdata = obj->getStaticData();
1557 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1558 // Add to the block where the object is located in
1559 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1560 // Get or generate the block
1561 MapBlock *block = m_map->emergeBlock(blockpos);
1563 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1566 // Block not found. Is the old block still ok?
1569 // Load from disk or generate
1571 block = m_map->emergeBlock(blockpos);
1576 block->m_static_objects.insert(0, s_obj);
1577 block->setChangedFlag();
1578 obj->m_static_exists = true;
1579 obj->m_static_block = block->getPos();
1582 infostream<<"ServerEnv: Could not find or generate "
1583 <<"a block for storing static object"<<std::endl;
1584 obj->m_static_exists = false;
1589 Delete active object if not known by some client,
1590 else set pending deactivation
1593 // If known by some client, don't delete.
1594 if(obj->m_known_by_count > 0 && force_delete == false)
1596 obj->m_pending_deactivation = true;
1600 /*infostream<<"Server: Stored static data. Deleting object."
1602 // Delete active object
1604 // Id to be removed from m_active_objects
1605 objects_to_remove.push_back(id);
1608 // Remove references from m_active_objects
1609 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1610 i != objects_to_remove.end(); i++)
1612 m_active_objects.remove(*i);
1623 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1631 ClientEnvironment::~ClientEnvironment()
1633 // delete active objects
1634 for(core::map<u16, ClientActiveObject*>::Iterator
1635 i = m_active_objects.getIterator();
1636 i.atEnd()==false; i++)
1638 delete i.getNode()->getValue();
1645 void ClientEnvironment::addPlayer(Player *player)
1647 DSTACK(__FUNCTION_NAME);
1649 It is a failure if player is local and there already is a local
1652 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1654 Environment::addPlayer(player);
1657 LocalPlayer * ClientEnvironment::getLocalPlayer()
1659 for(core::list<Player*>::Iterator i = m_players.begin();
1660 i != m_players.end(); i++)
1662 Player *player = *i;
1663 if(player->isLocal())
1664 return (LocalPlayer*)player;
1669 void ClientEnvironment::step(float dtime)
1671 DSTACK(__FUNCTION_NAME);
1673 // Get some settings
1674 bool free_move = g_settings->getBool("free_move");
1675 bool footprints = g_settings->getBool("footprints");
1678 LocalPlayer *lplayer = getLocalPlayer();
1680 // collision info queue
1681 core::list<CollisionInfo> player_collisions;
1684 Get the speed the player is going
1686 bool is_climbing = lplayer->is_climbing;
1688 f32 player_speed = 0.001; // just some small value
1689 player_speed = lplayer->getSpeed().getLength();
1692 Maximum position increment
1694 //f32 position_max_increment = 0.05*BS;
1695 f32 position_max_increment = 0.1*BS;
1697 // Maximum time increment (for collision detection etc)
1698 // time = distance / speed
1699 f32 dtime_max_increment = position_max_increment / player_speed;
1701 // Maximum time increment is 10ms or lower
1702 if(dtime_max_increment > 0.01)
1703 dtime_max_increment = 0.01;
1705 // Don't allow overly huge dtime
1709 f32 dtime_downcount = dtime;
1712 Stuff that has a maximum time increment
1721 if(dtime_downcount > dtime_max_increment)
1723 dtime_part = dtime_max_increment;
1724 dtime_downcount -= dtime_part;
1728 dtime_part = dtime_downcount;
1730 Setting this to 0 (no -=dtime_part) disables an infinite loop
1731 when dtime_part is so small that dtime_downcount -= dtime_part
1734 dtime_downcount = 0;
1742 v3f lplayerpos = lplayer->getPosition();
1745 if(free_move == false && is_climbing == false)
1748 v3f speed = lplayer->getSpeed();
1749 if(lplayer->swimming_up == false)
1750 speed.Y -= 9.81 * BS * dtime_part * 2;
1753 if(lplayer->in_water_stable || lplayer->in_water)
1755 f32 max_down = 2.0*BS;
1756 if(speed.Y < -max_down) speed.Y = -max_down;
1759 if(speed.getLength() > max)
1761 speed = speed / speed.getLength() * max;
1765 lplayer->setSpeed(speed);
1770 This also does collision detection.
1772 lplayer->move(dtime_part, *m_map, position_max_increment,
1773 &player_collisions);
1776 while(dtime_downcount > 0.001);
1778 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1780 for(core::list<CollisionInfo>::Iterator
1781 i = player_collisions.begin();
1782 i != player_collisions.end(); i++)
1784 CollisionInfo &info = *i;
1785 if(info.t == COLLISION_FALL)
1787 //f32 tolerance = BS*10; // 2 without damage
1788 f32 tolerance = BS*12; // 3 without damage
1790 if(info.speed > tolerance)
1792 f32 damage_f = (info.speed - tolerance)/BS*factor;
1793 u16 damage = (u16)(damage_f+0.5);
1794 if(lplayer->hp > damage)
1795 lplayer->hp -= damage;
1799 ClientEnvEvent event;
1800 event.type = CEE_PLAYER_DAMAGE;
1801 event.player_damage.amount = damage;
1802 m_client_event_queue.push_back(event);
1808 A quick draft of lava damage
1810 if(m_lava_hurt_interval.step(dtime, 1.0))
1812 v3f pf = lplayer->getPosition();
1814 // Feet, middle and head
1815 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1816 MapNode n1 = m_map->getNodeNoEx(p1);
1817 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1818 MapNode n2 = m_map->getNodeNoEx(p2);
1819 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1820 MapNode n3 = m_map->getNodeNoEx(p2);
1822 u32 damage_per_second = 0;
1823 damage_per_second = MYMAX(damage_per_second,
1824 content_features(n1).damage_per_second);
1825 damage_per_second = MYMAX(damage_per_second,
1826 content_features(n2).damage_per_second);
1827 damage_per_second = MYMAX(damage_per_second,
1828 content_features(n3).damage_per_second);
1830 if(damage_per_second != 0)
1832 ClientEnvEvent event;
1833 event.type = CEE_PLAYER_DAMAGE;
1834 event.player_damage.amount = damage_per_second;
1835 m_client_event_queue.push_back(event);
1840 Stuff that can be done in an arbitarily large dtime
1842 for(core::list<Player*>::Iterator i = m_players.begin();
1843 i != m_players.end(); i++)
1845 Player *player = *i;
1846 v3f playerpos = player->getPosition();
1849 Handle non-local players
1851 if(player->isLocal() == false)
1854 player->move(dtime, *m_map, 100*BS);
1858 // Update lighting on all players on client
1859 u8 light = LIGHT_MAX;
1862 v3s16 p = player->getLightPosition();
1863 MapNode n = m_map->getNode(p);
1864 light = n.getLightBlend(getDayNightRatio());
1866 catch(InvalidPositionException &e) {}
1867 player->updateLight(light);
1870 Add footsteps to grass
1874 // Get node that is at BS/4 under player
1875 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1877 MapNode n = m_map->getNode(bottompos);
1878 if(n.getContent() == CONTENT_GRASS)
1880 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1881 m_map->setNode(bottompos, n);
1882 // Update mesh on client
1883 if(m_map->mapType() == MAPTYPE_CLIENT)
1885 v3s16 p_blocks = getNodeBlockPos(bottompos);
1886 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1887 //b->updateMesh(getDayNightRatio());
1888 b->setMeshExpired(true);
1892 catch(InvalidPositionException &e)
1899 Step active objects and update lighting of them
1902 for(core::map<u16, ClientActiveObject*>::Iterator
1903 i = m_active_objects.getIterator();
1904 i.atEnd()==false; i++)
1906 ClientActiveObject* obj = i.getNode()->getValue();
1908 obj->step(dtime, this);
1910 if(m_active_object_light_update_interval.step(dtime, 0.21))
1913 //u8 light = LIGHT_MAX;
1917 v3s16 p = obj->getLightPosition();
1918 MapNode n = m_map->getNode(p);
1919 light = n.getLightBlend(getDayNightRatio());
1921 catch(InvalidPositionException &e) {}
1922 obj->updateLight(light);
1927 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1929 m_map->updateMeshes(blockpos, getDayNightRatio());
1932 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1934 m_map->expireMeshes(only_daynight_diffed);
1937 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1939 core::map<u16, ClientActiveObject*>::Node *n;
1940 n = m_active_objects.find(id);
1943 return n->getValue();
1946 bool isFreeClientActiveObjectId(u16 id,
1947 core::map<u16, ClientActiveObject*> &objects)
1952 for(core::map<u16, ClientActiveObject*>::Iterator
1953 i = objects.getIterator();
1954 i.atEnd()==false; i++)
1956 if(i.getNode()->getKey() == id)
1962 u16 getFreeClientActiveObjectId(
1963 core::map<u16, ClientActiveObject*> &objects)
1968 if(isFreeClientActiveObjectId(new_id, objects))
1978 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1981 if(object->getId() == 0)
1983 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1986 infostream<<"ClientEnvironment::addActiveObject(): "
1987 <<"no free ids available"<<std::endl;
1991 object->setId(new_id);
1993 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1995 infostream<<"ClientEnvironment::addActiveObject(): "
1996 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2000 infostream<<"ClientEnvironment::addActiveObject(): "
2001 <<"added (id="<<object->getId()<<")"<<std::endl;
2002 m_active_objects.insert(object->getId(), object);
2003 object->addToScene(m_smgr);
2004 return object->getId();
2007 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2008 const std::string &init_data)
2010 ClientActiveObject* obj = ClientActiveObject::create(type);
2013 infostream<<"ClientEnvironment::addActiveObject(): "
2014 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2021 obj->initialize(init_data);
2023 addActiveObject(obj);
2026 void ClientEnvironment::removeActiveObject(u16 id)
2028 infostream<<"ClientEnvironment::removeActiveObject(): "
2029 <<"id="<<id<<std::endl;
2030 ClientActiveObject* obj = getActiveObject(id);
2033 infostream<<"ClientEnvironment::removeActiveObject(): "
2034 <<"id="<<id<<" not found"<<std::endl;
2037 obj->removeFromScene();
2039 m_active_objects.remove(id);
2042 void ClientEnvironment::processActiveObjectMessage(u16 id,
2043 const std::string &data)
2045 ClientActiveObject* obj = getActiveObject(id);
2048 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2049 <<" got message for id="<<id<<", which doesn't exist."
2053 obj->processMessage(data);
2057 Callbacks for activeobjects
2060 void ClientEnvironment::damageLocalPlayer(u8 damage)
2062 LocalPlayer *lplayer = getLocalPlayer();
2065 if(lplayer->hp > damage)
2066 lplayer->hp -= damage;
2070 ClientEnvEvent event;
2071 event.type = CEE_PLAYER_DAMAGE;
2072 event.player_damage.amount = damage;
2073 m_client_event_queue.push_back(event);
2077 Client likes to call these
2080 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2081 core::array<DistanceSortedActiveObject> &dest)
2083 for(core::map<u16, ClientActiveObject*>::Iterator
2084 i = m_active_objects.getIterator();
2085 i.atEnd()==false; i++)
2087 ClientActiveObject* obj = i.getNode()->getValue();
2089 f32 d = (obj->getPosition() - origin).getLength();
2094 DistanceSortedActiveObject dso(obj, d);
2096 dest.push_back(dso);
2100 ClientEnvEvent ClientEnvironment::getClientEvent()
2102 if(m_client_event_queue.size() == 0)
2104 ClientEnvEvent event;
2105 event.type = CEE_NONE;
2108 return m_client_event_queue.pop_front();
2111 #endif // #ifndef SERVER