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;
949 for(core::map<v3s16, bool>::Iterator
950 i = m_active_blocks.m_list.getIterator();
951 i.atEnd()==false; i++)
953 v3s16 p = i.getNode()->getKey();
955 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
956 <<") being handled"<<std::endl;*/
958 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
962 // Set current time as timestamp
963 block->setTimestampNoChangedFlag(m_game_time);
968 Note that map modifications should be done using the event-
969 making map methods so that the server gets information
972 Reading can be done quickly directly from the block.
974 Everything should bind to inside this single content
975 searching loop to keep things fast.
977 // TODO: Implement usage of ActiveBlockModifier
979 // Find out how many objects the block contains
980 //u32 active_object_count = block->m_static_objects.m_active.size();
981 // Find out how many objects this and all the neighbors contain
982 u32 active_object_count_wider = 0;
983 for(s16 x=-1; x<=1; x++)
984 for(s16 y=-1; y<=1; y++)
985 for(s16 z=-1; z<=1; z++)
987 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
990 active_object_count_wider +=
991 block->m_static_objects.m_active.size()
992 + block->m_static_objects.m_stored.size();
994 /*if(block->m_static_objects.m_stored.size() != 0){
995 errorstream<<"ServerEnvironment::step(): "
996 <<PP(block->getPos())<<" contains "
997 <<block->m_static_objects.m_stored.size()
998 <<" stored objects; "
999 <<"when spawning objects, when counting active "
1000 <<"objects in wide area. relative position: "
1001 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1006 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1007 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1008 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1010 v3s16 p = p0 + block->getPosRelative();
1011 MapNode n = block->getNodeNoEx(p0);
1015 Convert mud under proper lighting to grass
1017 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
1019 if(myrand()%20 == 0)
1021 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1022 if(m_gamedef->ndef()->get(n_top).light_propagates &&
1023 !m_gamedef->ndef()->get(n_top).isLiquid() &&
1024 n_top.getLightBlend(getDayNightRatio(),
1025 m_gamedef->ndef()) >= 13)
1027 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
1028 m_map->addNodeWithEvent(p, n);
1033 Convert grass into mud if under something else than air
1035 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1037 //if(myrand()%20 == 0)
1039 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1040 if(m_gamedef->ndef()->get(n_top).light_propagates == false ||
1041 m_gamedef->ndef()->get(n_top).isLiquid())
1043 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_MUD"));
1044 m_map->addNodeWithEvent(p, n);
1049 Rats spawn around regular trees
1051 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_TREE") ||
1052 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_JUNGLETREE"))
1054 if(myrand()%200 == 0 && active_object_count_wider == 0)
1056 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1057 0, myrand_range(-2, 2));
1058 MapNode n1 = m_map->getNodeNoEx(p1);
1059 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1060 if(n1b.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS") &&
1061 n1.getContent() == CONTENT_AIR)
1063 v3f pos = intToFloat(p1, BS);
1064 ServerActiveObject *obj = new RatSAO(this, pos);
1065 addActiveObject(obj);
1070 Fun things spawn in caves and dungeons
1072 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_STONE") ||
1073 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MOSSYCOBBLE"))
1075 if(myrand()%200 == 0 && active_object_count_wider == 0)
1077 v3s16 p1 = p + v3s16(0,1,0);
1078 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1079 if(n1a.getLightBlend(getDayNightRatio(),
1080 m_gamedef->ndef()) <= 3){
1081 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1082 if(n1a.getContent() == CONTENT_AIR &&
1083 n1b.getContent() == CONTENT_AIR)
1085 v3f pos = intToFloat(p1, BS);
1087 if(i == 0 || i == 1){
1088 actionstream<<"A dungeon master spawns at "
1089 <<PP(p1)<<std::endl;
1090 Settings properties;
1091 getMob_dungeon_master(properties);
1092 ServerActiveObject *obj = new MobV2SAO(
1093 this, pos, &properties);
1094 addActiveObject(obj);
1095 } else if(i == 2 || i == 3){
1096 actionstream<<"Rats spawn at "
1097 <<PP(p1)<<std::endl;
1098 for(int j=0; j<3; j++){
1099 ServerActiveObject *obj = new RatSAO(
1101 addActiveObject(obj);
1104 actionstream<<"An oerkki spawns at "
1105 <<PP(p1)<<std::endl;
1106 ServerActiveObject *obj = new Oerkki1SAO(
1108 addActiveObject(obj);
1115 Make trees from saplings!
1117 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_SAPLING"))
1119 if(myrand()%50 == 0)
1121 actionstream<<"A sapling grows into a tree at "
1124 core::map<v3s16, MapBlock*> modified_blocks;
1126 ManualMapVoxelManipulator vmanip(m_map);
1127 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1128 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1129 bool is_apple_tree = myrand()%4 == 0;
1130 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1132 vmanip.blitBackAll(&modified_blocks);
1135 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1136 for(core::map<v3s16, MapBlock*>::Iterator
1137 i = modified_blocks.getIterator();
1138 i.atEnd() == false; i++)
1140 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1142 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1144 // Send a MEET_OTHER event
1146 event.type = MEET_OTHER;
1147 for(core::map<v3s16, MapBlock*>::Iterator
1148 i = modified_blocks.getIterator();
1149 i.atEnd() == false; i++)
1151 v3s16 p = i.getNode()->getKey();
1152 event.modified_blocks.insert(p, true);
1154 m_map->dispatchEvent(&event);
1162 Step script environment (run global on_step())
1164 scriptapi_environment_step(m_lua, dtime);
1170 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1171 //TimeTaker timer("Step active objects");
1173 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1175 // This helps the objects to send data at the same time
1176 bool send_recommended = false;
1177 m_send_recommended_timer += dtime;
1178 if(m_send_recommended_timer > getSendRecommendedInterval())
1180 m_send_recommended_timer -= getSendRecommendedInterval();
1181 send_recommended = true;
1184 for(core::map<u16, ServerActiveObject*>::Iterator
1185 i = m_active_objects.getIterator();
1186 i.atEnd()==false; i++)
1188 ServerActiveObject* obj = i.getNode()->getValue();
1189 // Remove non-peaceful mobs on peaceful mode
1190 if(g_settings->getBool("only_peaceful_mobs")){
1191 if(!obj->isPeaceful())
1192 obj->m_removed = true;
1194 // Don't step if is to be removed or stored statically
1195 if(obj->m_removed || obj->m_pending_deactivation)
1198 obj->step(dtime, send_recommended);
1199 // Read messages from object
1200 while(obj->m_messages_out.size() > 0)
1202 m_active_object_messages.push_back(
1203 obj->m_messages_out.pop_front());
1209 Manage active objects
1211 if(m_object_management_interval.step(dtime, 0.5))
1213 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1215 Remove objects that satisfy (m_removed && m_known_by_count==0)
1217 removeRemovedObjects();
1220 if(g_settings->getBool("enable_experimental"))
1227 m_random_spawn_timer -= dtime;
1228 if(m_random_spawn_timer < 0)
1230 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1231 //m_random_spawn_timer += 2.0;
1232 m_random_spawn_timer += 200.0;
1238 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1239 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1240 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1242 Player *player = getRandomConnectedPlayer();
1245 pos = player->getPosition();
1247 myrand_range(-3,3)*BS,
1249 myrand_range(-3,3)*BS
1253 Create a ServerActiveObject
1256 //TestSAO *obj = new TestSAO(this, pos);
1257 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1258 //ServerActiveObject *obj = new RatSAO(this, pos);
1259 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1260 //ServerActiveObject *obj = new FireflySAO(this, pos);
1262 infostream<<"Server: Spawning MobV2SAO at "
1263 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1265 Settings properties;
1266 getMob_dungeon_master(properties);
1267 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1268 addActiveObject(obj);
1272 } // enable_experimental
1275 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1277 core::map<u16, ServerActiveObject*>::Node *n;
1278 n = m_active_objects.find(id);
1281 return n->getValue();
1284 bool isFreeServerActiveObjectId(u16 id,
1285 core::map<u16, ServerActiveObject*> &objects)
1290 for(core::map<u16, ServerActiveObject*>::Iterator
1291 i = objects.getIterator();
1292 i.atEnd()==false; i++)
1294 if(i.getNode()->getKey() == id)
1300 u16 getFreeServerActiveObjectId(
1301 core::map<u16, ServerActiveObject*> &objects)
1306 if(isFreeServerActiveObjectId(new_id, objects))
1316 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1319 u16 id = addActiveObjectRaw(object, true);
1323 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1327 v3f objectpos = obj->getBasePosition();
1329 // The block in which the object resides in
1330 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1333 Update the static data
1336 // Create new static object
1337 std::string staticdata = obj->getStaticData();
1338 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1339 // Add to the block where the object is located in
1340 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1341 // Get or generate the block
1342 MapBlock *block = m_map->emergeBlock(blockpos);
1344 bool succeeded = false;
1348 block->m_static_objects.insert(0, s_obj);
1349 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1350 "addActiveObjectAsStatic");
1354 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1355 <<"Could not find or generate "
1356 <<"a block for storing static object"<<std::endl;
1366 Finds out what new objects have been added to
1367 inside a radius around a position
1369 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1370 core::map<u16, bool> ¤t_objects,
1371 core::map<u16, bool> &added_objects)
1373 v3f pos_f = intToFloat(pos, BS);
1374 f32 radius_f = radius * BS;
1376 Go through the object list,
1377 - discard m_removed objects,
1378 - discard objects that are too far away,
1379 - discard objects that are found in current_objects.
1380 - add remaining objects to added_objects
1382 for(core::map<u16, ServerActiveObject*>::Iterator
1383 i = m_active_objects.getIterator();
1384 i.atEnd()==false; i++)
1386 u16 id = i.getNode()->getKey();
1388 ServerActiveObject *object = i.getNode()->getValue();
1391 // Discard if removed
1392 if(object->m_removed)
1394 // Discard if too far
1395 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1396 if(distance_f > radius_f)
1398 // Discard if already on current_objects
1399 core::map<u16, bool>::Node *n;
1400 n = current_objects.find(id);
1403 // Add to added_objects
1404 added_objects.insert(id, false);
1409 Finds out what objects have been removed from
1410 inside a radius around a position
1412 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1413 core::map<u16, bool> ¤t_objects,
1414 core::map<u16, bool> &removed_objects)
1416 v3f pos_f = intToFloat(pos, BS);
1417 f32 radius_f = radius * BS;
1419 Go through current_objects; object is removed if:
1420 - object is not found in m_active_objects (this is actually an
1421 error condition; objects should be set m_removed=true and removed
1422 only after all clients have been informed about removal), or
1423 - object has m_removed=true, or
1424 - object is too far away
1426 for(core::map<u16, bool>::Iterator
1427 i = current_objects.getIterator();
1428 i.atEnd()==false; i++)
1430 u16 id = i.getNode()->getKey();
1431 ServerActiveObject *object = getActiveObject(id);
1434 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1435 <<" object in current_objects is NULL"<<std::endl;
1437 else if(object->m_removed == false)
1439 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1440 /*infostream<<"removed == false"
1441 <<"distance_f = "<<distance_f
1442 <<", radius_f = "<<radius_f<<std::endl;*/
1443 if(distance_f < radius_f)
1449 removed_objects.insert(id, false);
1453 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1455 if(m_active_object_messages.size() == 0)
1456 return ActiveObjectMessage(0);
1458 return m_active_object_messages.pop_front();
1462 ************ Private methods *************
1465 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1469 if(object->getId() == 0){
1470 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1473 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"no free ids available"<<std::endl;
1478 object->setId(new_id);
1481 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 <<"supplied with id "<<object->getId()<<std::endl;
1484 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1486 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1487 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1491 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1492 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1494 m_active_objects.insert(object->getId(), object);
1496 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1497 <<"Added id="<<object->getId()<<"; there are now "
1498 <<m_active_objects.size()<<" active objects."
1501 // Add static object to active static list of the block
1502 v3f objectpos = object->getBasePosition();
1503 std::string staticdata = object->getStaticData();
1504 StaticObject s_obj(object->getType(), objectpos, staticdata);
1505 // Add to the block where the object is located in
1506 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1507 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1510 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1511 object->m_static_exists = true;
1512 object->m_static_block = blockpos;
1515 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1516 "addActiveObjectRaw");
1519 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1520 <<"could not find block for storing id="<<object->getId()
1521 <<" statically"<<std::endl;
1524 // Register reference in scripting api (must be done before post-init)
1525 scriptapi_add_object_reference(m_lua, object);
1526 // Post-initialize object
1527 object->addedToEnvironment();
1529 return object->getId();
1533 Remove objects that satisfy (m_removed && m_known_by_count==0)
1535 void ServerEnvironment::removeRemovedObjects()
1537 core::list<u16> objects_to_remove;
1538 for(core::map<u16, ServerActiveObject*>::Iterator
1539 i = m_active_objects.getIterator();
1540 i.atEnd()==false; i++)
1542 u16 id = i.getNode()->getKey();
1543 ServerActiveObject* obj = i.getNode()->getValue();
1544 // This shouldn't happen but check it
1547 infostream<<"NULL object found in ServerEnvironment"
1548 <<" while finding removed objects. id="<<id<<std::endl;
1549 // Id to be removed from m_active_objects
1550 objects_to_remove.push_back(id);
1555 We will delete objects that are marked as removed or thatare
1556 waiting for deletion after deactivation
1558 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1562 Delete static data from block if is marked as removed
1564 if(obj->m_static_exists && obj->m_removed)
1566 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1569 block->m_static_objects.remove(id);
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 "removeRemovedObjects");
1572 obj->m_static_exists = false;
1576 // If m_known_by_count > 0, don't actually remove.
1577 if(obj->m_known_by_count > 0)
1580 // Deregister in scripting api
1581 scriptapi_rm_object_reference(m_lua, obj);
1585 // Id to be removed from m_active_objects
1586 objects_to_remove.push_back(id);
1588 // Remove references from m_active_objects
1589 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1590 i != objects_to_remove.end(); i++)
1592 m_active_objects.remove(*i);
1596 static void print_hexdump(std::ostream &o, const std::string &data)
1598 const int linelength = 16;
1599 for(int l=0; ; l++){
1600 int i0 = linelength * l;
1601 bool at_end = false;
1602 int thislinelength = linelength;
1603 if(i0 + thislinelength > (int)data.size()){
1604 thislinelength = data.size() - i0;
1607 for(int di=0; di<linelength; di++){
1610 if(di<thislinelength)
1611 snprintf(buf, 4, "%.2x ", data[i]);
1613 snprintf(buf, 4, " ");
1617 for(int di=0; di<thislinelength; di++){
1631 Convert stored objects from blocks near the players to active.
1633 void ServerEnvironment::activateObjects(MapBlock *block)
1637 // Ignore if no stored objects (to not set changed flag)
1638 if(block->m_static_objects.m_stored.size() == 0)
1640 verbosestream<<"ServerEnvironment::activateObjects(): "
1641 <<"activating objects of block "<<PP(block->getPos())
1642 <<" ("<<block->m_static_objects.m_stored.size()
1643 <<" objects)"<<std::endl;
1644 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1646 errorstream<<"suspiciously large amount of objects detected: "
1647 <<block->m_static_objects.m_stored.size()<<" in "
1648 <<PP(block->getPos())
1649 <<"; removing all of them."<<std::endl;
1650 // Clear stored list
1651 block->m_static_objects.m_stored.clear();
1652 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1653 "stored list cleared in activateObjects due to "
1654 "large amount of objects");
1657 // A list for objects that couldn't be converted to static for some
1658 // reason. They will be stored back.
1659 core::list<StaticObject> new_stored;
1660 // Loop through stored static objects
1661 for(core::list<StaticObject>::Iterator
1662 i = block->m_static_objects.m_stored.begin();
1663 i != block->m_static_objects.m_stored.end(); i++)
1665 /*infostream<<"Server: Creating an active object from "
1666 <<"static data"<<std::endl;*/
1667 StaticObject &s_obj = *i;
1668 // Create an active object from the data
1669 ServerActiveObject *obj = ServerActiveObject::create
1670 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1671 // If couldn't create object, store static data back.
1674 errorstream<<"ServerEnvironment::activateObjects(): "
1675 <<"failed to create active object from static object "
1676 <<"in block "<<PP(s_obj.pos/BS)
1677 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1678 print_hexdump(verbosestream, s_obj.data);
1680 new_stored.push_back(s_obj);
1683 verbosestream<<"ServerEnvironment::activateObjects(): "
1684 <<"activated static object pos="<<PP(s_obj.pos/BS)
1685 <<" type="<<(int)s_obj.type<<std::endl;
1686 // This will also add the object to the active static list
1687 addActiveObjectRaw(obj, false);
1689 // Clear stored list
1690 block->m_static_objects.m_stored.clear();
1691 // Add leftover failed stuff to stored list
1692 for(core::list<StaticObject>::Iterator
1693 i = new_stored.begin();
1694 i != new_stored.end(); i++)
1696 StaticObject &s_obj = *i;
1697 block->m_static_objects.m_stored.push_back(s_obj);
1700 Note: Block hasn't really been modified here.
1701 The objects have just been activated and moved from the stored
1702 static list to the active static list.
1703 As such, the block is essentially the same.
1704 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1705 Otherwise there would be a huge amount of unnecessary I/O.
1710 Convert objects that are not standing inside active blocks to static.
1712 If m_known_by_count != 0, active object is not deleted, but static
1713 data is still updated.
1715 If force_delete is set, active object is deleted nevertheless. It
1716 shall only be set so in the destructor of the environment.
1718 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1720 core::list<u16> objects_to_remove;
1721 for(core::map<u16, ServerActiveObject*>::Iterator
1722 i = m_active_objects.getIterator();
1723 i.atEnd()==false; i++)
1725 ServerActiveObject* obj = i.getNode()->getValue();
1727 // This shouldn't happen but check it
1730 errorstream<<"NULL object found in ServerEnvironment"
1736 // If pending deactivation, let removeRemovedObjects() do it
1737 if(obj->m_pending_deactivation)
1740 u16 id = i.getNode()->getKey();
1741 v3f objectpos = obj->getBasePosition();
1743 // The block in which the object resides in
1744 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1746 // If block is active, don't remove
1747 if(m_active_blocks.contains(blockpos_o))
1750 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1751 <<"deactivating object id="<<id<<" on inactive block "
1752 <<PP(blockpos_o)<<std::endl;
1754 // If known by some client, don't immediately delete.
1755 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1758 Update the static data
1761 // Create new static object
1762 std::string staticdata_new = obj->getStaticData();
1763 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1765 bool stays_in_same_block = false;
1766 bool data_changed = true;
1768 if(obj->m_static_exists){
1769 if(obj->m_static_block == blockpos_o)
1770 stays_in_same_block = true;
1772 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1774 core::map<u16, StaticObject>::Node *n =
1775 block->m_static_objects.m_active.find(id);
1777 StaticObject static_old = n->getValue();
1779 float save_movem = obj->getMinimumSavedMovement();
1781 if(static_old.data == staticdata_new &&
1782 (static_old.pos - objectpos).getLength() < save_movem)
1783 data_changed = false;
1785 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1786 <<"id="<<id<<" m_static_exists=true but "
1787 <<"static data doesn't actually exist in "
1788 <<PP(obj->m_static_block)<<std::endl;
1792 bool shall_be_written = (!stays_in_same_block || data_changed);
1794 // Delete old static object
1795 if(obj->m_static_exists)
1797 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1800 block->m_static_objects.remove(id);
1801 obj->m_static_exists = false;
1802 // Only mark block as modified if data changed considerably
1803 if(shall_be_written)
1804 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1805 "deactivateFarObjects: Static data "
1806 "changed considerably");
1810 // Add to the block where the object is located in
1811 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1812 // Get or generate the block
1813 MapBlock *block = m_map->emergeBlock(blockpos);
1817 if(block->m_static_objects.m_stored.size() >= 49){
1818 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1819 <<" statically but block "<<PP(blockpos)
1820 <<" already contains "
1821 <<block->m_static_objects.m_stored.size()
1822 <<" (over 49) objects."
1823 <<" Forcing delete."<<std::endl;
1824 force_delete = true;
1826 u16 new_id = pending_delete ? id : 0;
1827 block->m_static_objects.insert(new_id, s_obj);
1829 // Only mark block as modified if data changed considerably
1830 if(shall_be_written)
1831 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1832 "deactivateFarObjects: Static data "
1833 "changed considerably");
1835 obj->m_static_exists = true;
1836 obj->m_static_block = block->getPos();
1840 errorstream<<"ServerEnv: Could not find or generate "
1841 <<"a block for storing id="<<obj->getId()
1842 <<" statically"<<std::endl;
1847 If known by some client, set pending deactivation.
1848 Otherwise delete it immediately.
1853 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1854 <<"object id="<<id<<" is known by clients"
1855 <<"; not deleting yet"<<std::endl;
1857 obj->m_pending_deactivation = true;
1861 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1862 <<"object id="<<id<<" is not known by clients"
1863 <<"; deleting"<<std::endl;
1865 // Deregister in scripting api
1866 scriptapi_rm_object_reference(m_lua, obj);
1868 // Delete active object
1870 // Id to be removed from m_active_objects
1871 objects_to_remove.push_back(id);
1874 // Remove references from m_active_objects
1875 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1876 i != objects_to_remove.end(); i++)
1878 m_active_objects.remove(*i);
1889 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1890 ITextureSource *texturesource, IGameDef *gamedef):
1893 m_texturesource(texturesource),
1900 ClientEnvironment::~ClientEnvironment()
1902 // delete active objects
1903 for(core::map<u16, ClientActiveObject*>::Iterator
1904 i = m_active_objects.getIterator();
1905 i.atEnd()==false; i++)
1907 delete i.getNode()->getValue();
1914 void ClientEnvironment::addPlayer(Player *player)
1916 DSTACK(__FUNCTION_NAME);
1918 It is a failure if player is local and there already is a local
1921 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1923 Environment::addPlayer(player);
1926 LocalPlayer * ClientEnvironment::getLocalPlayer()
1928 for(core::list<Player*>::Iterator i = m_players.begin();
1929 i != m_players.end(); i++)
1931 Player *player = *i;
1932 if(player->isLocal())
1933 return (LocalPlayer*)player;
1938 void ClientEnvironment::step(float dtime)
1940 DSTACK(__FUNCTION_NAME);
1942 // Get some settings
1943 bool free_move = g_settings->getBool("free_move");
1944 bool footprints = g_settings->getBool("footprints");
1947 LocalPlayer *lplayer = getLocalPlayer();
1949 // collision info queue
1950 core::list<CollisionInfo> player_collisions;
1953 Get the speed the player is going
1955 bool is_climbing = lplayer->is_climbing;
1957 f32 player_speed = 0.001; // just some small value
1958 player_speed = lplayer->getSpeed().getLength();
1961 Maximum position increment
1963 //f32 position_max_increment = 0.05*BS;
1964 f32 position_max_increment = 0.1*BS;
1966 // Maximum time increment (for collision detection etc)
1967 // time = distance / speed
1968 f32 dtime_max_increment = position_max_increment / player_speed;
1970 // Maximum time increment is 10ms or lower
1971 if(dtime_max_increment > 0.01)
1972 dtime_max_increment = 0.01;
1974 // Don't allow overly huge dtime
1978 f32 dtime_downcount = dtime;
1981 Stuff that has a maximum time increment
1990 if(dtime_downcount > dtime_max_increment)
1992 dtime_part = dtime_max_increment;
1993 dtime_downcount -= dtime_part;
1997 dtime_part = dtime_downcount;
1999 Setting this to 0 (no -=dtime_part) disables an infinite loop
2000 when dtime_part is so small that dtime_downcount -= dtime_part
2003 dtime_downcount = 0;
2011 v3f lplayerpos = lplayer->getPosition();
2014 if(free_move == false && is_climbing == false)
2017 v3f speed = lplayer->getSpeed();
2018 if(lplayer->swimming_up == false)
2019 speed.Y -= 9.81 * BS * dtime_part * 2;
2022 if(lplayer->in_water_stable || lplayer->in_water)
2024 f32 max_down = 2.0*BS;
2025 if(speed.Y < -max_down) speed.Y = -max_down;
2028 if(speed.getLength() > max)
2030 speed = speed / speed.getLength() * max;
2034 lplayer->setSpeed(speed);
2039 This also does collision detection.
2041 lplayer->move(dtime_part, *m_map, position_max_increment,
2042 &player_collisions);
2045 while(dtime_downcount > 0.001);
2047 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2049 for(core::list<CollisionInfo>::Iterator
2050 i = player_collisions.begin();
2051 i != player_collisions.end(); i++)
2053 CollisionInfo &info = *i;
2054 if(info.t == COLLISION_FALL)
2056 //f32 tolerance = BS*10; // 2 without damage
2057 f32 tolerance = BS*12; // 3 without damage
2059 if(info.speed > tolerance)
2061 f32 damage_f = (info.speed - tolerance)/BS*factor;
2062 u16 damage = (u16)(damage_f+0.5);
2063 if(lplayer->hp > damage)
2064 lplayer->hp -= damage;
2068 ClientEnvEvent event;
2069 event.type = CEE_PLAYER_DAMAGE;
2070 event.player_damage.amount = damage;
2071 m_client_event_queue.push_back(event);
2077 A quick draft of lava damage
2079 if(m_lava_hurt_interval.step(dtime, 1.0))
2081 v3f pf = lplayer->getPosition();
2083 // Feet, middle and head
2084 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2085 MapNode n1 = m_map->getNodeNoEx(p1);
2086 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2087 MapNode n2 = m_map->getNodeNoEx(p2);
2088 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2089 MapNode n3 = m_map->getNodeNoEx(p2);
2091 u32 damage_per_second = 0;
2092 damage_per_second = MYMAX(damage_per_second,
2093 m_gamedef->ndef()->get(n1).damage_per_second);
2094 damage_per_second = MYMAX(damage_per_second,
2095 m_gamedef->ndef()->get(n2).damage_per_second);
2096 damage_per_second = MYMAX(damage_per_second,
2097 m_gamedef->ndef()->get(n3).damage_per_second);
2099 if(damage_per_second != 0)
2101 ClientEnvEvent event;
2102 event.type = CEE_PLAYER_DAMAGE;
2103 event.player_damage.amount = damage_per_second;
2104 m_client_event_queue.push_back(event);
2109 Stuff that can be done in an arbitarily large dtime
2111 for(core::list<Player*>::Iterator i = m_players.begin();
2112 i != m_players.end(); i++)
2114 Player *player = *i;
2115 v3f playerpos = player->getPosition();
2118 Handle non-local players
2120 if(player->isLocal() == false)
2123 player->move(dtime, *m_map, 100*BS);
2127 // Update lighting on all players on client
2128 u8 light = LIGHT_MAX;
2131 v3s16 p = player->getLightPosition();
2132 MapNode n = m_map->getNode(p);
2133 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2135 catch(InvalidPositionException &e) {}
2136 player->updateLight(light);
2139 Add footsteps to grass
2143 // Get node that is at BS/4 under player
2144 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2146 MapNode n = m_map->getNode(bottompos);
2147 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2149 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2150 m_map->setNode(bottompos, n);
2151 // Update mesh on client
2152 if(m_map->mapType() == MAPTYPE_CLIENT)
2154 v3s16 p_blocks = getNodeBlockPos(bottompos);
2155 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2156 //b->updateMesh(getDayNightRatio());
2157 b->setMeshExpired(true);
2161 catch(InvalidPositionException &e)
2168 Step active objects and update lighting of them
2171 for(core::map<u16, ClientActiveObject*>::Iterator
2172 i = m_active_objects.getIterator();
2173 i.atEnd()==false; i++)
2175 ClientActiveObject* obj = i.getNode()->getValue();
2177 obj->step(dtime, this);
2179 if(m_active_object_light_update_interval.step(dtime, 0.21))
2182 //u8 light = LIGHT_MAX;
2186 v3s16 p = obj->getLightPosition();
2187 MapNode n = m_map->getNode(p);
2188 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2190 catch(InvalidPositionException &e) {}
2191 obj->updateLight(light);
2196 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2198 m_map->updateMeshes(blockpos, getDayNightRatio());
2201 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2203 m_map->expireMeshes(only_daynight_diffed);
2206 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2208 core::map<u16, ClientActiveObject*>::Node *n;
2209 n = m_active_objects.find(id);
2212 return n->getValue();
2215 bool isFreeClientActiveObjectId(u16 id,
2216 core::map<u16, ClientActiveObject*> &objects)
2221 for(core::map<u16, ClientActiveObject*>::Iterator
2222 i = objects.getIterator();
2223 i.atEnd()==false; i++)
2225 if(i.getNode()->getKey() == id)
2231 u16 getFreeClientActiveObjectId(
2232 core::map<u16, ClientActiveObject*> &objects)
2237 if(isFreeClientActiveObjectId(new_id, objects))
2247 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2250 if(object->getId() == 0)
2252 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2255 infostream<<"ClientEnvironment::addActiveObject(): "
2256 <<"no free ids available"<<std::endl;
2260 object->setId(new_id);
2262 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2264 infostream<<"ClientEnvironment::addActiveObject(): "
2265 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2269 infostream<<"ClientEnvironment::addActiveObject(): "
2270 <<"added (id="<<object->getId()<<")"<<std::endl;
2271 m_active_objects.insert(object->getId(), object);
2272 // TODO: Make g_texturesource non-global
2273 object->addToScene(m_smgr, m_texturesource);
2274 return object->getId();
2277 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2278 const std::string &init_data)
2280 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2283 infostream<<"ClientEnvironment::addActiveObject(): "
2284 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2291 obj->initialize(init_data);
2293 addActiveObject(obj);
2296 void ClientEnvironment::removeActiveObject(u16 id)
2298 infostream<<"ClientEnvironment::removeActiveObject(): "
2299 <<"id="<<id<<std::endl;
2300 ClientActiveObject* obj = getActiveObject(id);
2303 infostream<<"ClientEnvironment::removeActiveObject(): "
2304 <<"id="<<id<<" not found"<<std::endl;
2307 obj->removeFromScene();
2309 m_active_objects.remove(id);
2312 void ClientEnvironment::processActiveObjectMessage(u16 id,
2313 const std::string &data)
2315 ClientActiveObject* obj = getActiveObject(id);
2318 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2319 <<" got message for id="<<id<<", which doesn't exist."
2323 obj->processMessage(data);
2327 Callbacks for activeobjects
2330 void ClientEnvironment::damageLocalPlayer(u8 damage)
2332 LocalPlayer *lplayer = getLocalPlayer();
2335 if(lplayer->hp > damage)
2336 lplayer->hp -= damage;
2340 ClientEnvEvent event;
2341 event.type = CEE_PLAYER_DAMAGE;
2342 event.player_damage.amount = damage;
2343 m_client_event_queue.push_back(event);
2347 Client likes to call these
2350 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2351 core::array<DistanceSortedActiveObject> &dest)
2353 for(core::map<u16, ClientActiveObject*>::Iterator
2354 i = m_active_objects.getIterator();
2355 i.atEnd()==false; i++)
2357 ClientActiveObject* obj = i.getNode()->getValue();
2359 f32 d = (obj->getPosition() - origin).getLength();
2364 DistanceSortedActiveObject dso(obj, d);
2366 dest.push_back(dso);
2370 ClientEnvEvent ClientEnvironment::getClientEvent()
2372 if(m_client_event_queue.size() == 0)
2374 ClientEnvEvent event;
2375 event.type = CEE_NONE;
2378 return m_client_event_queue.pop_front();
2381 #endif // #ifndef SERVER