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){
1347 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1348 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1349 <<"created new id "<<new_id<<std::endl;
1352 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1353 <<"no free ids available"<<std::endl;
1357 object->setId(new_id);
1360 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1361 <<"supplied with id "<<object->getId()<<std::endl;
1363 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1365 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1366 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1370 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1371 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1373 m_active_objects.insert(object->getId(), object);
1375 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1376 <<"Added id="<<object->getId()<<"; there are now "
1377 <<m_active_objects.size()<<" active objects in the list."
1380 // Add static object to active static list of the block
1381 v3f objectpos = object->getBasePosition();
1382 std::string staticdata = object->getStaticData();
1383 StaticObject s_obj(object->getType(), objectpos, staticdata);
1384 // Add to the block where the object is located in
1385 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1386 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1389 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1390 <<"found block for storing id="<<object->getId()
1392 <<" (set_changed="<<(set_changed?"true":"false")<<")"
1395 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1396 object->m_static_exists = true;
1397 object->m_static_block = blockpos;
1400 block->setChangedFlag();
1403 errorstream<<"ServerEnv: Could not find a block for "
1404 <<"storing newly added active object statically"<<std::endl;
1407 return object->getId();
1411 Remove objects that satisfy (m_removed && m_known_by_count==0)
1413 void ServerEnvironment::removeRemovedObjects()
1415 core::list<u16> objects_to_remove;
1416 for(core::map<u16, ServerActiveObject*>::Iterator
1417 i = m_active_objects.getIterator();
1418 i.atEnd()==false; i++)
1420 u16 id = i.getNode()->getKey();
1421 ServerActiveObject* obj = i.getNode()->getValue();
1422 // This shouldn't happen but check it
1425 infostream<<"NULL object found in ServerEnvironment"
1426 <<" while finding removed objects. id="<<id<<std::endl;
1427 // Id to be removed from m_active_objects
1428 objects_to_remove.push_back(id);
1433 We will delete objects that are marked as removed or thatare
1434 waiting for deletion after deactivation
1436 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1440 Delete static data from block if is marked as removed
1442 if(obj->m_static_exists && obj->m_removed)
1444 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1447 block->m_static_objects.remove(id);
1448 block->setChangedFlag();
1452 // If m_known_by_count > 0, don't actually remove.
1453 if(obj->m_known_by_count > 0)
1458 // Id to be removed from m_active_objects
1459 objects_to_remove.push_back(id);
1461 // Remove references from m_active_objects
1462 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1463 i != objects_to_remove.end(); i++)
1465 m_active_objects.remove(*i);
1470 Convert stored objects from blocks near the players to active.
1472 void ServerEnvironment::activateObjects(MapBlock *block)
1476 // Ignore if no stored objects (to not set changed flag)
1477 if(block->m_static_objects.m_stored.size() == 0)
1479 verbosestream<<"ServerEnvironment::activateObjects(): "
1480 <<"activating objects of block "<<PP(block->getPos())
1481 <<" ("<<block->m_static_objects.m_stored.size()
1482 <<" objects)"<<std::endl;
1483 // A list for objects that couldn't be converted to static for some
1484 // reason. They will be stored back.
1485 core::list<StaticObject> new_stored;
1486 // Loop through stored static objects
1487 for(core::list<StaticObject>::Iterator
1488 i = block->m_static_objects.m_stored.begin();
1489 i != block->m_static_objects.m_stored.end(); i++)
1491 /*infostream<<"Server: Creating an active object from "
1492 <<"static data"<<std::endl;*/
1493 StaticObject &s_obj = *i;
1494 // Create an active object from the data
1495 ServerActiveObject *obj = ServerActiveObject::create
1496 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1497 // If couldn't create object, store static data back.
1500 new_stored.push_back(s_obj);
1503 // This will also add the object to the active static list
1504 addActiveObjectRaw(obj, false);
1505 //u16 id = addActiveObjectRaw(obj, false);
1507 // Clear stored list
1508 block->m_static_objects.m_stored.clear();
1509 // Add leftover failed stuff to stored list
1510 for(core::list<StaticObject>::Iterator
1511 i = new_stored.begin();
1512 i != new_stored.end(); i++)
1514 StaticObject &s_obj = *i;
1515 block->m_static_objects.m_stored.push_back(s_obj);
1517 // Block has been modified
1518 // NOTE: No it has not really. Save I/O here.
1519 //block->setChangedFlag();
1523 Convert objects that are not in active blocks to static.
1525 If m_known_by_count != 0, active object is not deleted, but static
1526 data is still updated.
1528 If force_delete is set, active object is deleted nevertheless. It
1529 shall only be set so in the destructor of the environment.
1531 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1533 core::list<u16> objects_to_remove;
1534 for(core::map<u16, ServerActiveObject*>::Iterator
1535 i = m_active_objects.getIterator();
1536 i.atEnd()==false; i++)
1538 ServerActiveObject* obj = i.getNode()->getValue();
1540 // This shouldn't happen but check it
1543 infostream<<"NULL object found in ServerEnvironment"
1549 u16 id = i.getNode()->getKey();
1550 v3f objectpos = obj->getBasePosition();
1552 // The block in which the object resides in
1553 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1555 // If block is active, don't remove
1556 if(m_active_blocks.contains(blockpos_o))
1559 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1560 <<"deactivating object id="<<id<<" on inactive block "
1561 <<PP(blockpos_o)<<std::endl;
1564 Update the static data
1567 // Delete old static object
1568 MapBlock *oldblock = NULL;
1569 if(obj->m_static_exists)
1571 MapBlock *block = m_map->getBlockNoCreateNoEx
1572 (obj->m_static_block);
1575 block->m_static_objects.remove(id);
1579 // Create new static object
1580 std::string staticdata = obj->getStaticData();
1581 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1582 // Add to the block where the object is located in
1583 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1584 // Get or generate the block
1585 MapBlock *block = m_map->emergeBlock(blockpos);
1587 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1590 // Block not found. Is the old block still ok?
1593 // Load from disk or generate
1595 block = m_map->emergeBlock(blockpos);
1600 block->m_static_objects.insert(0, s_obj);
1601 block->setChangedFlag();
1602 obj->m_static_exists = true;
1603 obj->m_static_block = block->getPos();
1606 errorstream<<"ServerEnv: Could not find or generate "
1607 <<"a block for storing static object"<<std::endl;
1608 obj->m_static_exists = false;
1613 Delete active object if not known by some client,
1614 else set pending deactivation
1617 // If known by some client, don't delete.
1618 if(obj->m_known_by_count > 0 && force_delete == false)
1620 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1621 <<"object id="<<id<<" is known by clients"
1622 <<"; not deleting yet"<<std::endl;
1624 obj->m_pending_deactivation = true;
1628 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1629 <<"object id="<<id<<" is not known by clients"
1630 <<"; deleting"<<std::endl;
1631 // Delete active object
1633 // Id to be removed from m_active_objects
1634 objects_to_remove.push_back(id);
1637 // Remove references from m_active_objects
1638 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1639 i != objects_to_remove.end(); i++)
1641 m_active_objects.remove(*i);
1652 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1660 ClientEnvironment::~ClientEnvironment()
1662 // delete active objects
1663 for(core::map<u16, ClientActiveObject*>::Iterator
1664 i = m_active_objects.getIterator();
1665 i.atEnd()==false; i++)
1667 delete i.getNode()->getValue();
1674 void ClientEnvironment::addPlayer(Player *player)
1676 DSTACK(__FUNCTION_NAME);
1678 It is a failure if player is local and there already is a local
1681 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1683 Environment::addPlayer(player);
1686 LocalPlayer * ClientEnvironment::getLocalPlayer()
1688 for(core::list<Player*>::Iterator i = m_players.begin();
1689 i != m_players.end(); i++)
1691 Player *player = *i;
1692 if(player->isLocal())
1693 return (LocalPlayer*)player;
1698 void ClientEnvironment::step(float dtime)
1700 DSTACK(__FUNCTION_NAME);
1702 // Get some settings
1703 bool free_move = g_settings->getBool("free_move");
1704 bool footprints = g_settings->getBool("footprints");
1707 LocalPlayer *lplayer = getLocalPlayer();
1709 // collision info queue
1710 core::list<CollisionInfo> player_collisions;
1713 Get the speed the player is going
1715 bool is_climbing = lplayer->is_climbing;
1717 f32 player_speed = 0.001; // just some small value
1718 player_speed = lplayer->getSpeed().getLength();
1721 Maximum position increment
1723 //f32 position_max_increment = 0.05*BS;
1724 f32 position_max_increment = 0.1*BS;
1726 // Maximum time increment (for collision detection etc)
1727 // time = distance / speed
1728 f32 dtime_max_increment = position_max_increment / player_speed;
1730 // Maximum time increment is 10ms or lower
1731 if(dtime_max_increment > 0.01)
1732 dtime_max_increment = 0.01;
1734 // Don't allow overly huge dtime
1738 f32 dtime_downcount = dtime;
1741 Stuff that has a maximum time increment
1750 if(dtime_downcount > dtime_max_increment)
1752 dtime_part = dtime_max_increment;
1753 dtime_downcount -= dtime_part;
1757 dtime_part = dtime_downcount;
1759 Setting this to 0 (no -=dtime_part) disables an infinite loop
1760 when dtime_part is so small that dtime_downcount -= dtime_part
1763 dtime_downcount = 0;
1771 v3f lplayerpos = lplayer->getPosition();
1774 if(free_move == false && is_climbing == false)
1777 v3f speed = lplayer->getSpeed();
1778 if(lplayer->swimming_up == false)
1779 speed.Y -= 9.81 * BS * dtime_part * 2;
1782 if(lplayer->in_water_stable || lplayer->in_water)
1784 f32 max_down = 2.0*BS;
1785 if(speed.Y < -max_down) speed.Y = -max_down;
1788 if(speed.getLength() > max)
1790 speed = speed / speed.getLength() * max;
1794 lplayer->setSpeed(speed);
1799 This also does collision detection.
1801 lplayer->move(dtime_part, *m_map, position_max_increment,
1802 &player_collisions);
1805 while(dtime_downcount > 0.001);
1807 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1809 for(core::list<CollisionInfo>::Iterator
1810 i = player_collisions.begin();
1811 i != player_collisions.end(); i++)
1813 CollisionInfo &info = *i;
1814 if(info.t == COLLISION_FALL)
1816 //f32 tolerance = BS*10; // 2 without damage
1817 f32 tolerance = BS*12; // 3 without damage
1819 if(info.speed > tolerance)
1821 f32 damage_f = (info.speed - tolerance)/BS*factor;
1822 u16 damage = (u16)(damage_f+0.5);
1823 if(lplayer->hp > damage)
1824 lplayer->hp -= damage;
1828 ClientEnvEvent event;
1829 event.type = CEE_PLAYER_DAMAGE;
1830 event.player_damage.amount = damage;
1831 m_client_event_queue.push_back(event);
1837 A quick draft of lava damage
1839 if(m_lava_hurt_interval.step(dtime, 1.0))
1841 v3f pf = lplayer->getPosition();
1843 // Feet, middle and head
1844 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1845 MapNode n1 = m_map->getNodeNoEx(p1);
1846 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1847 MapNode n2 = m_map->getNodeNoEx(p2);
1848 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1849 MapNode n3 = m_map->getNodeNoEx(p2);
1851 u32 damage_per_second = 0;
1852 damage_per_second = MYMAX(damage_per_second,
1853 content_features(n1).damage_per_second);
1854 damage_per_second = MYMAX(damage_per_second,
1855 content_features(n2).damage_per_second);
1856 damage_per_second = MYMAX(damage_per_second,
1857 content_features(n3).damage_per_second);
1859 if(damage_per_second != 0)
1861 ClientEnvEvent event;
1862 event.type = CEE_PLAYER_DAMAGE;
1863 event.player_damage.amount = damage_per_second;
1864 m_client_event_queue.push_back(event);
1869 Stuff that can be done in an arbitarily large dtime
1871 for(core::list<Player*>::Iterator i = m_players.begin();
1872 i != m_players.end(); i++)
1874 Player *player = *i;
1875 v3f playerpos = player->getPosition();
1878 Handle non-local players
1880 if(player->isLocal() == false)
1883 player->move(dtime, *m_map, 100*BS);
1887 // Update lighting on all players on client
1888 u8 light = LIGHT_MAX;
1891 v3s16 p = player->getLightPosition();
1892 MapNode n = m_map->getNode(p);
1893 light = n.getLightBlend(getDayNightRatio());
1895 catch(InvalidPositionException &e) {}
1896 player->updateLight(light);
1899 Add footsteps to grass
1903 // Get node that is at BS/4 under player
1904 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1906 MapNode n = m_map->getNode(bottompos);
1907 if(n.getContent() == CONTENT_GRASS)
1909 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1910 m_map->setNode(bottompos, n);
1911 // Update mesh on client
1912 if(m_map->mapType() == MAPTYPE_CLIENT)
1914 v3s16 p_blocks = getNodeBlockPos(bottompos);
1915 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1916 //b->updateMesh(getDayNightRatio());
1917 b->setMeshExpired(true);
1921 catch(InvalidPositionException &e)
1928 Step active objects and update lighting of them
1931 for(core::map<u16, ClientActiveObject*>::Iterator
1932 i = m_active_objects.getIterator();
1933 i.atEnd()==false; i++)
1935 ClientActiveObject* obj = i.getNode()->getValue();
1937 obj->step(dtime, this);
1939 if(m_active_object_light_update_interval.step(dtime, 0.21))
1942 //u8 light = LIGHT_MAX;
1946 v3s16 p = obj->getLightPosition();
1947 MapNode n = m_map->getNode(p);
1948 light = n.getLightBlend(getDayNightRatio());
1950 catch(InvalidPositionException &e) {}
1951 obj->updateLight(light);
1956 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1958 m_map->updateMeshes(blockpos, getDayNightRatio());
1961 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1963 m_map->expireMeshes(only_daynight_diffed);
1966 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1968 core::map<u16, ClientActiveObject*>::Node *n;
1969 n = m_active_objects.find(id);
1972 return n->getValue();
1975 bool isFreeClientActiveObjectId(u16 id,
1976 core::map<u16, ClientActiveObject*> &objects)
1981 for(core::map<u16, ClientActiveObject*>::Iterator
1982 i = objects.getIterator();
1983 i.atEnd()==false; i++)
1985 if(i.getNode()->getKey() == id)
1991 u16 getFreeClientActiveObjectId(
1992 core::map<u16, ClientActiveObject*> &objects)
1997 if(isFreeClientActiveObjectId(new_id, objects))
2007 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2010 if(object->getId() == 0)
2012 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2015 infostream<<"ClientEnvironment::addActiveObject(): "
2016 <<"no free ids available"<<std::endl;
2020 object->setId(new_id);
2022 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2024 infostream<<"ClientEnvironment::addActiveObject(): "
2025 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2029 infostream<<"ClientEnvironment::addActiveObject(): "
2030 <<"added (id="<<object->getId()<<")"<<std::endl;
2031 m_active_objects.insert(object->getId(), object);
2032 object->addToScene(m_smgr);
2033 return object->getId();
2036 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2037 const std::string &init_data)
2039 ClientActiveObject* obj = ClientActiveObject::create(type);
2042 infostream<<"ClientEnvironment::addActiveObject(): "
2043 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2050 obj->initialize(init_data);
2052 addActiveObject(obj);
2055 void ClientEnvironment::removeActiveObject(u16 id)
2057 infostream<<"ClientEnvironment::removeActiveObject(): "
2058 <<"id="<<id<<std::endl;
2059 ClientActiveObject* obj = getActiveObject(id);
2062 infostream<<"ClientEnvironment::removeActiveObject(): "
2063 <<"id="<<id<<" not found"<<std::endl;
2066 obj->removeFromScene();
2068 m_active_objects.remove(id);
2071 void ClientEnvironment::processActiveObjectMessage(u16 id,
2072 const std::string &data)
2074 ClientActiveObject* obj = getActiveObject(id);
2077 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2078 <<" got message for id="<<id<<", which doesn't exist."
2082 obj->processMessage(data);
2086 Callbacks for activeobjects
2089 void ClientEnvironment::damageLocalPlayer(u8 damage)
2091 LocalPlayer *lplayer = getLocalPlayer();
2094 if(lplayer->hp > damage)
2095 lplayer->hp -= damage;
2099 ClientEnvEvent event;
2100 event.type = CEE_PLAYER_DAMAGE;
2101 event.player_damage.amount = damage;
2102 m_client_event_queue.push_back(event);
2106 Client likes to call these
2109 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2110 core::array<DistanceSortedActiveObject> &dest)
2112 for(core::map<u16, ClientActiveObject*>::Iterator
2113 i = m_active_objects.getIterator();
2114 i.atEnd()==false; i++)
2116 ClientActiveObject* obj = i.getNode()->getValue();
2118 f32 d = (obj->getPosition() - origin).getLength();
2123 DistanceSortedActiveObject dso(obj, d);
2125 dest.push_back(dso);
2129 ClientEnvEvent ClientEnvironment::getClientEvent()
2131 if(m_client_event_queue.size() == 0)
2133 ClientEnvEvent event;
2134 event.type = CEE_NONE;
2137 return m_client_event_queue.pop_front();
2140 #endif // #ifndef SERVER