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->setChangedFlag();
591 last_node_walkable = false;
597 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
599 // Get time difference
601 u32 stamp = block->getTimestamp();
602 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
603 dtime_s = m_game_time - block->getTimestamp();
604 dtime_s += additional_dtime;
606 // Set current time as timestamp (and let it set ChangedFlag)
607 block->setTimestamp(m_game_time);
609 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
611 // Activate stored objects
612 activateObjects(block);
615 bool changed = block->m_node_metadata->step((float)dtime_s);
619 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
620 event.p = block->getPos();
621 m_map->dispatchEvent(&event);
623 block->setChangedFlag();
626 // TODO: Do something
627 // TODO: Implement usage of ActiveBlockModifier
629 // Here's a quick demonstration
631 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
632 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
633 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
635 v3s16 p = p0 + block->getPosRelative();
636 MapNode n = block->getNodeNoEx(p0);
639 // Convert all mud under proper day lighting to grass
640 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
644 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
645 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
646 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
648 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
649 m_map->addNodeWithEvent(p, n);
657 void ServerEnvironment::clearAllObjects()
659 infostream<<"ServerEnvironment::clearAllObjects(): "
660 <<"Removing all active objects"<<std::endl;
661 core::list<u16> objects_to_remove;
662 for(core::map<u16, ServerActiveObject*>::Iterator
663 i = m_active_objects.getIterator();
664 i.atEnd()==false; i++)
666 ServerActiveObject* obj = i.getNode()->getValue();
667 u16 id = i.getNode()->getKey();
668 v3f objectpos = obj->getBasePosition();
669 // Delete static object if block is loaded
670 if(obj->m_static_exists){
671 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
673 block->m_static_objects.remove(id);
674 block->raiseModified(MOD_STATE_WRITE_NEEDED);
675 obj->m_static_exists = false;
678 // If known by some client, don't delete immediately
679 if(obj->m_known_by_count > 0){
680 obj->m_pending_deactivation = true;
681 obj->m_removed = true;
684 // Deregister in scripting api
685 scriptapi_rm_object_reference(m_lua, obj);
686 // Delete active object
688 // Id to be removed from m_active_objects
689 objects_to_remove.push_back(id);
691 // Remove references from m_active_objects
692 for(core::list<u16>::Iterator i = objects_to_remove.begin();
693 i != objects_to_remove.end(); i++)
695 m_active_objects.remove(*i);
698 core::list<v3s16> loadable_blocks;
699 infostream<<"ServerEnvironment::clearAllObjects(): "
700 <<"Listing all loadable blocks"<<std::endl;
701 m_map->listAllLoadableBlocks(loadable_blocks);
702 infostream<<"ServerEnvironment::clearAllObjects(): "
703 <<"Done listing all loadable blocks: "
704 <<loadable_blocks.size()
705 <<", now clearing"<<std::endl;
706 u32 report_interval = loadable_blocks.size() / 10;
707 u32 num_blocks_checked = 0;
708 u32 num_blocks_cleared = 0;
709 u32 num_objs_cleared = 0;
710 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
711 i != loadable_blocks.end(); i++)
714 MapBlock *block = m_map->emergeBlock(p, false);
716 errorstream<<"ServerEnvironment::clearAllObjects(): "
717 <<"Failed to emerge block "<<PP(p)<<std::endl;
720 u32 num_stored = block->m_static_objects.m_stored.size();
721 u32 num_active = block->m_static_objects.m_active.size();
722 if(num_stored != 0 || num_active != 0){
723 block->m_static_objects.m_stored.clear();
724 block->m_static_objects.m_active.clear();
725 block->raiseModified(MOD_STATE_WRITE_NEEDED);
726 num_objs_cleared += num_stored + num_active;
727 num_blocks_cleared++;
729 num_blocks_checked++;
731 if(num_blocks_checked % report_interval == 0){
732 float percent = 100.0 * (float)num_blocks_checked /
733 loadable_blocks.size();
734 infostream<<"ServerEnvironment::clearAllObjects(): "
735 <<"Cleared "<<num_objs_cleared<<" objects"
736 <<" in "<<num_blocks_cleared<<" blocks ("
737 <<percent<<"%)"<<std::endl;
740 infostream<<"ServerEnvironment::clearAllObjects(): "
741 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
742 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
745 static void getMob_dungeon_master(Settings &properties)
747 properties.set("looks", "dungeon_master");
748 properties.setFloat("yaw", 1.57);
749 properties.setFloat("hp", 30);
750 properties.setBool("bright_shooting", true);
751 properties.set("shoot_type", "fireball");
752 properties.set("shoot_y", "0.7");
753 properties.set("player_hit_damage", "1");
754 properties.set("player_hit_distance", "1.0");
755 properties.set("player_hit_interval", "0.5");
756 properties.setBool("mindless_rage", myrand_range(0,100)==0);
759 void ServerEnvironment::step(float dtime)
761 DSTACK(__FUNCTION_NAME);
763 //TimeTaker timer("ServerEnv step");
766 bool footprints = g_settings->getBool("footprints");
772 m_game_time_fraction_counter += dtime;
773 u32 inc_i = (u32)m_game_time_fraction_counter;
774 m_game_time += inc_i;
775 m_game_time_fraction_counter -= (float)inc_i;
782 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
783 for(core::list<Player*>::Iterator i = m_players.begin();
784 i != m_players.end(); i++)
788 // Ignore disconnected players
789 if(player->peer_id == 0)
792 v3f playerpos = player->getPosition();
795 player->move(dtime, *m_map, 100*BS);
798 Add footsteps to grass
802 // Get node that is at BS/4 under player
803 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
805 MapNode n = m_map->getNode(bottompos);
806 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
808 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
809 m_map->setNode(bottompos, n);
812 catch(InvalidPositionException &e)
820 Manage active block list
822 if(m_active_blocks_management_interval.step(dtime, 2.0))
824 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
826 Get player block positions
828 core::list<v3s16> players_blockpos;
829 for(core::list<Player*>::Iterator
830 i = m_players.begin();
831 i != m_players.end(); i++)
834 // Ignore disconnected players
835 if(player->peer_id == 0)
837 v3s16 blockpos = getNodeBlockPos(
838 floatToInt(player->getPosition(), BS));
839 players_blockpos.push_back(blockpos);
843 Update list of active blocks, collecting changes
845 const s16 active_block_range = g_settings->getS16("active_block_range");
846 core::map<v3s16, bool> blocks_removed;
847 core::map<v3s16, bool> blocks_added;
848 m_active_blocks.update(players_blockpos, active_block_range,
849 blocks_removed, blocks_added);
852 Handle removed blocks
855 // Convert active objects that are no more in active blocks to static
856 deactivateFarObjects(false);
858 for(core::map<v3s16, bool>::Iterator
859 i = blocks_removed.getIterator();
860 i.atEnd()==false; i++)
862 v3s16 p = i.getNode()->getKey();
864 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
865 <<") became inactive"<<std::endl;*/
867 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
871 // Set current time as timestamp (and let it set ChangedFlag)
872 block->setTimestamp(m_game_time);
879 for(core::map<v3s16, bool>::Iterator
880 i = blocks_added.getIterator();
881 i.atEnd()==false; i++)
883 v3s16 p = i.getNode()->getKey();
885 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
886 <<") became active"<<std::endl;*/
888 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
892 activateBlock(block);
897 Mess around in active blocks
899 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
901 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
905 for(core::map<v3s16, bool>::Iterator
906 i = m_active_blocks.m_list.getIterator();
907 i.atEnd()==false; i++)
909 v3s16 p = i.getNode()->getKey();
911 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
912 <<") being handled"<<std::endl;*/
914 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
918 // Reset block usage timer
919 block->resetUsageTimer();
921 // Set current time as timestamp
922 block->setTimestampNoChangedFlag(m_game_time);
925 bool changed = block->m_node_metadata->step(dtime);
929 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
931 m_map->dispatchEvent(&event);
933 block->setChangedFlag();
938 if(m_active_blocks_test_interval.step(dtime, 10.0))
940 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
941 //float dtime = 10.0;
943 for(core::map<v3s16, bool>::Iterator
944 i = m_active_blocks.m_list.getIterator();
945 i.atEnd()==false; i++)
947 v3s16 p = i.getNode()->getKey();
949 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
950 <<") being handled"<<std::endl;*/
952 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
956 // Set current time as timestamp
957 block->setTimestampNoChangedFlag(m_game_time);
962 Note that map modifications should be done using the event-
963 making map methods so that the server gets information
966 Reading can be done quickly directly from the block.
968 Everything should bind to inside this single content
969 searching loop to keep things fast.
971 // TODO: Implement usage of ActiveBlockModifier
973 // Find out how many objects the block contains
974 //u32 active_object_count = block->m_static_objects.m_active.size();
975 // Find out how many objects this and all the neighbors contain
976 u32 active_object_count_wider = 0;
977 for(s16 x=-1; x<=1; x++)
978 for(s16 y=-1; y<=1; y++)
979 for(s16 z=-1; z<=1; z++)
981 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
984 active_object_count_wider +=
985 block->m_static_objects.m_active.size()
986 + block->m_static_objects.m_stored.size();
988 /*if(block->m_static_objects.m_stored.size() != 0){
989 errorstream<<"ServerEnvironment::step(): "
990 <<PP(block->getPos())<<" contains "
991 <<block->m_static_objects.m_stored.size()
992 <<" stored objects; "
993 <<"when spawning objects, when counting active "
994 <<"objects in wide area. relative position: "
995 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1000 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1001 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1002 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1004 v3s16 p = p0 + block->getPosRelative();
1005 MapNode n = block->getNodeNoEx(p0);
1009 Convert mud under proper lighting to grass
1011 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
1013 if(myrand()%20 == 0)
1015 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1016 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
1017 n_top.getLightBlend(getDayNightRatio(),
1018 m_gamedef->ndef()) >= 13)
1020 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
1021 m_map->addNodeWithEvent(p, n);
1026 Convert grass into mud if under something else than air
1028 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1030 //if(myrand()%20 == 0)
1032 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1033 if(m_gamedef->ndef()->get(n_top).air_equivalent == false)
1035 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_MUD"));
1036 m_map->addNodeWithEvent(p, n);
1041 Rats spawn around regular trees
1043 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_TREE") ||
1044 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_JUNGLETREE"))
1046 if(myrand()%200 == 0 && active_object_count_wider == 0)
1048 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1049 0, myrand_range(-2, 2));
1050 MapNode n1 = m_map->getNodeNoEx(p1);
1051 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1052 if(n1b.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS") &&
1053 n1.getContent() == CONTENT_AIR)
1055 v3f pos = intToFloat(p1, BS);
1056 ServerActiveObject *obj = new RatSAO(this, pos);
1057 addActiveObject(obj);
1062 Fun things spawn in caves and dungeons
1064 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_STONE") ||
1065 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MOSSYCOBBLE"))
1067 if(myrand()%200 == 0 && active_object_count_wider == 0)
1069 v3s16 p1 = p + v3s16(0,1,0);
1070 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1071 if(n1a.getLightBlend(getDayNightRatio(),
1072 m_gamedef->ndef()) <= 3){
1073 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1074 if(n1a.getContent() == CONTENT_AIR &&
1075 n1b.getContent() == CONTENT_AIR)
1077 v3f pos = intToFloat(p1, BS);
1079 if(i == 0 || i == 1){
1080 actionstream<<"A dungeon master spawns at "
1081 <<PP(p1)<<std::endl;
1082 Settings properties;
1083 getMob_dungeon_master(properties);
1084 ServerActiveObject *obj = new MobV2SAO(
1085 this, pos, &properties);
1086 addActiveObject(obj);
1087 } else if(i == 2 || i == 3){
1088 actionstream<<"Rats spawn at "
1089 <<PP(p1)<<std::endl;
1090 for(int j=0; j<3; j++){
1091 ServerActiveObject *obj = new RatSAO(
1093 addActiveObject(obj);
1096 actionstream<<"An oerkki spawns at "
1097 <<PP(p1)<<std::endl;
1098 ServerActiveObject *obj = new Oerkki1SAO(
1100 addActiveObject(obj);
1107 Make trees from saplings!
1109 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_SAPLING"))
1111 if(myrand()%50 == 0)
1113 actionstream<<"A sapling grows into a tree at "
1116 core::map<v3s16, MapBlock*> modified_blocks;
1118 ManualMapVoxelManipulator vmanip(m_map);
1119 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1120 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1121 bool is_apple_tree = myrand()%4 == 0;
1122 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1124 vmanip.blitBackAll(&modified_blocks);
1127 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1128 for(core::map<v3s16, MapBlock*>::Iterator
1129 i = modified_blocks.getIterator();
1130 i.atEnd() == false; i++)
1132 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1134 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1136 // Send a MEET_OTHER event
1138 event.type = MEET_OTHER;
1139 for(core::map<v3s16, MapBlock*>::Iterator
1140 i = modified_blocks.getIterator();
1141 i.atEnd() == false; i++)
1143 v3s16 p = i.getNode()->getKey();
1144 event.modified_blocks.insert(p, true);
1146 m_map->dispatchEvent(&event);
1154 Step script environment (run global on_step())
1156 scriptapi_environment_step(m_lua, dtime);
1162 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1163 //TimeTaker timer("Step active objects");
1165 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1167 // This helps the objects to send data at the same time
1168 bool send_recommended = false;
1169 m_send_recommended_timer += dtime;
1170 if(m_send_recommended_timer > getSendRecommendedInterval())
1172 m_send_recommended_timer -= getSendRecommendedInterval();
1173 send_recommended = true;
1176 for(core::map<u16, ServerActiveObject*>::Iterator
1177 i = m_active_objects.getIterator();
1178 i.atEnd()==false; i++)
1180 ServerActiveObject* obj = i.getNode()->getValue();
1181 // Remove non-peaceful mobs on peaceful mode
1182 if(g_settings->getBool("only_peaceful_mobs")){
1183 if(!obj->isPeaceful())
1184 obj->m_removed = true;
1186 // Don't step if is to be removed or stored statically
1187 if(obj->m_removed || obj->m_pending_deactivation)
1190 obj->step(dtime, send_recommended);
1191 // Read messages from object
1192 while(obj->m_messages_out.size() > 0)
1194 m_active_object_messages.push_back(
1195 obj->m_messages_out.pop_front());
1201 Manage active objects
1203 if(m_object_management_interval.step(dtime, 0.5))
1205 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1207 Remove objects that satisfy (m_removed && m_known_by_count==0)
1209 removeRemovedObjects();
1212 if(g_settings->getBool("enable_experimental"))
1219 m_random_spawn_timer -= dtime;
1220 if(m_random_spawn_timer < 0)
1222 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1223 //m_random_spawn_timer += 2.0;
1224 m_random_spawn_timer += 200.0;
1230 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1231 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1232 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1234 Player *player = getRandomConnectedPlayer();
1237 pos = player->getPosition();
1239 myrand_range(-3,3)*BS,
1241 myrand_range(-3,3)*BS
1245 Create a ServerActiveObject
1248 //TestSAO *obj = new TestSAO(this, pos);
1249 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1250 //ServerActiveObject *obj = new RatSAO(this, pos);
1251 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1252 //ServerActiveObject *obj = new FireflySAO(this, pos);
1254 infostream<<"Server: Spawning MobV2SAO at "
1255 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1257 Settings properties;
1258 getMob_dungeon_master(properties);
1259 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1260 addActiveObject(obj);
1264 } // enable_experimental
1267 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1269 core::map<u16, ServerActiveObject*>::Node *n;
1270 n = m_active_objects.find(id);
1273 return n->getValue();
1276 bool isFreeServerActiveObjectId(u16 id,
1277 core::map<u16, ServerActiveObject*> &objects)
1282 for(core::map<u16, ServerActiveObject*>::Iterator
1283 i = objects.getIterator();
1284 i.atEnd()==false; i++)
1286 if(i.getNode()->getKey() == id)
1292 u16 getFreeServerActiveObjectId(
1293 core::map<u16, ServerActiveObject*> &objects)
1298 if(isFreeServerActiveObjectId(new_id, objects))
1308 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1311 u16 id = addActiveObjectRaw(object, true);
1315 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1319 v3f objectpos = obj->getBasePosition();
1321 // The block in which the object resides in
1322 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1325 Update the static data
1328 // Create new static object
1329 std::string staticdata = obj->getStaticData();
1330 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1331 // Add to the block where the object is located in
1332 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1333 // Get or generate the block
1334 MapBlock *block = m_map->emergeBlock(blockpos);
1336 bool succeeded = false;
1340 block->m_static_objects.insert(0, s_obj);
1341 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1345 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1346 <<"Could not find or generate "
1347 <<"a block for storing static object"<<std::endl;
1357 Finds out what new objects have been added to
1358 inside a radius around a position
1360 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1361 core::map<u16, bool> ¤t_objects,
1362 core::map<u16, bool> &added_objects)
1364 v3f pos_f = intToFloat(pos, BS);
1365 f32 radius_f = radius * BS;
1367 Go through the object list,
1368 - discard m_removed objects,
1369 - discard objects that are too far away,
1370 - discard objects that are found in current_objects.
1371 - add remaining objects to added_objects
1373 for(core::map<u16, ServerActiveObject*>::Iterator
1374 i = m_active_objects.getIterator();
1375 i.atEnd()==false; i++)
1377 u16 id = i.getNode()->getKey();
1379 ServerActiveObject *object = i.getNode()->getValue();
1382 // Discard if removed
1383 if(object->m_removed)
1385 // Discard if too far
1386 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1387 if(distance_f > radius_f)
1389 // Discard if already on current_objects
1390 core::map<u16, bool>::Node *n;
1391 n = current_objects.find(id);
1394 // Add to added_objects
1395 added_objects.insert(id, false);
1400 Finds out what objects have been removed from
1401 inside a radius around a position
1403 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1404 core::map<u16, bool> ¤t_objects,
1405 core::map<u16, bool> &removed_objects)
1407 v3f pos_f = intToFloat(pos, BS);
1408 f32 radius_f = radius * BS;
1410 Go through current_objects; object is removed if:
1411 - object is not found in m_active_objects (this is actually an
1412 error condition; objects should be set m_removed=true and removed
1413 only after all clients have been informed about removal), or
1414 - object has m_removed=true, or
1415 - object is too far away
1417 for(core::map<u16, bool>::Iterator
1418 i = current_objects.getIterator();
1419 i.atEnd()==false; i++)
1421 u16 id = i.getNode()->getKey();
1422 ServerActiveObject *object = getActiveObject(id);
1425 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1426 <<" object in current_objects is NULL"<<std::endl;
1428 else if(object->m_removed == false)
1430 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1431 /*infostream<<"removed == false"
1432 <<"distance_f = "<<distance_f
1433 <<", radius_f = "<<radius_f<<std::endl;*/
1434 if(distance_f < radius_f)
1440 removed_objects.insert(id, false);
1444 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1446 if(m_active_object_messages.size() == 0)
1447 return ActiveObjectMessage(0);
1449 return m_active_object_messages.pop_front();
1453 ************ Private methods *************
1456 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1460 if(object->getId() == 0){
1461 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1464 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1465 <<"no free ids available"<<std::endl;
1469 object->setId(new_id);
1472 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1473 <<"supplied with id "<<object->getId()<<std::endl;
1475 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1477 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1478 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1482 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1485 m_active_objects.insert(object->getId(), object);
1487 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1488 <<"Added id="<<object->getId()<<"; there are now "
1489 <<m_active_objects.size()<<" active objects."
1492 // Add static object to active static list of the block
1493 v3f objectpos = object->getBasePosition();
1494 std::string staticdata = object->getStaticData();
1495 StaticObject s_obj(object->getType(), objectpos, staticdata);
1496 // Add to the block where the object is located in
1497 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1498 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1501 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1502 object->m_static_exists = true;
1503 object->m_static_block = blockpos;
1506 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1509 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1510 <<"could not find block for storing id="<<object->getId()
1511 <<" statically"<<std::endl;
1514 // Register reference in scripting api (must be done before post-init)
1515 scriptapi_add_object_reference(m_lua, object);
1516 // Post-initialize object
1517 object->addedToEnvironment();
1519 return object->getId();
1523 Remove objects that satisfy (m_removed && m_known_by_count==0)
1525 void ServerEnvironment::removeRemovedObjects()
1527 core::list<u16> objects_to_remove;
1528 for(core::map<u16, ServerActiveObject*>::Iterator
1529 i = m_active_objects.getIterator();
1530 i.atEnd()==false; i++)
1532 u16 id = i.getNode()->getKey();
1533 ServerActiveObject* obj = i.getNode()->getValue();
1534 // This shouldn't happen but check it
1537 infostream<<"NULL object found in ServerEnvironment"
1538 <<" while finding removed objects. id="<<id<<std::endl;
1539 // Id to be removed from m_active_objects
1540 objects_to_remove.push_back(id);
1545 We will delete objects that are marked as removed or thatare
1546 waiting for deletion after deactivation
1548 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1552 Delete static data from block if is marked as removed
1554 if(obj->m_static_exists && obj->m_removed)
1556 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1559 block->m_static_objects.remove(id);
1560 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1561 obj->m_static_exists = false;
1565 // If m_known_by_count > 0, don't actually remove.
1566 if(obj->m_known_by_count > 0)
1569 // Deregister in scripting api
1570 scriptapi_rm_object_reference(m_lua, obj);
1574 // Id to be removed from m_active_objects
1575 objects_to_remove.push_back(id);
1577 // Remove references from m_active_objects
1578 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1579 i != objects_to_remove.end(); i++)
1581 m_active_objects.remove(*i);
1585 static void print_hexdump(std::ostream &o, const std::string &data)
1587 const int linelength = 16;
1588 for(int l=0; ; l++){
1589 int i0 = linelength * l;
1590 bool at_end = false;
1591 int thislinelength = linelength;
1592 if(i0 + thislinelength > (int)data.size()){
1593 thislinelength = data.size() - i0;
1596 for(int di=0; di<linelength; di++){
1599 if(di<thislinelength)
1600 snprintf(buf, 4, "%.2x ", data[i]);
1602 snprintf(buf, 4, " ");
1606 for(int di=0; di<thislinelength; di++){
1620 Convert stored objects from blocks near the players to active.
1622 void ServerEnvironment::activateObjects(MapBlock *block)
1626 // Ignore if no stored objects (to not set changed flag)
1627 if(block->m_static_objects.m_stored.size() == 0)
1629 verbosestream<<"ServerEnvironment::activateObjects(): "
1630 <<"activating objects of block "<<PP(block->getPos())
1631 <<" ("<<block->m_static_objects.m_stored.size()
1632 <<" objects)"<<std::endl;
1633 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1635 errorstream<<"suspiciously large amount of objects detected: "
1636 <<block->m_static_objects.m_stored.size()<<" in "
1637 <<PP(block->getPos())
1638 <<"; removing all of them."<<std::endl;
1639 // Clear stored list
1640 block->m_static_objects.m_stored.clear();
1641 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1644 // A list for objects that couldn't be converted to static for some
1645 // reason. They will be stored back.
1646 core::list<StaticObject> new_stored;
1647 // Loop through stored static objects
1648 for(core::list<StaticObject>::Iterator
1649 i = block->m_static_objects.m_stored.begin();
1650 i != block->m_static_objects.m_stored.end(); i++)
1652 /*infostream<<"Server: Creating an active object from "
1653 <<"static data"<<std::endl;*/
1654 StaticObject &s_obj = *i;
1655 // Create an active object from the data
1656 ServerActiveObject *obj = ServerActiveObject::create
1657 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1658 // If couldn't create object, store static data back.
1661 errorstream<<"ServerEnvironment::activateObjects(): "
1662 <<"failed to create active object from static object "
1663 <<"in block "<<PP(s_obj.pos/BS)
1664 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1665 print_hexdump(verbosestream, s_obj.data);
1667 new_stored.push_back(s_obj);
1670 verbosestream<<"ServerEnvironment::activateObjects(): "
1671 <<"activated static object pos="<<PP(s_obj.pos/BS)
1672 <<" type="<<(int)s_obj.type<<std::endl;
1673 // This will also add the object to the active static list
1674 addActiveObjectRaw(obj, false);
1676 // Clear stored list
1677 block->m_static_objects.m_stored.clear();
1678 // Add leftover failed stuff to stored list
1679 for(core::list<StaticObject>::Iterator
1680 i = new_stored.begin();
1681 i != new_stored.end(); i++)
1683 StaticObject &s_obj = *i;
1684 block->m_static_objects.m_stored.push_back(s_obj);
1687 Note: Block hasn't really been modified here.
1688 The objects have just been activated and moved from the stored
1689 static list to the active static list.
1690 As such, the block is essentially the same.
1691 Thus, do not call block->setChangedFlag().
1692 Otherwise there would be a huge amount of unnecessary I/O.
1697 Convert objects that are not standing inside active blocks to static.
1699 If m_known_by_count != 0, active object is not deleted, but static
1700 data is still updated.
1702 If force_delete is set, active object is deleted nevertheless. It
1703 shall only be set so in the destructor of the environment.
1705 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1707 core::list<u16> objects_to_remove;
1708 for(core::map<u16, ServerActiveObject*>::Iterator
1709 i = m_active_objects.getIterator();
1710 i.atEnd()==false; i++)
1712 ServerActiveObject* obj = i.getNode()->getValue();
1714 // This shouldn't happen but check it
1717 errorstream<<"NULL object found in ServerEnvironment"
1723 // If pending deactivation, let removeRemovedObjects() do it
1724 if(obj->m_pending_deactivation)
1727 u16 id = i.getNode()->getKey();
1728 v3f objectpos = obj->getBasePosition();
1730 // The block in which the object resides in
1731 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1733 // If block is active, don't remove
1734 if(m_active_blocks.contains(blockpos_o))
1737 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1738 <<"deactivating object id="<<id<<" on inactive block "
1739 <<PP(blockpos_o)<<std::endl;
1741 // If known by some client, don't immediately delete.
1742 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1745 Update the static data
1748 // Create new static object
1749 std::string staticdata_new = obj->getStaticData();
1750 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1752 bool stays_in_same_block = false;
1753 bool data_changed = true;
1755 if(obj->m_static_exists){
1756 if(obj->m_static_block == blockpos_o)
1757 stays_in_same_block = true;
1759 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1761 core::map<u16, StaticObject>::Node *n =
1762 block->m_static_objects.m_active.find(id);
1764 StaticObject static_old = n->getValue();
1766 float save_movem = obj->getMinimumSavedMovement();
1768 if(static_old.data == staticdata_new &&
1769 (static_old.pos - objectpos).getLength() < save_movem)
1770 data_changed = false;
1772 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1773 <<"id="<<id<<" m_static_exists=true but "
1774 <<"static data doesn't actually exist in "
1775 <<PP(obj->m_static_block)<<std::endl;
1779 bool shall_be_written = (!stays_in_same_block || data_changed);
1781 // Delete old static object
1782 if(obj->m_static_exists)
1784 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1787 block->m_static_objects.remove(id);
1788 obj->m_static_exists = false;
1789 // Only mark block as modified if data changed considerably
1790 if(shall_be_written)
1791 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1795 // Add to the block where the object is located in
1796 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1797 // Get or generate the block
1798 MapBlock *block = m_map->emergeBlock(blockpos);
1802 if(block->m_static_objects.m_stored.size() >= 49){
1803 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1804 <<" statically but block "<<PP(blockpos)
1805 <<" already contains "
1806 <<block->m_static_objects.m_stored.size()
1807 <<" (over 49) objects."
1808 <<" Forcing delete."<<std::endl;
1809 force_delete = true;
1811 u16 new_id = pending_delete ? id : 0;
1812 block->m_static_objects.insert(new_id, s_obj);
1814 // Only mark block as modified if data changed considerably
1815 if(shall_be_written)
1816 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1818 obj->m_static_exists = true;
1819 obj->m_static_block = block->getPos();
1823 errorstream<<"ServerEnv: Could not find or generate "
1824 <<"a block for storing id="<<obj->getId()
1825 <<" statically"<<std::endl;
1830 If known by some client, set pending deactivation.
1831 Otherwise delete it immediately.
1836 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1837 <<"object id="<<id<<" is known by clients"
1838 <<"; not deleting yet"<<std::endl;
1840 obj->m_pending_deactivation = true;
1844 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1845 <<"object id="<<id<<" is not known by clients"
1846 <<"; deleting"<<std::endl;
1848 // Deregister in scripting api
1849 scriptapi_rm_object_reference(m_lua, obj);
1851 // Delete active object
1853 // Id to be removed from m_active_objects
1854 objects_to_remove.push_back(id);
1857 // Remove references from m_active_objects
1858 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1859 i != objects_to_remove.end(); i++)
1861 m_active_objects.remove(*i);
1872 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1873 ITextureSource *texturesource, IGameDef *gamedef):
1876 m_texturesource(texturesource),
1883 ClientEnvironment::~ClientEnvironment()
1885 // delete active objects
1886 for(core::map<u16, ClientActiveObject*>::Iterator
1887 i = m_active_objects.getIterator();
1888 i.atEnd()==false; i++)
1890 delete i.getNode()->getValue();
1897 void ClientEnvironment::addPlayer(Player *player)
1899 DSTACK(__FUNCTION_NAME);
1901 It is a failure if player is local and there already is a local
1904 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1906 Environment::addPlayer(player);
1909 LocalPlayer * ClientEnvironment::getLocalPlayer()
1911 for(core::list<Player*>::Iterator i = m_players.begin();
1912 i != m_players.end(); i++)
1914 Player *player = *i;
1915 if(player->isLocal())
1916 return (LocalPlayer*)player;
1921 void ClientEnvironment::step(float dtime)
1923 DSTACK(__FUNCTION_NAME);
1925 // Get some settings
1926 bool free_move = g_settings->getBool("free_move");
1927 bool footprints = g_settings->getBool("footprints");
1930 LocalPlayer *lplayer = getLocalPlayer();
1932 // collision info queue
1933 core::list<CollisionInfo> player_collisions;
1936 Get the speed the player is going
1938 bool is_climbing = lplayer->is_climbing;
1940 f32 player_speed = 0.001; // just some small value
1941 player_speed = lplayer->getSpeed().getLength();
1944 Maximum position increment
1946 //f32 position_max_increment = 0.05*BS;
1947 f32 position_max_increment = 0.1*BS;
1949 // Maximum time increment (for collision detection etc)
1950 // time = distance / speed
1951 f32 dtime_max_increment = position_max_increment / player_speed;
1953 // Maximum time increment is 10ms or lower
1954 if(dtime_max_increment > 0.01)
1955 dtime_max_increment = 0.01;
1957 // Don't allow overly huge dtime
1961 f32 dtime_downcount = dtime;
1964 Stuff that has a maximum time increment
1973 if(dtime_downcount > dtime_max_increment)
1975 dtime_part = dtime_max_increment;
1976 dtime_downcount -= dtime_part;
1980 dtime_part = dtime_downcount;
1982 Setting this to 0 (no -=dtime_part) disables an infinite loop
1983 when dtime_part is so small that dtime_downcount -= dtime_part
1986 dtime_downcount = 0;
1994 v3f lplayerpos = lplayer->getPosition();
1997 if(free_move == false && is_climbing == false)
2000 v3f speed = lplayer->getSpeed();
2001 if(lplayer->swimming_up == false)
2002 speed.Y -= 9.81 * BS * dtime_part * 2;
2005 if(lplayer->in_water_stable || lplayer->in_water)
2007 f32 max_down = 2.0*BS;
2008 if(speed.Y < -max_down) speed.Y = -max_down;
2011 if(speed.getLength() > max)
2013 speed = speed / speed.getLength() * max;
2017 lplayer->setSpeed(speed);
2022 This also does collision detection.
2024 lplayer->move(dtime_part, *m_map, position_max_increment,
2025 &player_collisions);
2028 while(dtime_downcount > 0.001);
2030 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2032 for(core::list<CollisionInfo>::Iterator
2033 i = player_collisions.begin();
2034 i != player_collisions.end(); i++)
2036 CollisionInfo &info = *i;
2037 if(info.t == COLLISION_FALL)
2039 //f32 tolerance = BS*10; // 2 without damage
2040 f32 tolerance = BS*12; // 3 without damage
2042 if(info.speed > tolerance)
2044 f32 damage_f = (info.speed - tolerance)/BS*factor;
2045 u16 damage = (u16)(damage_f+0.5);
2046 if(lplayer->hp > damage)
2047 lplayer->hp -= damage;
2051 ClientEnvEvent event;
2052 event.type = CEE_PLAYER_DAMAGE;
2053 event.player_damage.amount = damage;
2054 m_client_event_queue.push_back(event);
2060 A quick draft of lava damage
2062 if(m_lava_hurt_interval.step(dtime, 1.0))
2064 v3f pf = lplayer->getPosition();
2066 // Feet, middle and head
2067 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2068 MapNode n1 = m_map->getNodeNoEx(p1);
2069 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2070 MapNode n2 = m_map->getNodeNoEx(p2);
2071 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2072 MapNode n3 = m_map->getNodeNoEx(p2);
2074 u32 damage_per_second = 0;
2075 damage_per_second = MYMAX(damage_per_second,
2076 m_gamedef->ndef()->get(n1).damage_per_second);
2077 damage_per_second = MYMAX(damage_per_second,
2078 m_gamedef->ndef()->get(n2).damage_per_second);
2079 damage_per_second = MYMAX(damage_per_second,
2080 m_gamedef->ndef()->get(n3).damage_per_second);
2082 if(damage_per_second != 0)
2084 ClientEnvEvent event;
2085 event.type = CEE_PLAYER_DAMAGE;
2086 event.player_damage.amount = damage_per_second;
2087 m_client_event_queue.push_back(event);
2092 Stuff that can be done in an arbitarily large dtime
2094 for(core::list<Player*>::Iterator i = m_players.begin();
2095 i != m_players.end(); i++)
2097 Player *player = *i;
2098 v3f playerpos = player->getPosition();
2101 Handle non-local players
2103 if(player->isLocal() == false)
2106 player->move(dtime, *m_map, 100*BS);
2110 // Update lighting on all players on client
2111 u8 light = LIGHT_MAX;
2114 v3s16 p = player->getLightPosition();
2115 MapNode n = m_map->getNode(p);
2116 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2118 catch(InvalidPositionException &e) {}
2119 player->updateLight(light);
2122 Add footsteps to grass
2126 // Get node that is at BS/4 under player
2127 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2129 MapNode n = m_map->getNode(bottompos);
2130 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2132 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2133 m_map->setNode(bottompos, n);
2134 // Update mesh on client
2135 if(m_map->mapType() == MAPTYPE_CLIENT)
2137 v3s16 p_blocks = getNodeBlockPos(bottompos);
2138 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2139 //b->updateMesh(getDayNightRatio());
2140 b->setMeshExpired(true);
2144 catch(InvalidPositionException &e)
2151 Step active objects and update lighting of them
2154 for(core::map<u16, ClientActiveObject*>::Iterator
2155 i = m_active_objects.getIterator();
2156 i.atEnd()==false; i++)
2158 ClientActiveObject* obj = i.getNode()->getValue();
2160 obj->step(dtime, this);
2162 if(m_active_object_light_update_interval.step(dtime, 0.21))
2165 //u8 light = LIGHT_MAX;
2169 v3s16 p = obj->getLightPosition();
2170 MapNode n = m_map->getNode(p);
2171 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2173 catch(InvalidPositionException &e) {}
2174 obj->updateLight(light);
2179 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2181 m_map->updateMeshes(blockpos, getDayNightRatio());
2184 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2186 m_map->expireMeshes(only_daynight_diffed);
2189 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2191 core::map<u16, ClientActiveObject*>::Node *n;
2192 n = m_active_objects.find(id);
2195 return n->getValue();
2198 bool isFreeClientActiveObjectId(u16 id,
2199 core::map<u16, ClientActiveObject*> &objects)
2204 for(core::map<u16, ClientActiveObject*>::Iterator
2205 i = objects.getIterator();
2206 i.atEnd()==false; i++)
2208 if(i.getNode()->getKey() == id)
2214 u16 getFreeClientActiveObjectId(
2215 core::map<u16, ClientActiveObject*> &objects)
2220 if(isFreeClientActiveObjectId(new_id, objects))
2230 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2233 if(object->getId() == 0)
2235 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2238 infostream<<"ClientEnvironment::addActiveObject(): "
2239 <<"no free ids available"<<std::endl;
2243 object->setId(new_id);
2245 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2247 infostream<<"ClientEnvironment::addActiveObject(): "
2248 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2252 infostream<<"ClientEnvironment::addActiveObject(): "
2253 <<"added (id="<<object->getId()<<")"<<std::endl;
2254 m_active_objects.insert(object->getId(), object);
2255 // TODO: Make g_texturesource non-global
2256 object->addToScene(m_smgr, m_texturesource);
2257 return object->getId();
2260 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2261 const std::string &init_data)
2263 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2266 infostream<<"ClientEnvironment::addActiveObject(): "
2267 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2274 obj->initialize(init_data);
2276 addActiveObject(obj);
2279 void ClientEnvironment::removeActiveObject(u16 id)
2281 infostream<<"ClientEnvironment::removeActiveObject(): "
2282 <<"id="<<id<<std::endl;
2283 ClientActiveObject* obj = getActiveObject(id);
2286 infostream<<"ClientEnvironment::removeActiveObject(): "
2287 <<"id="<<id<<" not found"<<std::endl;
2290 obj->removeFromScene();
2292 m_active_objects.remove(id);
2295 void ClientEnvironment::processActiveObjectMessage(u16 id,
2296 const std::string &data)
2298 ClientActiveObject* obj = getActiveObject(id);
2301 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2302 <<" got message for id="<<id<<", which doesn't exist."
2306 obj->processMessage(data);
2310 Callbacks for activeobjects
2313 void ClientEnvironment::damageLocalPlayer(u8 damage)
2315 LocalPlayer *lplayer = getLocalPlayer();
2318 if(lplayer->hp > damage)
2319 lplayer->hp -= damage;
2323 ClientEnvEvent event;
2324 event.type = CEE_PLAYER_DAMAGE;
2325 event.player_damage.amount = damage;
2326 m_client_event_queue.push_back(event);
2330 Client likes to call these
2333 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2334 core::array<DistanceSortedActiveObject> &dest)
2336 for(core::map<u16, ClientActiveObject*>::Iterator
2337 i = m_active_objects.getIterator();
2338 i.atEnd()==false; i++)
2340 ClientActiveObject* obj = i.getNode()->getValue();
2342 f32 d = (obj->getPosition() - origin).getLength();
2347 DistanceSortedActiveObject dso(obj, d);
2349 dest.push_back(dso);
2353 ClientEnvEvent ClientEnvironment::getClientEvent()
2355 if(m_client_event_queue.size() == 0)
2357 ClientEnvEvent event;
2358 event.type = CEE_NONE;
2361 return m_client_event_queue.pop_front();
2364 #endif // #ifndef SERVER