3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
32 #include "scriptapi.h"
34 #include "nodemetadata.h"
35 #include "main.h" // For g_settings, g_profiler
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
40 Environment::Environment():
45 Environment::~Environment()
48 for(core::list<Player*>::Iterator i = m_players.begin();
49 i != m_players.end(); i++)
55 void Environment::addPlayer(Player *player)
57 DSTACK(__FUNCTION_NAME);
59 Check that peer_ids are unique.
60 Also check that names are unique.
61 Exception: there can be multiple players with peer_id=0
63 // If peer id is non-zero, it has to be unique.
64 if(player->peer_id != 0)
65 assert(getPlayer(player->peer_id) == NULL);
66 // Name has to be unique.
67 assert(getPlayer(player->getName()) == NULL);
69 m_players.push_back(player);
72 void Environment::removePlayer(u16 peer_id)
74 DSTACK(__FUNCTION_NAME);
76 for(core::list<Player*>::Iterator i = m_players.begin();
77 i != m_players.end(); i++)
80 if(player->peer_id != peer_id)
85 // See if there is an another one
86 // (shouldn't be, but just to be sure)
91 Player * Environment::getPlayer(u16 peer_id)
93 for(core::list<Player*>::Iterator i = m_players.begin();
94 i != m_players.end(); i++)
97 if(player->peer_id == peer_id)
103 Player * Environment::getPlayer(const char *name)
105 for(core::list<Player*>::Iterator i = m_players.begin();
106 i != m_players.end(); i++)
109 if(strcmp(player->getName(), name) == 0)
115 Player * Environment::getRandomConnectedPlayer()
117 core::list<Player*> connected_players = getPlayers(true);
118 u32 chosen_one = myrand() % connected_players.size();
120 for(core::list<Player*>::Iterator
121 i = connected_players.begin();
122 i != connected_players.end(); i++)
134 Player * Environment::getNearestConnectedPlayer(v3f pos)
136 core::list<Player*> connected_players = getPlayers(true);
138 Player *nearest_player = NULL;
139 for(core::list<Player*>::Iterator
140 i = connected_players.begin();
141 i != connected_players.end(); i++)
144 f32 d = player->getPosition().getDistanceFrom(pos);
145 if(d < nearest_d || nearest_player == NULL)
148 nearest_player = player;
151 return nearest_player;
154 core::list<Player*> Environment::getPlayers()
159 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
161 core::list<Player*> newlist;
162 for(core::list<Player*>::Iterator
163 i = m_players.begin();
164 i != m_players.end(); i++)
168 if(ignore_disconnected)
170 // Ignore disconnected players
171 if(player->peer_id == 0)
175 newlist.push_back(player);
180 void Environment::printPlayers(std::ostream &o)
182 o<<"Players in environment:"<<std::endl;
183 for(core::list<Player*>::Iterator i = m_players.begin();
184 i != m_players.end(); i++)
187 o<<"Player peer_id="<<player->peer_id<<std::endl;
191 /*void Environment::setDayNightRatio(u32 r)
193 getDayNightRatio() = r;
196 u32 Environment::getDayNightRatio()
198 //return getDayNightRatio();
199 return time_to_daynight_ratio(m_time_of_day);
206 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
209 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
210 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
211 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
218 void ActiveBlockList::update(core::list<v3s16> &active_positions,
220 core::map<v3s16, bool> &blocks_removed,
221 core::map<v3s16, bool> &blocks_added)
226 core::map<v3s16, bool> newlist;
227 for(core::list<v3s16>::Iterator i = active_positions.begin();
228 i != active_positions.end(); i++)
230 fillRadiusBlock(*i, radius, newlist);
234 Find out which blocks on the old list are not on the new list
236 // Go through old list
237 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
238 i.atEnd()==false; i++)
240 v3s16 p = i.getNode()->getKey();
241 // If not on new list, it's been removed
242 if(newlist.find(p) == NULL)
243 blocks_removed.insert(p, true);
247 Find out which blocks on the new list are not on the old list
249 // Go through new list
250 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251 i.atEnd()==false; i++)
253 v3s16 p = i.getNode()->getKey();
254 // If not on old list, it's been added
255 if(m_list.find(p) == NULL)
256 blocks_added.insert(p, true);
263 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
264 i.atEnd()==false; i++)
266 v3s16 p = i.getNode()->getKey();
267 m_list.insert(p, true);
275 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
280 m_random_spawn_timer(3),
281 m_send_recommended_timer(0),
283 m_game_time_fraction_counter(0)
287 ServerEnvironment::~ServerEnvironment()
289 // Clear active block list.
290 // This makes the next one delete all active objects.
291 m_active_blocks.clear();
293 // Convert all objects to static and delete the active objects
294 deactivateFarObjects(true);
300 void ServerEnvironment::serializePlayers(const std::string &savedir)
302 std::string players_path = savedir + "/players";
303 fs::CreateDir(players_path);
305 core::map<Player*, bool> saved_players;
307 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
308 for(u32 i=0; i<player_files.size(); i++)
310 if(player_files[i].dir)
313 // Full path to this file
314 std::string path = players_path + "/" + player_files[i].name;
316 //infostream<<"Checking player file "<<path<<std::endl;
318 // Load player to see what is its name
319 ServerRemotePlayer testplayer(this);
321 // Open file and deserialize
322 std::ifstream is(path.c_str(), std::ios_base::binary);
323 if(is.good() == false)
325 infostream<<"Failed to read "<<path<<std::endl;
328 testplayer.deSerialize(is);
331 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
333 // Search for the player
334 std::string playername = testplayer.getName();
335 Player *player = getPlayer(playername.c_str());
338 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
342 //infostream<<"Found matching player, overwriting."<<std::endl;
344 // OK, found. Save player there.
346 // Open file and serialize
347 std::ofstream os(path.c_str(), std::ios_base::binary);
348 if(os.good() == false)
350 infostream<<"Failed to overwrite "<<path<<std::endl;
353 player->serialize(os);
354 saved_players.insert(player, true);
358 for(core::list<Player*>::Iterator i = m_players.begin();
359 i != m_players.end(); i++)
362 if(saved_players.find(player) != NULL)
364 /*infostream<<"Player "<<player->getName()
365 <<" was already saved."<<std::endl;*/
368 std::string playername = player->getName();
369 // Don't save unnamed player
372 //infostream<<"Not saving unnamed player."<<std::endl;
378 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
379 playername = "player";
380 std::string path = players_path + "/" + playername;
382 for(u32 i=0; i<1000; i++)
384 if(fs::PathExists(path) == false)
389 path = players_path + "/" + playername + itos(i);
393 infostream<<"Didn't find free file for player"<<std::endl;
398 /*infostream<<"Saving player "<<player->getName()<<" to "
400 // Open file and serialize
401 std::ofstream os(path.c_str(), std::ios_base::binary);
402 if(os.good() == false)
404 infostream<<"Failed to overwrite "<<path<<std::endl;
407 player->serialize(os);
408 saved_players.insert(player, true);
412 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
415 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
417 std::string players_path = savedir + "/players";
419 core::map<Player*, bool> saved_players;
421 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
422 for(u32 i=0; i<player_files.size(); i++)
424 if(player_files[i].dir)
427 // Full path to this file
428 std::string path = players_path + "/" + player_files[i].name;
430 infostream<<"Checking player file "<<path<<std::endl;
432 // Load player to see what is its name
433 ServerRemotePlayer testplayer(this);
435 // Open file and deserialize
436 std::ifstream is(path.c_str(), std::ios_base::binary);
437 if(is.good() == false)
439 infostream<<"Failed to read "<<path<<std::endl;
442 testplayer.deSerialize(is);
445 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
447 infostream<<"Not loading player with invalid name: "
448 <<testplayer.getName()<<std::endl;
451 infostream<<"Loaded test player with name "<<testplayer.getName()
454 // Search for the player
455 std::string playername = testplayer.getName();
456 Player *player = getPlayer(playername.c_str());
457 bool newplayer = false;
460 infostream<<"Is a new player"<<std::endl;
461 player = new ServerRemotePlayer(this);
467 infostream<<"Reading player "<<testplayer.getName()<<" from "
469 // Open file and deserialize
470 std::ifstream is(path.c_str(), std::ios_base::binary);
471 if(is.good() == false)
473 infostream<<"Failed to read "<<path<<std::endl;
476 player->deSerialize(is);
484 void ServerEnvironment::saveMeta(const std::string &savedir)
486 std::string path = savedir + "/env_meta.txt";
488 // Open file and serialize
489 std::ofstream os(path.c_str(), std::ios_base::binary);
490 if(os.good() == false)
492 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
494 throw SerializationError("Couldn't save env meta");
498 args.setU64("game_time", m_game_time);
499 args.setU64("time_of_day", getTimeOfDay());
504 void ServerEnvironment::loadMeta(const std::string &savedir)
506 std::string path = savedir + "/env_meta.txt";
508 // Open file and deserialize
509 std::ifstream is(path.c_str(), std::ios_base::binary);
510 if(is.good() == false)
512 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
514 throw SerializationError("Couldn't load env meta");
522 throw SerializationError
523 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
525 std::getline(is, line);
526 std::string trimmedline = trim(line);
527 if(trimmedline == "EnvArgsEnd")
529 args.parseConfigLine(line);
533 m_game_time = args.getU64("game_time");
534 }catch(SettingNotFoundException &e){
535 // Getting this is crucial, otherwise timestamps are useless
536 throw SerializationError("Couldn't load env meta game_time");
540 m_time_of_day = args.getU64("time_of_day");
541 }catch(SettingNotFoundException &e){
542 // This is not as important
543 m_time_of_day = 9000;
548 // This is probably very useless
549 void spawnRandomObjects(MapBlock *block)
551 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
552 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
554 bool last_node_walkable = false;
555 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
558 MapNode n = block->getNodeNoEx(p);
559 if(n.getContent() == CONTENT_IGNORE)
561 if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
563 if(m_gamedef->ndef()->get(n).walkable)
565 last_node_walkable = true;
568 if(last_node_walkable)
570 // If block contains light information
571 if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
573 if(n.getLight(LIGHTBANK_DAY) <= 5)
575 if(myrand() % 1000 == 0)
577 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
579 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
580 std::string data = obj->getStaticData();
581 StaticObject s_obj(obj->getType(),
582 obj->getBasePosition(), data);
584 block->m_static_objects.insert(0, s_obj);
586 block->raiseModified(MOD_STATE_WRITE_NEEDED,
587 "spawnRandomObjects");
592 last_node_walkable = false;
598 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
600 // Get time difference
602 u32 stamp = block->getTimestamp();
603 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
604 dtime_s = m_game_time - block->getTimestamp();
605 dtime_s += additional_dtime;
607 // Set current time as timestamp (and let it set ChangedFlag)
608 block->setTimestamp(m_game_time);
610 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
612 // Activate stored objects
613 activateObjects(block);
616 bool changed = block->m_node_metadata->step((float)dtime_s);
620 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
621 event.p = block->getPos();
622 m_map->dispatchEvent(&event);
624 block->raiseModified(MOD_STATE_WRITE_NEEDED,
625 "node metadata modified in activateBlock");
628 // TODO: Do something
629 // TODO: Implement usage of ActiveBlockModifier
631 // Here's a quick demonstration
633 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
634 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
635 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
637 v3s16 p = p0 + block->getPosRelative();
638 MapNode n = block->getNodeNoEx(p0);
641 // Convert all mud under proper day lighting to grass
642 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
646 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
647 if(m_gamedef->ndef()->get(n_top).light_propagates &&
648 !m_gamedef->ndef()->get(n_top).isLiquid() &&
649 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
651 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
652 m_map->addNodeWithEvent(p, n);
660 void ServerEnvironment::clearAllObjects()
662 infostream<<"ServerEnvironment::clearAllObjects(): "
663 <<"Removing all active objects"<<std::endl;
664 core::list<u16> objects_to_remove;
665 for(core::map<u16, ServerActiveObject*>::Iterator
666 i = m_active_objects.getIterator();
667 i.atEnd()==false; i++)
669 ServerActiveObject* obj = i.getNode()->getValue();
670 u16 id = i.getNode()->getKey();
671 v3f objectpos = obj->getBasePosition();
672 // Delete static object if block is loaded
673 if(obj->m_static_exists){
674 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
676 block->m_static_objects.remove(id);
677 block->raiseModified(MOD_STATE_WRITE_NEEDED,
679 obj->m_static_exists = false;
682 // If known by some client, don't delete immediately
683 if(obj->m_known_by_count > 0){
684 obj->m_pending_deactivation = true;
685 obj->m_removed = true;
688 // Deregister in scripting api
689 scriptapi_rm_object_reference(m_lua, obj);
690 // Delete active object
692 // Id to be removed from m_active_objects
693 objects_to_remove.push_back(id);
695 // Remove references from m_active_objects
696 for(core::list<u16>::Iterator i = objects_to_remove.begin();
697 i != objects_to_remove.end(); i++)
699 m_active_objects.remove(*i);
702 core::list<v3s16> loadable_blocks;
703 infostream<<"ServerEnvironment::clearAllObjects(): "
704 <<"Listing all loadable blocks"<<std::endl;
705 m_map->listAllLoadableBlocks(loadable_blocks);
706 infostream<<"ServerEnvironment::clearAllObjects(): "
707 <<"Done listing all loadable blocks: "
708 <<loadable_blocks.size()
709 <<", now clearing"<<std::endl;
710 u32 report_interval = loadable_blocks.size() / 10;
711 u32 num_blocks_checked = 0;
712 u32 num_blocks_cleared = 0;
713 u32 num_objs_cleared = 0;
714 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
715 i != loadable_blocks.end(); i++)
718 MapBlock *block = m_map->emergeBlock(p, false);
720 errorstream<<"ServerEnvironment::clearAllObjects(): "
721 <<"Failed to emerge block "<<PP(p)<<std::endl;
724 u32 num_stored = block->m_static_objects.m_stored.size();
725 u32 num_active = block->m_static_objects.m_active.size();
726 if(num_stored != 0 || num_active != 0){
727 block->m_static_objects.m_stored.clear();
728 block->m_static_objects.m_active.clear();
729 block->raiseModified(MOD_STATE_WRITE_NEEDED,
731 num_objs_cleared += num_stored + num_active;
732 num_blocks_cleared++;
734 num_blocks_checked++;
736 if(num_blocks_checked % report_interval == 0){
737 float percent = 100.0 * (float)num_blocks_checked /
738 loadable_blocks.size();
739 infostream<<"ServerEnvironment::clearAllObjects(): "
740 <<"Cleared "<<num_objs_cleared<<" objects"
741 <<" in "<<num_blocks_cleared<<" blocks ("
742 <<percent<<"%)"<<std::endl;
745 infostream<<"ServerEnvironment::clearAllObjects(): "
746 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
747 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
750 static void getMob_dungeon_master(Settings &properties)
752 properties.set("looks", "dungeon_master");
753 properties.setFloat("yaw", 1.57);
754 properties.setFloat("hp", 30);
755 properties.setBool("bright_shooting", true);
756 properties.set("shoot_type", "fireball");
757 properties.set("shoot_y", "0.7");
758 properties.set("player_hit_damage", "1");
759 properties.set("player_hit_distance", "1.0");
760 properties.set("player_hit_interval", "0.5");
761 properties.setBool("mindless_rage", myrand_range(0,100)==0);
764 void ServerEnvironment::step(float dtime)
766 DSTACK(__FUNCTION_NAME);
768 //TimeTaker timer("ServerEnv step");
771 bool footprints = g_settings->getBool("footprints");
777 m_game_time_fraction_counter += dtime;
778 u32 inc_i = (u32)m_game_time_fraction_counter;
779 m_game_time += inc_i;
780 m_game_time_fraction_counter -= (float)inc_i;
787 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
788 for(core::list<Player*>::Iterator i = m_players.begin();
789 i != m_players.end(); i++)
793 // Ignore disconnected players
794 if(player->peer_id == 0)
797 v3f playerpos = player->getPosition();
800 player->move(dtime, *m_map, 100*BS);
803 Add footsteps to grass
807 // Get node that is at BS/4 under player
808 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
810 MapNode n = m_map->getNode(bottompos);
811 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
813 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
814 m_map->setNode(bottompos, n);
817 catch(InvalidPositionException &e)
825 Manage active block list
827 if(m_active_blocks_management_interval.step(dtime, 2.0))
829 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
831 Get player block positions
833 core::list<v3s16> players_blockpos;
834 for(core::list<Player*>::Iterator
835 i = m_players.begin();
836 i != m_players.end(); i++)
839 // Ignore disconnected players
840 if(player->peer_id == 0)
842 v3s16 blockpos = getNodeBlockPos(
843 floatToInt(player->getPosition(), BS));
844 players_blockpos.push_back(blockpos);
848 Update list of active blocks, collecting changes
850 const s16 active_block_range = g_settings->getS16("active_block_range");
851 core::map<v3s16, bool> blocks_removed;
852 core::map<v3s16, bool> blocks_added;
853 m_active_blocks.update(players_blockpos, active_block_range,
854 blocks_removed, blocks_added);
857 Handle removed blocks
860 // Convert active objects that are no more in active blocks to static
861 deactivateFarObjects(false);
863 for(core::map<v3s16, bool>::Iterator
864 i = blocks_removed.getIterator();
865 i.atEnd()==false; i++)
867 v3s16 p = i.getNode()->getKey();
869 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
870 <<") became inactive"<<std::endl;*/
872 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
876 // Set current time as timestamp (and let it set ChangedFlag)
877 block->setTimestamp(m_game_time);
884 for(core::map<v3s16, bool>::Iterator
885 i = blocks_added.getIterator();
886 i.atEnd()==false; i++)
888 v3s16 p = i.getNode()->getKey();
890 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
891 <<") became active"<<std::endl;*/
893 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
897 activateBlock(block);
902 Mess around in active blocks
904 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
906 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
910 for(core::map<v3s16, bool>::Iterator
911 i = m_active_blocks.m_list.getIterator();
912 i.atEnd()==false; i++)
914 v3s16 p = i.getNode()->getKey();
916 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
917 <<") being handled"<<std::endl;*/
919 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
923 // Reset block usage timer
924 block->resetUsageTimer();
926 // Set current time as timestamp
927 block->setTimestampNoChangedFlag(m_game_time);
930 bool changed = block->m_node_metadata->step(dtime);
934 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
936 m_map->dispatchEvent(&event);
938 block->raiseModified(MOD_STATE_WRITE_NEEDED,
939 "node metadata modified in step");
944 if(m_active_blocks_test_interval.step(dtime, 10.0))
946 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
947 //float dtime = 10.0;
948 TimeTaker timer("modify in active blocks");
950 INodeDefManager *ndef = m_gamedef->ndef();
952 // Pre-fetch content ids for the luge loop
953 content_t c_dirt = ndef->getId("dirt");
954 content_t c_grass = ndef->getId("dirt_with_grass");
955 content_t c_tree = ndef->getId("tree");
956 content_t c_jungletree = ndef->getId("jungletree");
957 content_t c_stone = ndef->getId("stone");
958 content_t c_mossycobble = ndef->getId("mossycobble");
959 content_t c_sapling = ndef->getId("sapling");
961 for(core::map<v3s16, bool>::Iterator
962 i = m_active_blocks.m_list.getIterator();
963 i.atEnd()==false; i++)
965 v3s16 p = i.getNode()->getKey();
967 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
968 <<") being handled"<<std::endl;*/
970 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
974 // Set current time as timestamp
975 block->setTimestampNoChangedFlag(m_game_time);
980 Note that map modifications should be done using the event-
981 making map methods so that the server gets information
984 Reading can be done quickly directly from the block.
986 Everything should bind to inside this single content
987 searching loop to keep things fast.
989 // TODO: Implement usage of ActiveBlockModifier
991 // Find out how many objects the block contains
992 //u32 active_object_count = block->m_static_objects.m_active.size();
993 // Find out how many objects this and all the neighbors contain
994 u32 active_object_count_wider = 0;
995 for(s16 x=-1; x<=1; x++)
996 for(s16 y=-1; y<=1; y++)
997 for(s16 z=-1; z<=1; z++)
999 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
1002 active_object_count_wider +=
1003 block->m_static_objects.m_active.size()
1004 + block->m_static_objects.m_stored.size();
1006 /*if(block->m_static_objects.m_stored.size() != 0){
1007 errorstream<<"ServerEnvironment::step(): "
1008 <<PP(block->getPos())<<" contains "
1009 <<block->m_static_objects.m_stored.size()
1010 <<" stored objects; "
1011 <<"when spawning objects, when counting active "
1012 <<"objects in wide area. relative position: "
1013 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1018 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1019 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1020 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1022 v3s16 p = p0 + block->getPosRelative();
1023 MapNode n = block->getNodeNoEx(p0);
1027 Convert dirt under proper lighting to grass
1029 if(n.getContent() == c_dirt)
1031 if(myrand()%20 == 0)
1033 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1034 if(m_gamedef->ndef()->get(n_top).light_propagates &&
1035 !m_gamedef->ndef()->get(n_top).isLiquid() &&
1036 n_top.getLightBlend(getDayNightRatio(),
1037 m_gamedef->ndef()) >= 13)
1039 n.setContent(c_grass);
1040 m_map->addNodeWithEvent(p, n);
1045 Convert grass into dirt if under something else than air
1047 if(n.getContent() == c_grass)
1049 //if(myrand()%20 == 0)
1051 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1052 if(!m_gamedef->ndef()->get(n_top).light_propagates ||
1053 m_gamedef->ndef()->get(n_top).isLiquid())
1055 n.setContent(c_dirt);
1056 m_map->addNodeWithEvent(p, n);
1061 Rats spawn around regular trees
1063 if(n.getContent() == c_tree ||
1064 n.getContent() == c_jungletree)
1066 if(myrand()%200 == 0 && active_object_count_wider == 0)
1068 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1069 0, myrand_range(-2, 2));
1070 MapNode n1 = m_map->getNodeNoEx(p1);
1071 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1072 if(n1b.getContent() == c_grass &&
1073 n1.getContent() == CONTENT_AIR)
1075 v3f pos = intToFloat(p1, BS);
1076 ServerActiveObject *obj = new RatSAO(this, pos);
1077 addActiveObject(obj);
1082 Fun things spawn in caves and dungeons
1084 if(n.getContent() == c_stone ||
1085 n.getContent() == c_mossycobble)
1087 if(myrand()%200 == 0 && active_object_count_wider == 0)
1089 v3s16 p1 = p + v3s16(0,1,0);
1090 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1091 if(n1a.getLightBlend(getDayNightRatio(),
1092 m_gamedef->ndef()) <= 3){
1093 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1094 if(n1a.getContent() == CONTENT_AIR &&
1095 n1b.getContent() == CONTENT_AIR)
1097 v3f pos = intToFloat(p1, BS);
1099 if(i == 0 || i == 1){
1100 actionstream<<"A dungeon master spawns at "
1101 <<PP(p1)<<std::endl;
1102 Settings properties;
1103 getMob_dungeon_master(properties);
1104 ServerActiveObject *obj = new MobV2SAO(
1105 this, pos, &properties);
1106 addActiveObject(obj);
1107 } else if(i == 2 || i == 3){
1108 actionstream<<"Rats spawn at "
1109 <<PP(p1)<<std::endl;
1110 for(int j=0; j<3; j++){
1111 ServerActiveObject *obj = new RatSAO(
1113 addActiveObject(obj);
1116 actionstream<<"An oerkki spawns at "
1117 <<PP(p1)<<std::endl;
1118 ServerActiveObject *obj = new Oerkki1SAO(
1120 addActiveObject(obj);
1127 Make trees from saplings!
1129 if(n.getContent() == c_sapling)
1131 if(myrand()%50 == 0)
1133 actionstream<<"A sapling grows into a tree at "
1136 core::map<v3s16, MapBlock*> modified_blocks;
1138 ManualMapVoxelManipulator vmanip(m_map);
1139 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1140 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1141 bool is_apple_tree = myrand()%4 == 0;
1142 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1144 vmanip.blitBackAll(&modified_blocks);
1147 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1148 for(core::map<v3s16, MapBlock*>::Iterator
1149 i = modified_blocks.getIterator();
1150 i.atEnd() == false; i++)
1152 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1154 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1156 // Send a MEET_OTHER event
1158 event.type = MEET_OTHER;
1159 for(core::map<v3s16, MapBlock*>::Iterator
1160 i = modified_blocks.getIterator();
1161 i.atEnd() == false; i++)
1163 v3s16 p = i.getNode()->getKey();
1164 event.modified_blocks.insert(p, true);
1166 m_map->dispatchEvent(&event);
1172 u32 time_ms = timer.stop(true);
1173 u32 max_time_ms = 200;
1174 if(time_ms > max_time_ms){
1175 infostream<<"WARNING: active block modifiers took "
1176 <<time_ms<<"ms (longer than "
1177 <<max_time_ms<<"ms)"<<std::endl;
1182 Step script environment (run global on_step())
1184 scriptapi_environment_step(m_lua, dtime);
1190 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1191 //TimeTaker timer("Step active objects");
1193 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1195 // This helps the objects to send data at the same time
1196 bool send_recommended = false;
1197 m_send_recommended_timer += dtime;
1198 if(m_send_recommended_timer > getSendRecommendedInterval())
1200 m_send_recommended_timer -= getSendRecommendedInterval();
1201 send_recommended = true;
1204 for(core::map<u16, ServerActiveObject*>::Iterator
1205 i = m_active_objects.getIterator();
1206 i.atEnd()==false; i++)
1208 ServerActiveObject* obj = i.getNode()->getValue();
1209 // Remove non-peaceful mobs on peaceful mode
1210 if(g_settings->getBool("only_peaceful_mobs")){
1211 if(!obj->isPeaceful())
1212 obj->m_removed = true;
1214 // Don't step if is to be removed or stored statically
1215 if(obj->m_removed || obj->m_pending_deactivation)
1218 obj->step(dtime, send_recommended);
1219 // Read messages from object
1220 while(obj->m_messages_out.size() > 0)
1222 m_active_object_messages.push_back(
1223 obj->m_messages_out.pop_front());
1229 Manage active objects
1231 if(m_object_management_interval.step(dtime, 0.5))
1233 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1235 Remove objects that satisfy (m_removed && m_known_by_count==0)
1237 removeRemovedObjects();
1240 if(g_settings->getBool("enable_experimental"))
1247 m_random_spawn_timer -= dtime;
1248 if(m_random_spawn_timer < 0)
1250 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1251 //m_random_spawn_timer += 2.0;
1252 m_random_spawn_timer += 200.0;
1258 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1259 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1260 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1262 Player *player = getRandomConnectedPlayer();
1265 pos = player->getPosition();
1267 myrand_range(-3,3)*BS,
1269 myrand_range(-3,3)*BS
1273 Create a ServerActiveObject
1276 //TestSAO *obj = new TestSAO(this, pos);
1277 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1278 //ServerActiveObject *obj = new RatSAO(this, pos);
1279 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1280 //ServerActiveObject *obj = new FireflySAO(this, pos);
1282 infostream<<"Server: Spawning MobV2SAO at "
1283 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1285 Settings properties;
1286 getMob_dungeon_master(properties);
1287 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1288 addActiveObject(obj);
1292 } // enable_experimental
1295 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1297 core::map<u16, ServerActiveObject*>::Node *n;
1298 n = m_active_objects.find(id);
1301 return n->getValue();
1304 bool isFreeServerActiveObjectId(u16 id,
1305 core::map<u16, ServerActiveObject*> &objects)
1310 for(core::map<u16, ServerActiveObject*>::Iterator
1311 i = objects.getIterator();
1312 i.atEnd()==false; i++)
1314 if(i.getNode()->getKey() == id)
1320 u16 getFreeServerActiveObjectId(
1321 core::map<u16, ServerActiveObject*> &objects)
1326 if(isFreeServerActiveObjectId(new_id, objects))
1336 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1339 u16 id = addActiveObjectRaw(object, true);
1343 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1347 v3f objectpos = obj->getBasePosition();
1349 // The block in which the object resides in
1350 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1353 Update the static data
1356 // Create new static object
1357 std::string staticdata = obj->getStaticData();
1358 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1359 // Add to the block where the object is located in
1360 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1361 // Get or generate the block
1362 MapBlock *block = m_map->emergeBlock(blockpos);
1364 bool succeeded = false;
1368 block->m_static_objects.insert(0, s_obj);
1369 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1370 "addActiveObjectAsStatic");
1374 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1375 <<"Could not find or generate "
1376 <<"a block for storing static object"<<std::endl;
1386 Finds out what new objects have been added to
1387 inside a radius around a position
1389 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1390 core::map<u16, bool> ¤t_objects,
1391 core::map<u16, bool> &added_objects)
1393 v3f pos_f = intToFloat(pos, BS);
1394 f32 radius_f = radius * BS;
1396 Go through the object list,
1397 - discard m_removed objects,
1398 - discard objects that are too far away,
1399 - discard objects that are found in current_objects.
1400 - add remaining objects to added_objects
1402 for(core::map<u16, ServerActiveObject*>::Iterator
1403 i = m_active_objects.getIterator();
1404 i.atEnd()==false; i++)
1406 u16 id = i.getNode()->getKey();
1408 ServerActiveObject *object = i.getNode()->getValue();
1411 // Discard if removed
1412 if(object->m_removed)
1414 // Discard if too far
1415 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1416 if(distance_f > radius_f)
1418 // Discard if already on current_objects
1419 core::map<u16, bool>::Node *n;
1420 n = current_objects.find(id);
1423 // Add to added_objects
1424 added_objects.insert(id, false);
1429 Finds out what objects have been removed from
1430 inside a radius around a position
1432 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1433 core::map<u16, bool> ¤t_objects,
1434 core::map<u16, bool> &removed_objects)
1436 v3f pos_f = intToFloat(pos, BS);
1437 f32 radius_f = radius * BS;
1439 Go through current_objects; object is removed if:
1440 - object is not found in m_active_objects (this is actually an
1441 error condition; objects should be set m_removed=true and removed
1442 only after all clients have been informed about removal), or
1443 - object has m_removed=true, or
1444 - object is too far away
1446 for(core::map<u16, bool>::Iterator
1447 i = current_objects.getIterator();
1448 i.atEnd()==false; i++)
1450 u16 id = i.getNode()->getKey();
1451 ServerActiveObject *object = getActiveObject(id);
1454 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1455 <<" object in current_objects is NULL"<<std::endl;
1457 else if(object->m_removed == false)
1459 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1460 /*infostream<<"removed == false"
1461 <<"distance_f = "<<distance_f
1462 <<", radius_f = "<<radius_f<<std::endl;*/
1463 if(distance_f < radius_f)
1469 removed_objects.insert(id, false);
1473 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1475 if(m_active_object_messages.size() == 0)
1476 return ActiveObjectMessage(0);
1478 return m_active_object_messages.pop_front();
1482 ************ Private methods *************
1485 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1489 if(object->getId() == 0){
1490 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1493 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1494 <<"no free ids available"<<std::endl;
1498 object->setId(new_id);
1501 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"supplied with id "<<object->getId()<<std::endl;
1504 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1506 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1511 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1512 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1514 m_active_objects.insert(object->getId(), object);
1516 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1517 <<"Added id="<<object->getId()<<"; there are now "
1518 <<m_active_objects.size()<<" active objects."
1521 // Add static object to active static list of the block
1522 v3f objectpos = object->getBasePosition();
1523 std::string staticdata = object->getStaticData();
1524 StaticObject s_obj(object->getType(), objectpos, staticdata);
1525 // Add to the block where the object is located in
1526 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1527 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1530 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1531 object->m_static_exists = true;
1532 object->m_static_block = blockpos;
1535 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1536 "addActiveObjectRaw");
1539 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1540 <<"could not find block for storing id="<<object->getId()
1541 <<" statically"<<std::endl;
1544 // Register reference in scripting api (must be done before post-init)
1545 scriptapi_add_object_reference(m_lua, object);
1546 // Post-initialize object
1547 object->addedToEnvironment();
1549 return object->getId();
1553 Remove objects that satisfy (m_removed && m_known_by_count==0)
1555 void ServerEnvironment::removeRemovedObjects()
1557 core::list<u16> objects_to_remove;
1558 for(core::map<u16, ServerActiveObject*>::Iterator
1559 i = m_active_objects.getIterator();
1560 i.atEnd()==false; i++)
1562 u16 id = i.getNode()->getKey();
1563 ServerActiveObject* obj = i.getNode()->getValue();
1564 // This shouldn't happen but check it
1567 infostream<<"NULL object found in ServerEnvironment"
1568 <<" while finding removed objects. id="<<id<<std::endl;
1569 // Id to be removed from m_active_objects
1570 objects_to_remove.push_back(id);
1575 We will delete objects that are marked as removed or thatare
1576 waiting for deletion after deactivation
1578 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1582 Delete static data from block if is marked as removed
1584 if(obj->m_static_exists && obj->m_removed)
1586 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1589 block->m_static_objects.remove(id);
1590 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1591 "removeRemovedObjects");
1592 obj->m_static_exists = false;
1596 // If m_known_by_count > 0, don't actually remove.
1597 if(obj->m_known_by_count > 0)
1600 // Deregister in scripting api
1601 scriptapi_rm_object_reference(m_lua, obj);
1605 // Id to be removed from m_active_objects
1606 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);
1616 static void print_hexdump(std::ostream &o, const std::string &data)
1618 const int linelength = 16;
1619 for(int l=0; ; l++){
1620 int i0 = linelength * l;
1621 bool at_end = false;
1622 int thislinelength = linelength;
1623 if(i0 + thislinelength > (int)data.size()){
1624 thislinelength = data.size() - i0;
1627 for(int di=0; di<linelength; di++){
1630 if(di<thislinelength)
1631 snprintf(buf, 4, "%.2x ", data[i]);
1633 snprintf(buf, 4, " ");
1637 for(int di=0; di<thislinelength; di++){
1651 Convert stored objects from blocks near the players to active.
1653 void ServerEnvironment::activateObjects(MapBlock *block)
1657 // Ignore if no stored objects (to not set changed flag)
1658 if(block->m_static_objects.m_stored.size() == 0)
1660 verbosestream<<"ServerEnvironment::activateObjects(): "
1661 <<"activating objects of block "<<PP(block->getPos())
1662 <<" ("<<block->m_static_objects.m_stored.size()
1663 <<" objects)"<<std::endl;
1664 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1666 errorstream<<"suspiciously large amount of objects detected: "
1667 <<block->m_static_objects.m_stored.size()<<" in "
1668 <<PP(block->getPos())
1669 <<"; removing all of them."<<std::endl;
1670 // Clear stored list
1671 block->m_static_objects.m_stored.clear();
1672 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1673 "stored list cleared in activateObjects due to "
1674 "large amount of objects");
1677 // A list for objects that couldn't be converted to static for some
1678 // reason. They will be stored back.
1679 core::list<StaticObject> new_stored;
1680 // Loop through stored static objects
1681 for(core::list<StaticObject>::Iterator
1682 i = block->m_static_objects.m_stored.begin();
1683 i != block->m_static_objects.m_stored.end(); i++)
1685 /*infostream<<"Server: Creating an active object from "
1686 <<"static data"<<std::endl;*/
1687 StaticObject &s_obj = *i;
1688 // Create an active object from the data
1689 ServerActiveObject *obj = ServerActiveObject::create
1690 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1691 // If couldn't create object, store static data back.
1694 errorstream<<"ServerEnvironment::activateObjects(): "
1695 <<"failed to create active object from static object "
1696 <<"in block "<<PP(s_obj.pos/BS)
1697 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1698 print_hexdump(verbosestream, s_obj.data);
1700 new_stored.push_back(s_obj);
1703 verbosestream<<"ServerEnvironment::activateObjects(): "
1704 <<"activated static object pos="<<PP(s_obj.pos/BS)
1705 <<" type="<<(int)s_obj.type<<std::endl;
1706 // This will also add the object to the active static list
1707 addActiveObjectRaw(obj, false);
1709 // Clear stored list
1710 block->m_static_objects.m_stored.clear();
1711 // Add leftover failed stuff to stored list
1712 for(core::list<StaticObject>::Iterator
1713 i = new_stored.begin();
1714 i != new_stored.end(); i++)
1716 StaticObject &s_obj = *i;
1717 block->m_static_objects.m_stored.push_back(s_obj);
1720 Note: Block hasn't really been modified here.
1721 The objects have just been activated and moved from the stored
1722 static list to the active static list.
1723 As such, the block is essentially the same.
1724 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1725 Otherwise there would be a huge amount of unnecessary I/O.
1730 Convert objects that are not standing inside active blocks to static.
1732 If m_known_by_count != 0, active object is not deleted, but static
1733 data is still updated.
1735 If force_delete is set, active object is deleted nevertheless. It
1736 shall only be set so in the destructor of the environment.
1738 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1740 core::list<u16> objects_to_remove;
1741 for(core::map<u16, ServerActiveObject*>::Iterator
1742 i = m_active_objects.getIterator();
1743 i.atEnd()==false; i++)
1745 ServerActiveObject* obj = i.getNode()->getValue();
1747 // This shouldn't happen but check it
1750 errorstream<<"NULL object found in ServerEnvironment"
1756 // If pending deactivation, let removeRemovedObjects() do it
1757 if(obj->m_pending_deactivation)
1760 u16 id = i.getNode()->getKey();
1761 v3f objectpos = obj->getBasePosition();
1763 // The block in which the object resides in
1764 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1766 // If block is active, don't remove
1767 if(m_active_blocks.contains(blockpos_o))
1770 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1771 <<"deactivating object id="<<id<<" on inactive block "
1772 <<PP(blockpos_o)<<std::endl;
1774 // If known by some client, don't immediately delete.
1775 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1778 Update the static data
1781 // Create new static object
1782 std::string staticdata_new = obj->getStaticData();
1783 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1785 bool stays_in_same_block = false;
1786 bool data_changed = true;
1788 if(obj->m_static_exists){
1789 if(obj->m_static_block == blockpos_o)
1790 stays_in_same_block = true;
1792 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1794 core::map<u16, StaticObject>::Node *n =
1795 block->m_static_objects.m_active.find(id);
1797 StaticObject static_old = n->getValue();
1799 float save_movem = obj->getMinimumSavedMovement();
1801 if(static_old.data == staticdata_new &&
1802 (static_old.pos - objectpos).getLength() < save_movem)
1803 data_changed = false;
1805 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1806 <<"id="<<id<<" m_static_exists=true but "
1807 <<"static data doesn't actually exist in "
1808 <<PP(obj->m_static_block)<<std::endl;
1812 bool shall_be_written = (!stays_in_same_block || data_changed);
1814 // Delete old static object
1815 if(obj->m_static_exists)
1817 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1820 block->m_static_objects.remove(id);
1821 obj->m_static_exists = false;
1822 // Only mark block as modified if data changed considerably
1823 if(shall_be_written)
1824 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1825 "deactivateFarObjects: Static data "
1826 "changed considerably");
1830 // Add to the block where the object is located in
1831 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1832 // Get or generate the block
1833 MapBlock *block = m_map->emergeBlock(blockpos);
1837 if(block->m_static_objects.m_stored.size() >= 49){
1838 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1839 <<" statically but block "<<PP(blockpos)
1840 <<" already contains "
1841 <<block->m_static_objects.m_stored.size()
1842 <<" (over 49) objects."
1843 <<" Forcing delete."<<std::endl;
1844 force_delete = true;
1846 u16 new_id = pending_delete ? id : 0;
1847 block->m_static_objects.insert(new_id, s_obj);
1849 // Only mark block as modified if data changed considerably
1850 if(shall_be_written)
1851 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1852 "deactivateFarObjects: Static data "
1853 "changed considerably");
1855 obj->m_static_exists = true;
1856 obj->m_static_block = block->getPos();
1860 errorstream<<"ServerEnv: Could not find or generate "
1861 <<"a block for storing id="<<obj->getId()
1862 <<" statically"<<std::endl;
1867 If known by some client, set pending deactivation.
1868 Otherwise delete it immediately.
1873 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1874 <<"object id="<<id<<" is known by clients"
1875 <<"; not deleting yet"<<std::endl;
1877 obj->m_pending_deactivation = true;
1881 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1882 <<"object id="<<id<<" is not known by clients"
1883 <<"; deleting"<<std::endl;
1885 // Deregister in scripting api
1886 scriptapi_rm_object_reference(m_lua, obj);
1888 // Delete active object
1890 // Id to be removed from m_active_objects
1891 objects_to_remove.push_back(id);
1894 // Remove references from m_active_objects
1895 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1896 i != objects_to_remove.end(); i++)
1898 m_active_objects.remove(*i);
1909 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1910 ITextureSource *texturesource, IGameDef *gamedef):
1913 m_texturesource(texturesource),
1920 ClientEnvironment::~ClientEnvironment()
1922 // delete active objects
1923 for(core::map<u16, ClientActiveObject*>::Iterator
1924 i = m_active_objects.getIterator();
1925 i.atEnd()==false; i++)
1927 delete i.getNode()->getValue();
1934 void ClientEnvironment::addPlayer(Player *player)
1936 DSTACK(__FUNCTION_NAME);
1938 It is a failure if player is local and there already is a local
1941 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1943 Environment::addPlayer(player);
1946 LocalPlayer * ClientEnvironment::getLocalPlayer()
1948 for(core::list<Player*>::Iterator i = m_players.begin();
1949 i != m_players.end(); i++)
1951 Player *player = *i;
1952 if(player->isLocal())
1953 return (LocalPlayer*)player;
1958 void ClientEnvironment::step(float dtime)
1960 DSTACK(__FUNCTION_NAME);
1962 // Get some settings
1963 bool free_move = g_settings->getBool("free_move");
1964 bool footprints = g_settings->getBool("footprints");
1967 LocalPlayer *lplayer = getLocalPlayer();
1969 // collision info queue
1970 core::list<CollisionInfo> player_collisions;
1973 Get the speed the player is going
1975 bool is_climbing = lplayer->is_climbing;
1977 f32 player_speed = lplayer->getSpeed().getLength();
1980 Maximum position increment
1982 //f32 position_max_increment = 0.05*BS;
1983 f32 position_max_increment = 0.1*BS;
1985 // Maximum time increment (for collision detection etc)
1986 // time = distance / speed
1987 f32 dtime_max_increment = 1;
1988 if(player_speed > 0.001)
1989 dtime_max_increment = position_max_increment / player_speed;
1991 // Maximum time increment is 10ms or lower
1992 if(dtime_max_increment > 0.01)
1993 dtime_max_increment = 0.01;
1995 // Don't allow overly huge dtime
1999 f32 dtime_downcount = dtime;
2002 Stuff that has a maximum time increment
2011 if(dtime_downcount > dtime_max_increment)
2013 dtime_part = dtime_max_increment;
2014 dtime_downcount -= dtime_part;
2018 dtime_part = dtime_downcount;
2020 Setting this to 0 (no -=dtime_part) disables an infinite loop
2021 when dtime_part is so small that dtime_downcount -= dtime_part
2024 dtime_downcount = 0;
2032 v3f lplayerpos = lplayer->getPosition();
2035 if(free_move == false && is_climbing == false)
2038 v3f speed = lplayer->getSpeed();
2039 if(lplayer->swimming_up == false)
2040 speed.Y -= 9.81 * BS * dtime_part * 2;
2043 if(lplayer->in_water_stable || lplayer->in_water)
2045 f32 max_down = 2.0*BS;
2046 if(speed.Y < -max_down) speed.Y = -max_down;
2049 if(speed.getLength() > max)
2051 speed = speed / speed.getLength() * max;
2055 lplayer->setSpeed(speed);
2060 This also does collision detection.
2062 lplayer->move(dtime_part, *m_map, position_max_increment,
2063 &player_collisions);
2066 while(dtime_downcount > 0.001);
2068 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2070 for(core::list<CollisionInfo>::Iterator
2071 i = player_collisions.begin();
2072 i != player_collisions.end(); i++)
2074 CollisionInfo &info = *i;
2075 if(info.t == COLLISION_FALL)
2077 //f32 tolerance = BS*10; // 2 without damage
2078 f32 tolerance = BS*12; // 3 without damage
2080 if(info.speed > tolerance)
2082 f32 damage_f = (info.speed - tolerance)/BS*factor;
2083 u16 damage = (u16)(damage_f+0.5);
2084 if(lplayer->hp > damage)
2085 lplayer->hp -= damage;
2089 ClientEnvEvent event;
2090 event.type = CEE_PLAYER_DAMAGE;
2091 event.player_damage.amount = damage;
2092 m_client_event_queue.push_back(event);
2098 A quick draft of lava damage
2100 if(m_lava_hurt_interval.step(dtime, 1.0))
2102 v3f pf = lplayer->getPosition();
2104 // Feet, middle and head
2105 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2106 MapNode n1 = m_map->getNodeNoEx(p1);
2107 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2108 MapNode n2 = m_map->getNodeNoEx(p2);
2109 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2110 MapNode n3 = m_map->getNodeNoEx(p2);
2112 u32 damage_per_second = 0;
2113 damage_per_second = MYMAX(damage_per_second,
2114 m_gamedef->ndef()->get(n1).damage_per_second);
2115 damage_per_second = MYMAX(damage_per_second,
2116 m_gamedef->ndef()->get(n2).damage_per_second);
2117 damage_per_second = MYMAX(damage_per_second,
2118 m_gamedef->ndef()->get(n3).damage_per_second);
2120 if(damage_per_second != 0)
2122 ClientEnvEvent event;
2123 event.type = CEE_PLAYER_DAMAGE;
2124 event.player_damage.amount = damage_per_second;
2125 m_client_event_queue.push_back(event);
2130 Stuff that can be done in an arbitarily large dtime
2132 for(core::list<Player*>::Iterator i = m_players.begin();
2133 i != m_players.end(); i++)
2135 Player *player = *i;
2136 v3f playerpos = player->getPosition();
2139 Handle non-local players
2141 if(player->isLocal() == false)
2144 player->move(dtime, *m_map, 100*BS);
2148 // Update lighting on all players on client
2149 u8 light = LIGHT_MAX;
2152 v3s16 p = player->getLightPosition();
2153 MapNode n = m_map->getNode(p);
2154 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2156 catch(InvalidPositionException &e) {}
2157 player->updateLight(light);
2160 Add footsteps to grass
2164 // Get node that is at BS/4 under player
2165 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2167 MapNode n = m_map->getNode(bottompos);
2168 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2170 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2171 m_map->setNode(bottompos, n);
2172 // Update mesh on client
2173 if(m_map->mapType() == MAPTYPE_CLIENT)
2175 v3s16 p_blocks = getNodeBlockPos(bottompos);
2176 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2177 //b->updateMesh(getDayNightRatio());
2178 b->setMeshExpired(true);
2182 catch(InvalidPositionException &e)
2189 Step active objects and update lighting of them
2192 for(core::map<u16, ClientActiveObject*>::Iterator
2193 i = m_active_objects.getIterator();
2194 i.atEnd()==false; i++)
2196 ClientActiveObject* obj = i.getNode()->getValue();
2198 obj->step(dtime, this);
2200 if(m_active_object_light_update_interval.step(dtime, 0.21))
2203 //u8 light = LIGHT_MAX;
2207 v3s16 p = obj->getLightPosition();
2208 MapNode n = m_map->getNode(p);
2209 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2211 catch(InvalidPositionException &e) {}
2212 obj->updateLight(light);
2217 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2219 m_map->updateMeshes(blockpos, getDayNightRatio());
2222 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2224 m_map->expireMeshes(only_daynight_diffed);
2227 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2229 core::map<u16, ClientActiveObject*>::Node *n;
2230 n = m_active_objects.find(id);
2233 return n->getValue();
2236 bool isFreeClientActiveObjectId(u16 id,
2237 core::map<u16, ClientActiveObject*> &objects)
2242 for(core::map<u16, ClientActiveObject*>::Iterator
2243 i = objects.getIterator();
2244 i.atEnd()==false; i++)
2246 if(i.getNode()->getKey() == id)
2252 u16 getFreeClientActiveObjectId(
2253 core::map<u16, ClientActiveObject*> &objects)
2258 if(isFreeClientActiveObjectId(new_id, objects))
2268 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2271 if(object->getId() == 0)
2273 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2276 infostream<<"ClientEnvironment::addActiveObject(): "
2277 <<"no free ids available"<<std::endl;
2281 object->setId(new_id);
2283 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2285 infostream<<"ClientEnvironment::addActiveObject(): "
2286 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2290 infostream<<"ClientEnvironment::addActiveObject(): "
2291 <<"added (id="<<object->getId()<<")"<<std::endl;
2292 m_active_objects.insert(object->getId(), object);
2293 object->addToScene(m_smgr, m_texturesource);
2294 { // Update lighting immediately
2298 v3s16 p = object->getLightPosition();
2299 MapNode n = m_map->getNode(p);
2300 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2302 catch(InvalidPositionException &e) {}
2303 object->updateLight(light);
2305 return object->getId();
2308 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2309 const std::string &init_data)
2311 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2314 infostream<<"ClientEnvironment::addActiveObject(): "
2315 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2322 obj->initialize(init_data);
2324 addActiveObject(obj);
2327 void ClientEnvironment::removeActiveObject(u16 id)
2329 infostream<<"ClientEnvironment::removeActiveObject(): "
2330 <<"id="<<id<<std::endl;
2331 ClientActiveObject* obj = getActiveObject(id);
2334 infostream<<"ClientEnvironment::removeActiveObject(): "
2335 <<"id="<<id<<" not found"<<std::endl;
2338 obj->removeFromScene();
2340 m_active_objects.remove(id);
2343 void ClientEnvironment::processActiveObjectMessage(u16 id,
2344 const std::string &data)
2346 ClientActiveObject* obj = getActiveObject(id);
2349 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2350 <<" got message for id="<<id<<", which doesn't exist."
2354 obj->processMessage(data);
2358 Callbacks for activeobjects
2361 void ClientEnvironment::damageLocalPlayer(u8 damage)
2363 LocalPlayer *lplayer = getLocalPlayer();
2366 if(lplayer->hp > damage)
2367 lplayer->hp -= damage;
2371 ClientEnvEvent event;
2372 event.type = CEE_PLAYER_DAMAGE;
2373 event.player_damage.amount = damage;
2374 m_client_event_queue.push_back(event);
2378 Client likes to call these
2381 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2382 core::array<DistanceSortedActiveObject> &dest)
2384 for(core::map<u16, ClientActiveObject*>::Iterator
2385 i = m_active_objects.getIterator();
2386 i.atEnd()==false; i++)
2388 ClientActiveObject* obj = i.getNode()->getValue();
2390 f32 d = (obj->getPosition() - origin).getLength();
2395 DistanceSortedActiveObject dso(obj, d);
2397 dest.push_back(dso);
2401 ClientEnvEvent ClientEnvironment::getClientEvent()
2403 if(m_client_event_queue.size() == 0)
2405 ClientEnvEvent event;
2406 event.type = CEE_NONE;
2409 return m_client_event_queue.pop_front();
2412 #endif // #ifndef SERVER