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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
44 #include "daynightratio.h"
46 #include "util/serialize.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
58 Environment::~Environment()
61 for(core::list<Player*>::Iterator i = m_players.begin();
62 i != m_players.end(); i++)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(core::list<Player*>::Iterator i = m_players.begin();
90 i != m_players.end(); i++)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(core::list<Player*>::Iterator i = m_players.begin();
107 i != m_players.end(); i++)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(core::list<Player*>::Iterator i = m_players.begin();
119 i != m_players.end(); i++)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 core::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 core::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(core::list<Player*>::Iterator
153 i = connected_players.begin();
154 i != connected_players.end(); i++)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 core::list<Player*> Environment::getPlayers()
172 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 core::list<Player*> newlist;
175 for(core::list<Player*>::Iterator
176 i = m_players.begin();
177 i != m_players.end(); i++)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 void Environment::printPlayers(std::ostream &o)
195 o<<"Players in environment:"<<std::endl;
196 for(core::list<Player*>::Iterator i = m_players.begin();
197 i != m_players.end(); i++)
200 o<<"Player peer_id="<<player->peer_id<<std::endl;
204 u32 Environment::getDayNightRatio()
206 return time_to_daynight_ratio(m_time_of_day);
209 void Environment::stepTimeOfDay(float dtime)
211 m_time_counter += dtime;
212 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
213 u32 units = (u32)(m_time_counter*speed);
214 m_time_counter -= (f32)units / speed;
218 if(m_time_of_day + units >= 24000)
220 m_time_of_day = (m_time_of_day + units) % 24000;
222 m_time_of_day_f = (float)m_time_of_day / 24000.0;
225 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
226 if(m_time_of_day_f > 1.0)
227 m_time_of_day_f -= 1.0;
228 if(m_time_of_day_f < 0.0)
229 m_time_of_day_f += 1.0;
237 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
241 // Initialize timer to random value to spread processing
242 float itv = abm->getTriggerInterval();
243 itv = MYMAX(0.001, itv); // No less than 1ms
244 int minval = MYMAX(-0.51*itv, -60); // Clamp to
245 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
246 timer = myrand_range(minval, maxval);
253 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
256 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
257 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
258 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
265 void ActiveBlockList::update(core::list<v3s16> &active_positions,
267 core::map<v3s16, bool> &blocks_removed,
268 core::map<v3s16, bool> &blocks_added)
273 core::map<v3s16, bool> newlist;
274 for(core::list<v3s16>::Iterator i = active_positions.begin();
275 i != active_positions.end(); i++)
277 fillRadiusBlock(*i, radius, newlist);
281 Find out which blocks on the old list are not on the new list
283 // Go through old list
284 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
285 i.atEnd()==false; i++)
287 v3s16 p = i.getNode()->getKey();
288 // If not on new list, it's been removed
289 if(newlist.find(p) == NULL)
290 blocks_removed.insert(p, true);
294 Find out which blocks on the new list are not on the old list
296 // Go through new list
297 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
298 i.atEnd()==false; i++)
300 v3s16 p = i.getNode()->getKey();
301 // If not on old list, it's been added
302 if(m_list.find(p) == NULL)
303 blocks_added.insert(p, true);
310 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
311 i.atEnd()==false; i++)
313 v3s16 p = i.getNode()->getKey();
314 m_list.insert(p, true);
322 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
323 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
328 m_random_spawn_timer(3),
329 m_send_recommended_timer(0),
330 m_active_block_interval_overload_skip(0),
332 m_game_time_fraction_counter(0)
336 ServerEnvironment::~ServerEnvironment()
338 // Clear active block list.
339 // This makes the next one delete all active objects.
340 m_active_blocks.clear();
342 // Convert all objects to static and delete the active objects
343 deactivateFarObjects(true);
348 // Delete ActiveBlockModifiers
349 for(core::list<ABMWithState>::Iterator
350 i = m_abms.begin(); i != m_abms.end(); i++){
355 Map & ServerEnvironment::getMap()
360 ServerMap & ServerEnvironment::getServerMap()
366 void ServerEnvironment::serializePlayers(const std::string &savedir)
368 std::string players_path = savedir + "/players";
369 fs::CreateDir(players_path);
371 core::map<Player*, bool> saved_players;
373 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
374 for(u32 i=0; i<player_files.size(); i++)
376 if(player_files[i].dir)
379 // Full path to this file
380 std::string path = players_path + "/" + player_files[i].name;
382 //infostream<<"Checking player file "<<path<<std::endl;
384 // Load player to see what is its name
385 RemotePlayer testplayer(m_gamedef);
387 // Open file and deserialize
388 std::ifstream is(path.c_str(), std::ios_base::binary);
389 if(is.good() == false)
391 infostream<<"Failed to read "<<path<<std::endl;
394 testplayer.deSerialize(is);
397 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
399 // Search for the player
400 std::string playername = testplayer.getName();
401 Player *player = getPlayer(playername.c_str());
404 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
408 //infostream<<"Found matching player, overwriting."<<std::endl;
410 // OK, found. Save player there.
412 // Open file and serialize
413 std::ofstream os(path.c_str(), std::ios_base::binary);
414 if(os.good() == false)
416 infostream<<"Failed to overwrite "<<path<<std::endl;
419 player->serialize(os);
420 saved_players.insert(player, true);
424 for(core::list<Player*>::Iterator i = m_players.begin();
425 i != m_players.end(); i++)
428 if(saved_players.find(player) != NULL)
430 /*infostream<<"Player "<<player->getName()
431 <<" was already saved."<<std::endl;*/
434 std::string playername = player->getName();
435 // Don't save unnamed player
438 //infostream<<"Not saving unnamed player."<<std::endl;
444 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
445 playername = "player";
446 std::string path = players_path + "/" + playername;
448 for(u32 i=0; i<1000; i++)
450 if(fs::PathExists(path) == false)
455 path = players_path + "/" + playername + itos(i);
459 infostream<<"Didn't find free file for player"<<std::endl;
464 /*infostream<<"Saving player "<<player->getName()<<" to "
466 // Open file and serialize
467 std::ofstream os(path.c_str(), std::ios_base::binary);
468 if(os.good() == false)
470 infostream<<"Failed to overwrite "<<path<<std::endl;
473 player->serialize(os);
474 saved_players.insert(player, true);
478 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
481 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
483 std::string players_path = savedir + "/players";
485 core::map<Player*, bool> saved_players;
487 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
488 for(u32 i=0; i<player_files.size(); i++)
490 if(player_files[i].dir)
493 // Full path to this file
494 std::string path = players_path + "/" + player_files[i].name;
496 //infostream<<"Checking player file "<<path<<std::endl;
498 // Load player to see what is its name
499 RemotePlayer testplayer(m_gamedef);
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"Failed to read "<<path<<std::endl;
508 testplayer.deSerialize(is);
511 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
513 infostream<<"Not loading player with invalid name: "
514 <<testplayer.getName()<<std::endl;
517 /*infostream<<"Loaded test player with name "<<testplayer.getName()
520 // Search for the player
521 std::string playername = testplayer.getName();
522 Player *player = getPlayer(playername.c_str());
523 bool newplayer = false;
526 //infostream<<"Is a new player"<<std::endl;
527 player = new RemotePlayer(m_gamedef);
533 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
535 // Open file and deserialize
536 std::ifstream is(path.c_str(), std::ios_base::binary);
537 if(is.good() == false)
539 infostream<<"Failed to read "<<path<<std::endl;
542 player->deSerialize(is);
552 void ServerEnvironment::saveMeta(const std::string &savedir)
554 std::string path = savedir + "/env_meta.txt";
556 // Open file and serialize
557 std::ofstream os(path.c_str(), std::ios_base::binary);
558 if(os.good() == false)
560 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
562 throw SerializationError("Couldn't save env meta");
566 args.setU64("game_time", m_game_time);
567 args.setU64("time_of_day", getTimeOfDay());
572 void ServerEnvironment::loadMeta(const std::string &savedir)
574 std::string path = savedir + "/env_meta.txt";
576 // Open file and deserialize
577 std::ifstream is(path.c_str(), std::ios_base::binary);
578 if(is.good() == false)
580 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
582 throw SerializationError("Couldn't load env meta");
590 throw SerializationError
591 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
593 std::getline(is, line);
594 std::string trimmedline = trim(line);
595 if(trimmedline == "EnvArgsEnd")
597 args.parseConfigLine(line);
601 m_game_time = args.getU64("game_time");
602 }catch(SettingNotFoundException &e){
603 // Getting this is crucial, otherwise timestamps are useless
604 throw SerializationError("Couldn't load env meta game_time");
608 m_time_of_day = args.getU64("time_of_day");
609 }catch(SettingNotFoundException &e){
610 // This is not as important
611 m_time_of_day = 9000;
617 ActiveBlockModifier *abm;
619 std::set<content_t> required_neighbors;
625 ServerEnvironment *m_env;
626 std::map<content_t, std::list<ActiveABM> > m_aabms;
628 ABMHandler(core::list<ABMWithState> &abms,
629 float dtime_s, ServerEnvironment *env,
635 INodeDefManager *ndef = env->getGameDef()->ndef();
636 for(core::list<ABMWithState>::Iterator
637 i = abms.begin(); i != abms.end(); i++){
638 ActiveBlockModifier *abm = i->abm;
639 float trigger_interval = abm->getTriggerInterval();
640 if(trigger_interval < 0.001)
641 trigger_interval = 0.001;
642 float actual_interval = dtime_s;
645 if(i->timer < trigger_interval)
647 i->timer -= trigger_interval;
648 actual_interval = trigger_interval;
650 float intervals = actual_interval / trigger_interval;
653 float chance = abm->getTriggerChance();
658 aabm.chance = chance / intervals;
662 std::set<std::string> required_neighbors_s
663 = abm->getRequiredNeighbors();
664 for(std::set<std::string>::iterator
665 i = required_neighbors_s.begin();
666 i != required_neighbors_s.end(); i++)
668 ndef->getIds(*i, aabm.required_neighbors);
671 std::set<std::string> contents_s = abm->getTriggerContents();
672 for(std::set<std::string>::iterator
673 i = contents_s.begin(); i != contents_s.end(); i++)
675 std::set<content_t> ids;
676 ndef->getIds(*i, ids);
677 for(std::set<content_t>::const_iterator k = ids.begin();
681 std::map<content_t, std::list<ActiveABM> >::iterator j;
683 if(j == m_aabms.end()){
684 std::list<ActiveABM> aabmlist;
685 m_aabms[c] = aabmlist;
688 j->second.push_back(aabm);
693 void apply(MapBlock *block)
698 ServerMap *map = &m_env->getServerMap();
701 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
702 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
703 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
705 MapNode n = block->getNodeNoEx(p0);
706 content_t c = n.getContent();
707 v3s16 p = p0 + block->getPosRelative();
709 std::map<content_t, std::list<ActiveABM> >::iterator j;
711 if(j == m_aabms.end())
714 for(std::list<ActiveABM>::iterator
715 i = j->second.begin(); i != j->second.end(); i++)
717 if(myrand() % i->chance != 0)
721 if(!i->required_neighbors.empty())
724 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
725 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
726 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
730 MapNode n = map->getNodeNoEx(p1);
731 content_t c = n.getContent();
732 std::set<content_t>::const_iterator k;
733 k = i->required_neighbors.find(c);
734 if(k != i->required_neighbors.end()){
738 // No required neighbor found
743 // Find out how many objects the block contains
744 u32 active_object_count = block->m_static_objects.m_active.size();
745 // Find out how many objects this and all the neighbors contain
746 u32 active_object_count_wider = 0;
747 u32 wider_unknown_count = 0;
748 for(s16 x=-1; x<=1; x++)
749 for(s16 y=-1; y<=1; y++)
750 for(s16 z=-1; z<=1; z++)
752 MapBlock *block2 = map->getBlockNoCreateNoEx(
753 block->getPos() + v3s16(x,y,z));
755 wider_unknown_count = 0;
758 active_object_count_wider +=
759 block2->m_static_objects.m_active.size()
760 + block2->m_static_objects.m_stored.size();
763 u32 wider_known_count = 3*3*3 - wider_unknown_count;
764 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
766 // Call all the trigger variations
767 i->abm->trigger(m_env, p, n);
768 i->abm->trigger(m_env, p, n,
769 active_object_count, active_object_count_wider);
775 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
777 // Get time difference
779 u32 stamp = block->getTimestamp();
780 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
781 dtime_s = m_game_time - block->getTimestamp();
782 dtime_s += additional_dtime;
784 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
785 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
787 // Set current time as timestamp
788 block->setTimestampNoChangedFlag(m_game_time);
790 /*infostream<<"ServerEnvironment::activateBlock(): block is "
791 <<dtime_s<<" seconds old."<<std::endl;*/
793 // Activate stored objects
794 activateObjects(block, dtime_s);
797 std::map<v3s16, NodeTimer> elapsed_timers =
798 block->m_node_timers.step((float)dtime_s);
799 if(!elapsed_timers.empty()){
801 for(std::map<v3s16, NodeTimer>::iterator
802 i = elapsed_timers.begin();
803 i != elapsed_timers.end(); i++){
804 n = block->getNodeNoEx(i->first);
805 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
806 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
810 /* Handle ActiveBlockModifiers */
811 ABMHandler abmhandler(m_abms, dtime_s, this, false);
812 abmhandler.apply(block);
815 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
817 m_abms.push_back(ABMWithState(abm));
820 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
822 std::set<u16> objects;
823 for(core::map<u16, ServerActiveObject*>::Iterator
824 i = m_active_objects.getIterator();
825 i.atEnd()==false; i++)
827 ServerActiveObject* obj = i.getNode()->getValue();
828 u16 id = i.getNode()->getKey();
829 v3f objectpos = obj->getBasePosition();
830 if(objectpos.getDistanceFrom(pos) > radius)
837 void ServerEnvironment::clearAllObjects()
839 infostream<<"ServerEnvironment::clearAllObjects(): "
840 <<"Removing all active objects"<<std::endl;
841 core::list<u16> objects_to_remove;
842 for(core::map<u16, ServerActiveObject*>::Iterator
843 i = m_active_objects.getIterator();
844 i.atEnd()==false; i++)
846 ServerActiveObject* obj = i.getNode()->getValue();
847 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
849 u16 id = i.getNode()->getKey();
850 v3f objectpos = obj->getBasePosition();
851 // Delete static object if block is loaded
852 if(obj->m_static_exists){
853 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
855 block->m_static_objects.remove(id);
856 block->raiseModified(MOD_STATE_WRITE_NEEDED,
858 obj->m_static_exists = false;
861 // If known by some client, don't delete immediately
862 if(obj->m_known_by_count > 0){
863 obj->m_pending_deactivation = true;
864 obj->m_removed = true;
868 // Tell the object about removal
869 obj->removingFromEnvironment();
870 // Deregister in scripting api
871 scriptapi_rm_object_reference(m_lua, obj);
873 // Delete active object
874 if(obj->environmentDeletes())
876 // Id to be removed from m_active_objects
877 objects_to_remove.push_back(id);
879 // Remove references from m_active_objects
880 for(core::list<u16>::Iterator i = objects_to_remove.begin();
881 i != objects_to_remove.end(); i++)
883 m_active_objects.remove(*i);
886 core::list<v3s16> loadable_blocks;
887 infostream<<"ServerEnvironment::clearAllObjects(): "
888 <<"Listing all loadable blocks"<<std::endl;
889 m_map->listAllLoadableBlocks(loadable_blocks);
890 infostream<<"ServerEnvironment::clearAllObjects(): "
891 <<"Done listing all loadable blocks: "
892 <<loadable_blocks.size()
893 <<", now clearing"<<std::endl;
894 u32 report_interval = loadable_blocks.size() / 10;
895 u32 num_blocks_checked = 0;
896 u32 num_blocks_cleared = 0;
897 u32 num_objs_cleared = 0;
898 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
899 i != loadable_blocks.end(); i++)
902 MapBlock *block = m_map->emergeBlock(p, false);
904 errorstream<<"ServerEnvironment::clearAllObjects(): "
905 <<"Failed to emerge block "<<PP(p)<<std::endl;
908 u32 num_stored = block->m_static_objects.m_stored.size();
909 u32 num_active = block->m_static_objects.m_active.size();
910 if(num_stored != 0 || num_active != 0){
911 block->m_static_objects.m_stored.clear();
912 block->m_static_objects.m_active.clear();
913 block->raiseModified(MOD_STATE_WRITE_NEEDED,
915 num_objs_cleared += num_stored + num_active;
916 num_blocks_cleared++;
918 num_blocks_checked++;
920 if(num_blocks_checked % report_interval == 0){
921 float percent = 100.0 * (float)num_blocks_checked /
922 loadable_blocks.size();
923 infostream<<"ServerEnvironment::clearAllObjects(): "
924 <<"Cleared "<<num_objs_cleared<<" objects"
925 <<" in "<<num_blocks_cleared<<" blocks ("
926 <<percent<<"%)"<<std::endl;
929 infostream<<"ServerEnvironment::clearAllObjects(): "
930 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
931 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
934 void ServerEnvironment::step(float dtime)
936 DSTACK(__FUNCTION_NAME);
938 //TimeTaker timer("ServerEnv step");
940 /* Step time of day */
941 stepTimeOfDay(dtime);
947 m_game_time_fraction_counter += dtime;
948 u32 inc_i = (u32)m_game_time_fraction_counter;
949 m_game_time += inc_i;
950 m_game_time_fraction_counter -= (float)inc_i;
957 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
958 for(core::list<Player*>::Iterator i = m_players.begin();
959 i != m_players.end(); i++)
963 // Ignore disconnected players
964 if(player->peer_id == 0)
967 v3f playerpos = player->getPosition();
970 player->move(dtime, *m_map, 100*BS);
975 Manage active block list
977 if(m_active_blocks_management_interval.step(dtime, 2.0))
979 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
981 Get player block positions
983 core::list<v3s16> players_blockpos;
984 for(core::list<Player*>::Iterator
985 i = m_players.begin();
986 i != m_players.end(); i++)
989 // Ignore disconnected players
990 if(player->peer_id == 0)
992 v3s16 blockpos = getNodeBlockPos(
993 floatToInt(player->getPosition(), BS));
994 players_blockpos.push_back(blockpos);
998 Update list of active blocks, collecting changes
1000 const s16 active_block_range = g_settings->getS16("active_block_range");
1001 core::map<v3s16, bool> blocks_removed;
1002 core::map<v3s16, bool> blocks_added;
1003 m_active_blocks.update(players_blockpos, active_block_range,
1004 blocks_removed, blocks_added);
1007 Handle removed blocks
1010 // Convert active objects that are no more in active blocks to static
1011 deactivateFarObjects(false);
1013 for(core::map<v3s16, bool>::Iterator
1014 i = blocks_removed.getIterator();
1015 i.atEnd()==false; i++)
1017 v3s16 p = i.getNode()->getKey();
1019 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1020 <<") became inactive"<<std::endl;*/
1022 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1026 // Set current time as timestamp (and let it set ChangedFlag)
1027 block->setTimestamp(m_game_time);
1034 for(core::map<v3s16, bool>::Iterator
1035 i = blocks_added.getIterator();
1036 i.atEnd()==false; i++)
1038 v3s16 p = i.getNode()->getKey();
1040 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1041 <<") became active"<<std::endl;*/
1043 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1045 // Block needs to be fetched first
1046 m_emerger->queueBlockEmerge(p, false);
1047 m_active_blocks.m_list.remove(p);
1051 activateBlock(block);
1056 Mess around in active blocks
1058 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1060 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1064 for(core::map<v3s16, bool>::Iterator
1065 i = m_active_blocks.m_list.getIterator();
1066 i.atEnd()==false; i++)
1068 v3s16 p = i.getNode()->getKey();
1070 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1071 <<") being handled"<<std::endl;*/
1073 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1077 // Reset block usage timer
1078 block->resetUsageTimer();
1080 // Set current time as timestamp
1081 block->setTimestampNoChangedFlag(m_game_time);
1082 // If time has changed much from the one on disk,
1083 // set block to be saved when it is unloaded
1084 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1085 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1086 "Timestamp older than 60s (step)");
1089 std::map<v3s16, NodeTimer> elapsed_timers =
1090 block->m_node_timers.step((float)dtime);
1091 if(!elapsed_timers.empty()){
1093 for(std::map<v3s16, NodeTimer>::iterator
1094 i = elapsed_timers.begin();
1095 i != elapsed_timers.end(); i++){
1096 n = block->getNodeNoEx(i->first);
1097 p = i->first + block->getPosRelative();
1098 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1099 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1105 const float abm_interval = 1.0;
1106 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1108 if(m_active_block_interval_overload_skip > 0){
1109 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1110 m_active_block_interval_overload_skip--;
1113 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1114 TimeTaker timer("modify in active blocks");
1116 // Initialize handling of ActiveBlockModifiers
1117 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1119 for(core::map<v3s16, bool>::Iterator
1120 i = m_active_blocks.m_list.getIterator();
1121 i.atEnd()==false; i++)
1123 v3s16 p = i.getNode()->getKey();
1125 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1126 <<") being handled"<<std::endl;*/
1128 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1132 // Set current time as timestamp
1133 block->setTimestampNoChangedFlag(m_game_time);
1135 /* Handle ActiveBlockModifiers */
1136 abmhandler.apply(block);
1139 u32 time_ms = timer.stop(true);
1140 u32 max_time_ms = 200;
1141 if(time_ms > max_time_ms){
1142 infostream<<"WARNING: active block modifiers took "
1143 <<time_ms<<"ms (longer than "
1144 <<max_time_ms<<"ms)"<<std::endl;
1145 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1150 Step script environment (run global on_step())
1152 scriptapi_environment_step(m_lua, dtime);
1158 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1159 //TimeTaker timer("Step active objects");
1161 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1163 // This helps the objects to send data at the same time
1164 bool send_recommended = false;
1165 m_send_recommended_timer += dtime;
1166 if(m_send_recommended_timer > getSendRecommendedInterval())
1168 m_send_recommended_timer -= getSendRecommendedInterval();
1169 send_recommended = true;
1172 for(core::map<u16, ServerActiveObject*>::Iterator
1173 i = m_active_objects.getIterator();
1174 i.atEnd()==false; i++)
1176 ServerActiveObject* obj = i.getNode()->getValue();
1177 // Remove non-peaceful mobs on peaceful mode
1178 if(g_settings->getBool("only_peaceful_mobs")){
1179 if(!obj->isPeaceful())
1180 obj->m_removed = true;
1182 // Don't step if is to be removed or stored statically
1183 if(obj->m_removed || obj->m_pending_deactivation)
1186 obj->step(dtime, send_recommended);
1187 // Read messages from object
1188 while(obj->m_messages_out.size() > 0)
1190 m_active_object_messages.push_back(
1191 obj->m_messages_out.pop_front());
1197 Manage active objects
1199 if(m_object_management_interval.step(dtime, 0.5))
1201 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1203 Remove objects that satisfy (m_removed && m_known_by_count==0)
1205 removeRemovedObjects();
1209 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1211 core::map<u16, ServerActiveObject*>::Node *n;
1212 n = m_active_objects.find(id);
1215 return n->getValue();
1218 bool isFreeServerActiveObjectId(u16 id,
1219 core::map<u16, ServerActiveObject*> &objects)
1224 for(core::map<u16, ServerActiveObject*>::Iterator
1225 i = objects.getIterator();
1226 i.atEnd()==false; i++)
1228 if(i.getNode()->getKey() == id)
1234 u16 getFreeServerActiveObjectId(
1235 core::map<u16, ServerActiveObject*> &objects)
1240 if(isFreeServerActiveObjectId(new_id, objects))
1250 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1253 u16 id = addActiveObjectRaw(object, true, 0);
1257 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1261 v3f objectpos = obj->getBasePosition();
1263 // The block in which the object resides in
1264 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1267 Update the static data
1270 // Create new static object
1271 std::string staticdata = obj->getStaticData();
1272 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1273 // Add to the block where the object is located in
1274 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1275 // Get or generate the block
1276 MapBlock *block = m_map->emergeBlock(blockpos);
1278 bool succeeded = false;
1282 block->m_static_objects.insert(0, s_obj);
1283 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1284 "addActiveObjectAsStatic");
1288 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1289 <<"Could not find or generate "
1290 <<"a block for storing static object"<<std::endl;
1294 if(obj->environmentDeletes())
1301 Finds out what new objects have been added to
1302 inside a radius around a position
1304 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1305 core::map<u16, bool> ¤t_objects,
1306 core::map<u16, bool> &added_objects)
1308 v3f pos_f = intToFloat(pos, BS);
1309 f32 radius_f = radius * BS;
1311 Go through the object list,
1312 - discard m_removed objects,
1313 - discard objects that are too far away,
1314 - discard objects that are found in current_objects.
1315 - add remaining objects to added_objects
1317 for(core::map<u16, ServerActiveObject*>::Iterator
1318 i = m_active_objects.getIterator();
1319 i.atEnd()==false; i++)
1321 u16 id = i.getNode()->getKey();
1323 ServerActiveObject *object = i.getNode()->getValue();
1326 // Discard if removed
1327 if(object->m_removed)
1329 if(object->unlimitedTransferDistance() == false){
1330 // Discard if too far
1331 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1332 if(distance_f > radius_f)
1335 // Discard if already on current_objects
1336 core::map<u16, bool>::Node *n;
1337 n = current_objects.find(id);
1340 // Add to added_objects
1341 added_objects.insert(id, false);
1346 Finds out what objects have been removed from
1347 inside a radius around a position
1349 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1350 core::map<u16, bool> ¤t_objects,
1351 core::map<u16, bool> &removed_objects)
1353 v3f pos_f = intToFloat(pos, BS);
1354 f32 radius_f = radius * BS;
1356 Go through current_objects; object is removed if:
1357 - object is not found in m_active_objects (this is actually an
1358 error condition; objects should be set m_removed=true and removed
1359 only after all clients have been informed about removal), or
1360 - object has m_removed=true, or
1361 - object is too far away
1363 for(core::map<u16, bool>::Iterator
1364 i = current_objects.getIterator();
1365 i.atEnd()==false; i++)
1367 u16 id = i.getNode()->getKey();
1368 ServerActiveObject *object = getActiveObject(id);
1371 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1372 <<" object in current_objects is NULL"<<std::endl;
1373 removed_objects.insert(id, false);
1377 if(object->m_removed)
1379 removed_objects.insert(id, false);
1383 // If transfer distance is unlimited, don't remove
1384 if(object->unlimitedTransferDistance())
1387 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1389 if(distance_f >= radius_f)
1391 removed_objects.insert(id, false);
1399 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1401 if(m_active_object_messages.size() == 0)
1402 return ActiveObjectMessage(0);
1404 return m_active_object_messages.pop_front();
1408 ************ Private methods *************
1411 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1412 bool set_changed, u32 dtime_s)
1415 if(object->getId() == 0){
1416 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1419 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1420 <<"no free ids available"<<std::endl;
1421 if(object->environmentDeletes())
1425 object->setId(new_id);
1428 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1429 <<"supplied with id "<<object->getId()<<std::endl;
1431 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1433 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1434 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1435 if(object->environmentDeletes())
1439 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1440 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1442 m_active_objects.insert(object->getId(), object);
1444 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1445 <<"Added id="<<object->getId()<<"; there are now "
1446 <<m_active_objects.size()<<" active objects."
1449 // Register reference in scripting api (must be done before post-init)
1450 scriptapi_add_object_reference(m_lua, object);
1451 // Post-initialize object
1452 object->addedToEnvironment(dtime_s);
1454 // Add static data to block
1455 if(object->isStaticAllowed())
1457 // Add static object to active static list of the block
1458 v3f objectpos = object->getBasePosition();
1459 std::string staticdata = object->getStaticData();
1460 StaticObject s_obj(object->getType(), objectpos, staticdata);
1461 // Add to the block where the object is located in
1462 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1463 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1466 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1467 object->m_static_exists = true;
1468 object->m_static_block = blockpos;
1471 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1472 "addActiveObjectRaw");
1475 v3s16 p = floatToInt(objectpos, BS);
1476 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477 <<"could not find block for storing id="<<object->getId()
1478 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1482 return object->getId();
1486 Remove objects that satisfy (m_removed && m_known_by_count==0)
1488 void ServerEnvironment::removeRemovedObjects()
1490 core::list<u16> objects_to_remove;
1491 for(core::map<u16, ServerActiveObject*>::Iterator
1492 i = m_active_objects.getIterator();
1493 i.atEnd()==false; i++)
1495 u16 id = i.getNode()->getKey();
1496 ServerActiveObject* obj = i.getNode()->getValue();
1497 // This shouldn't happen but check it
1500 infostream<<"NULL object found in ServerEnvironment"
1501 <<" while finding removed objects. id="<<id<<std::endl;
1502 // Id to be removed from m_active_objects
1503 objects_to_remove.push_back(id);
1508 We will delete objects that are marked as removed or thatare
1509 waiting for deletion after deactivation
1511 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1515 Delete static data from block if is marked as removed
1517 if(obj->m_static_exists && obj->m_removed)
1519 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1522 block->m_static_objects.remove(id);
1523 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1524 "removeRemovedObjects");
1525 obj->m_static_exists = false;
1529 // If m_known_by_count > 0, don't actually remove.
1530 if(obj->m_known_by_count > 0)
1533 // Tell the object about removal
1534 obj->removingFromEnvironment();
1535 // Deregister in scripting api
1536 scriptapi_rm_object_reference(m_lua, obj);
1539 if(obj->environmentDeletes())
1541 // Id to be removed from m_active_objects
1542 objects_to_remove.push_back(id);
1544 // Remove references from m_active_objects
1545 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1546 i != objects_to_remove.end(); i++)
1548 m_active_objects.remove(*i);
1552 static void print_hexdump(std::ostream &o, const std::string &data)
1554 const int linelength = 16;
1555 for(int l=0; ; l++){
1556 int i0 = linelength * l;
1557 bool at_end = false;
1558 int thislinelength = linelength;
1559 if(i0 + thislinelength > (int)data.size()){
1560 thislinelength = data.size() - i0;
1563 for(int di=0; di<linelength; di++){
1566 if(di<thislinelength)
1567 snprintf(buf, 4, "%.2x ", data[i]);
1569 snprintf(buf, 4, " ");
1573 for(int di=0; di<thislinelength; di++){
1587 Convert stored objects from blocks near the players to active.
1589 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1593 // Ignore if no stored objects (to not set changed flag)
1594 if(block->m_static_objects.m_stored.size() == 0)
1596 verbosestream<<"ServerEnvironment::activateObjects(): "
1597 <<"activating objects of block "<<PP(block->getPos())
1598 <<" ("<<block->m_static_objects.m_stored.size()
1599 <<" objects)"<<std::endl;
1600 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1602 errorstream<<"suspiciously large amount of objects detected: "
1603 <<block->m_static_objects.m_stored.size()<<" in "
1604 <<PP(block->getPos())
1605 <<"; removing all of them."<<std::endl;
1606 // Clear stored list
1607 block->m_static_objects.m_stored.clear();
1608 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1609 "stored list cleared in activateObjects due to "
1610 "large amount of objects");
1613 // A list for objects that couldn't be converted to active for some
1614 // reason. They will be stored back.
1615 core::list<StaticObject> new_stored;
1616 // Loop through stored static objects
1617 for(core::list<StaticObject>::Iterator
1618 i = block->m_static_objects.m_stored.begin();
1619 i != block->m_static_objects.m_stored.end(); i++)
1621 /*infostream<<"Server: Creating an active object from "
1622 <<"static data"<<std::endl;*/
1623 StaticObject &s_obj = *i;
1624 // Create an active object from the data
1625 ServerActiveObject *obj = ServerActiveObject::create
1626 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1627 // If couldn't create object, store static data back.
1630 errorstream<<"ServerEnvironment::activateObjects(): "
1631 <<"failed to create active object from static object "
1632 <<"in block "<<PP(s_obj.pos/BS)
1633 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1634 print_hexdump(verbosestream, s_obj.data);
1636 new_stored.push_back(s_obj);
1639 verbosestream<<"ServerEnvironment::activateObjects(): "
1640 <<"activated static object pos="<<PP(s_obj.pos/BS)
1641 <<" type="<<(int)s_obj.type<<std::endl;
1642 // This will also add the object to the active static list
1643 addActiveObjectRaw(obj, false, dtime_s);
1645 // Clear stored list
1646 block->m_static_objects.m_stored.clear();
1647 // Add leftover failed stuff to stored list
1648 for(core::list<StaticObject>::Iterator
1649 i = new_stored.begin();
1650 i != new_stored.end(); i++)
1652 StaticObject &s_obj = *i;
1653 block->m_static_objects.m_stored.push_back(s_obj);
1656 Note: Block hasn't really been modified here.
1657 The objects have just been activated and moved from the stored
1658 static list to the active static list.
1659 As such, the block is essentially the same.
1660 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1661 Otherwise there would be a huge amount of unnecessary I/O.
1666 Convert objects that are not standing inside active blocks to static.
1668 If m_known_by_count != 0, active object is not deleted, but static
1669 data is still updated.
1671 If force_delete is set, active object is deleted nevertheless. It
1672 shall only be set so in the destructor of the environment.
1674 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1676 core::list<u16> objects_to_remove;
1677 for(core::map<u16, ServerActiveObject*>::Iterator
1678 i = m_active_objects.getIterator();
1679 i.atEnd()==false; i++)
1681 ServerActiveObject* obj = i.getNode()->getValue();
1684 // Do not deactivate if static data creation not allowed
1685 if(!force_delete && !obj->isStaticAllowed())
1688 // If pending deactivation, let removeRemovedObjects() do it
1689 if(!force_delete && obj->m_pending_deactivation)
1692 u16 id = i.getNode()->getKey();
1693 v3f objectpos = obj->getBasePosition();
1695 // The block in which the object resides in
1696 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1698 // If block is active, don't remove
1699 if(!force_delete && m_active_blocks.contains(blockpos_o))
1702 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1703 <<"deactivating object id="<<id<<" on inactive block "
1704 <<PP(blockpos_o)<<std::endl;
1706 // If known by some client, don't immediately delete.
1707 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1710 Update the static data
1713 if(obj->isStaticAllowed())
1715 // Create new static object
1716 std::string staticdata_new = obj->getStaticData();
1717 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1719 bool stays_in_same_block = false;
1720 bool data_changed = true;
1722 if(obj->m_static_exists){
1723 if(obj->m_static_block == blockpos_o)
1724 stays_in_same_block = true;
1726 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1728 core::map<u16, StaticObject>::Node *n =
1729 block->m_static_objects.m_active.find(id);
1731 StaticObject static_old = n->getValue();
1733 float save_movem = obj->getMinimumSavedMovement();
1735 if(static_old.data == staticdata_new &&
1736 (static_old.pos - objectpos).getLength() < save_movem)
1737 data_changed = false;
1739 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1740 <<"id="<<id<<" m_static_exists=true but "
1741 <<"static data doesn't actually exist in "
1742 <<PP(obj->m_static_block)<<std::endl;
1746 bool shall_be_written = (!stays_in_same_block || data_changed);
1748 // Delete old static object
1749 if(obj->m_static_exists)
1751 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1754 block->m_static_objects.remove(id);
1755 obj->m_static_exists = false;
1756 // Only mark block as modified if data changed considerably
1757 if(shall_be_written)
1758 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1759 "deactivateFarObjects: Static data "
1760 "changed considerably");
1764 // Add to the block where the object is located in
1765 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1766 // Get or generate the block
1767 MapBlock *block = NULL;
1769 block = m_map->emergeBlock(blockpos);
1770 } catch(InvalidPositionException &e){
1771 // Handled via NULL pointer
1776 if(block->m_static_objects.m_stored.size() >= 49){
1777 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1778 <<" statically but block "<<PP(blockpos)
1779 <<" already contains "
1780 <<block->m_static_objects.m_stored.size()
1781 <<" (over 49) objects."
1782 <<" Forcing delete."<<std::endl;
1783 force_delete = true;
1785 u16 new_id = pending_delete ? id : 0;
1786 // If static counterpart already exists, remove it first.
1787 // This shouldn't happen, but happens rarely for some
1788 // unknown reason. Unsuccessful attempts have been made to
1789 // find said reason.
1790 if(new_id && block->m_static_objects.m_active.find(new_id)){
1791 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1793 block->m_static_objects.remove(new_id);
1795 block->m_static_objects.insert(new_id, s_obj);
1797 // Only mark block as modified if data changed considerably
1798 if(shall_be_written)
1799 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1800 "deactivateFarObjects: Static data "
1801 "changed considerably");
1803 obj->m_static_exists = true;
1804 obj->m_static_block = block->getPos();
1809 v3s16 p = floatToInt(objectpos, BS);
1810 errorstream<<"ServerEnv: Could not find or generate "
1811 <<"a block for storing id="<<obj->getId()
1812 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1819 If known by some client, set pending deactivation.
1820 Otherwise delete it immediately.
1823 if(pending_delete && !force_delete)
1825 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1826 <<"object id="<<id<<" is known by clients"
1827 <<"; not deleting yet"<<std::endl;
1829 obj->m_pending_deactivation = true;
1833 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1834 <<"object id="<<id<<" is not known by clients"
1835 <<"; deleting"<<std::endl;
1837 // Tell the object about removal
1838 obj->removingFromEnvironment();
1839 // Deregister in scripting api
1840 scriptapi_rm_object_reference(m_lua, obj);
1842 // Delete active object
1843 if(obj->environmentDeletes())
1845 // Id to be removed from m_active_objects
1846 objects_to_remove.push_back(id);
1849 // Remove references from m_active_objects
1850 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1851 i != objects_to_remove.end(); i++)
1853 m_active_objects.remove(*i);
1860 #include "clientsimpleobject.h"
1866 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1867 ITextureSource *texturesource, IGameDef *gamedef,
1868 IrrlichtDevice *irr):
1871 m_texturesource(texturesource),
1877 ClientEnvironment::~ClientEnvironment()
1879 // delete active objects
1880 for(core::map<u16, ClientActiveObject*>::Iterator
1881 i = m_active_objects.getIterator();
1882 i.atEnd()==false; i++)
1884 delete i.getNode()->getValue();
1887 for(core::list<ClientSimpleObject*>::Iterator
1888 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1897 Map & ClientEnvironment::getMap()
1902 ClientMap & ClientEnvironment::getClientMap()
1907 void ClientEnvironment::addPlayer(Player *player)
1909 DSTACK(__FUNCTION_NAME);
1911 It is a failure if player is local and there already is a local
1914 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1916 Environment::addPlayer(player);
1919 LocalPlayer * ClientEnvironment::getLocalPlayer()
1921 for(core::list<Player*>::Iterator i = m_players.begin();
1922 i != m_players.end(); i++)
1924 Player *player = *i;
1925 if(player->isLocal())
1926 return (LocalPlayer*)player;
1931 void ClientEnvironment::step(float dtime)
1933 DSTACK(__FUNCTION_NAME);
1935 /* Step time of day */
1936 stepTimeOfDay(dtime);
1938 // Get some settings
1939 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1940 bool free_move = fly_allowed && g_settings->getBool("free_move");
1943 LocalPlayer *lplayer = getLocalPlayer();
1945 // collision info queue
1946 core::list<CollisionInfo> player_collisions;
1949 Get the speed the player is going
1951 bool is_climbing = lplayer->is_climbing;
1953 f32 player_speed = lplayer->getSpeed().getLength();
1956 Maximum position increment
1958 //f32 position_max_increment = 0.05*BS;
1959 f32 position_max_increment = 0.1*BS;
1961 // Maximum time increment (for collision detection etc)
1962 // time = distance / speed
1963 f32 dtime_max_increment = 1;
1964 if(player_speed > 0.001)
1965 dtime_max_increment = position_max_increment / player_speed;
1967 // Maximum time increment is 10ms or lower
1968 if(dtime_max_increment > 0.01)
1969 dtime_max_increment = 0.01;
1971 // Don't allow overly huge dtime
1975 f32 dtime_downcount = dtime;
1978 Stuff that has a maximum time increment
1987 if(dtime_downcount > dtime_max_increment)
1989 dtime_part = dtime_max_increment;
1990 dtime_downcount -= dtime_part;
1994 dtime_part = dtime_downcount;
1996 Setting this to 0 (no -=dtime_part) disables an infinite loop
1997 when dtime_part is so small that dtime_downcount -= dtime_part
2000 dtime_downcount = 0;
2008 v3f lplayerpos = lplayer->getPosition();
2011 if(free_move == false && is_climbing == false)
2014 v3f speed = lplayer->getSpeed();
2015 if(lplayer->swimming_up == false)
2016 speed.Y -= 9.81 * BS * dtime_part * 2;
2019 if(lplayer->in_water_stable || lplayer->in_water)
2021 f32 max_down = 2.0*BS;
2022 if(speed.Y < -max_down) speed.Y = -max_down;
2025 if(speed.getLength() > max)
2027 speed = speed / speed.getLength() * max;
2031 lplayer->setSpeed(speed);
2036 This also does collision detection.
2038 lplayer->move(dtime_part, *m_map, position_max_increment,
2039 &player_collisions);
2042 while(dtime_downcount > 0.001);
2044 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2046 for(core::list<CollisionInfo>::Iterator
2047 i = player_collisions.begin();
2048 i != player_collisions.end(); i++)
2050 CollisionInfo &info = *i;
2051 v3f speed_diff = info.new_speed - info.old_speed;;
2052 // Handle only fall damage
2053 // (because otherwise walking against something in fast_move kills you)
2054 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2056 // Get rid of other components
2059 f32 pre_factor = 1; // 1 hp per node/s
2060 f32 tolerance = BS*14; // 5 without damage
2061 f32 post_factor = 1; // 1 hp per node/s
2062 if(info.type == COLLISION_NODE)
2064 const ContentFeatures &f = m_gamedef->ndef()->
2065 get(m_map->getNodeNoEx(info.node_p));
2066 // Determine fall damage multiplier
2067 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2068 pre_factor = 1.0 + (float)addp/100.0;
2070 float speed = pre_factor * speed_diff.getLength();
2071 if(speed > tolerance)
2073 f32 damage_f = (speed - tolerance)/BS * post_factor;
2074 u16 damage = (u16)(damage_f+0.5);
2076 damageLocalPlayer(damage, true);
2081 A quick draft of lava damage
2083 if(m_lava_hurt_interval.step(dtime, 1.0))
2085 v3f pf = lplayer->getPosition();
2087 // Feet, middle and head
2088 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2089 MapNode n1 = m_map->getNodeNoEx(p1);
2090 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2091 MapNode n2 = m_map->getNodeNoEx(p2);
2092 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2093 MapNode n3 = m_map->getNodeNoEx(p2);
2095 u32 damage_per_second = 0;
2096 damage_per_second = MYMAX(damage_per_second,
2097 m_gamedef->ndef()->get(n1).damage_per_second);
2098 damage_per_second = MYMAX(damage_per_second,
2099 m_gamedef->ndef()->get(n2).damage_per_second);
2100 damage_per_second = MYMAX(damage_per_second,
2101 m_gamedef->ndef()->get(n3).damage_per_second);
2103 if(damage_per_second != 0)
2105 damageLocalPlayer(damage_per_second, true);
2110 Stuff that can be done in an arbitarily large dtime
2112 for(core::list<Player*>::Iterator i = m_players.begin();
2113 i != m_players.end(); i++)
2115 Player *player = *i;
2116 v3f playerpos = player->getPosition();
2119 Handle non-local players
2121 if(player->isLocal() == false)
2124 player->move(dtime, *m_map, 100*BS);
2128 // Update lighting on all players on client
2129 u8 light = LIGHT_MAX;
2132 v3s16 p = player->getLightPosition();
2133 MapNode n = m_map->getNode(p);
2134 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2136 catch(InvalidPositionException &e){
2137 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2139 player->light = light;
2143 Step active objects and update lighting of them
2146 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2147 for(core::map<u16, ClientActiveObject*>::Iterator
2148 i = m_active_objects.getIterator();
2149 i.atEnd()==false; i++)
2151 ClientActiveObject* obj = i.getNode()->getValue();
2153 obj->step(dtime, this);
2161 v3s16 p = obj->getLightPosition();
2162 MapNode n = m_map->getNode(p);
2163 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2165 catch(InvalidPositionException &e){
2166 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2168 obj->updateLight(light);
2173 Step and handle simple objects
2175 for(core::list<ClientSimpleObject*>::Iterator
2176 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2178 ClientSimpleObject *simple = *i;
2179 core::list<ClientSimpleObject*>::Iterator cur = i;
2181 simple->step(dtime);
2182 if(simple->m_to_be_removed){
2184 m_simple_objects.erase(cur);
2189 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2191 m_simple_objects.push_back(simple);
2194 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2196 core::map<u16, ClientActiveObject*>::Node *n;
2197 n = m_active_objects.find(id);
2200 return n->getValue();
2203 bool isFreeClientActiveObjectId(u16 id,
2204 core::map<u16, ClientActiveObject*> &objects)
2209 for(core::map<u16, ClientActiveObject*>::Iterator
2210 i = objects.getIterator();
2211 i.atEnd()==false; i++)
2213 if(i.getNode()->getKey() == id)
2219 u16 getFreeClientActiveObjectId(
2220 core::map<u16, ClientActiveObject*> &objects)
2225 if(isFreeClientActiveObjectId(new_id, objects))
2235 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2238 if(object->getId() == 0)
2240 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2243 infostream<<"ClientEnvironment::addActiveObject(): "
2244 <<"no free ids available"<<std::endl;
2248 object->setId(new_id);
2250 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2252 infostream<<"ClientEnvironment::addActiveObject(): "
2253 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2257 infostream<<"ClientEnvironment::addActiveObject(): "
2258 <<"added (id="<<object->getId()<<")"<<std::endl;
2259 m_active_objects.insert(object->getId(), object);
2260 object->addToScene(m_smgr, m_texturesource, m_irr);
2261 { // Update lighting immediately
2265 v3s16 p = object->getLightPosition();
2266 MapNode n = m_map->getNode(p);
2267 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2269 catch(InvalidPositionException &e){
2270 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2272 object->updateLight(light);
2274 return object->getId();
2277 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2278 const std::string &init_data)
2280 ClientActiveObject* obj =
2281 ClientActiveObject::create(type, m_gamedef, this);
2284 infostream<<"ClientEnvironment::addActiveObject(): "
2285 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2294 obj->initialize(init_data);
2296 catch(SerializationError &e)
2298 errorstream<<"ClientEnvironment::addActiveObject():"
2299 <<" id="<<id<<" type="<<type
2300 <<": SerializationError in initialize(): "
2302 <<": init_data="<<serializeJsonString(init_data)
2306 addActiveObject(obj);
2309 void ClientEnvironment::removeActiveObject(u16 id)
2311 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2312 <<"id="<<id<<std::endl;
2313 ClientActiveObject* obj = getActiveObject(id);
2316 infostream<<"ClientEnvironment::removeActiveObject(): "
2317 <<"id="<<id<<" not found"<<std::endl;
2320 obj->removeFromScene(true);
2322 m_active_objects.remove(id);
2325 void ClientEnvironment::processActiveObjectMessage(u16 id,
2326 const std::string &data)
2328 ClientActiveObject* obj = getActiveObject(id);
2331 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2332 <<" got message for id="<<id<<", which doesn't exist."
2338 obj->processMessage(data);
2340 catch(SerializationError &e)
2342 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2343 <<" id="<<id<<" type="<<obj->getType()
2344 <<" SerializationError in processMessage(),"
2345 <<" message="<<serializeJsonString(data)
2351 Callbacks for activeobjects
2354 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2356 LocalPlayer *lplayer = getLocalPlayer();
2360 if(lplayer->hp > damage)
2361 lplayer->hp -= damage;
2366 ClientEnvEvent event;
2367 event.type = CEE_PLAYER_DAMAGE;
2368 event.player_damage.amount = damage;
2369 event.player_damage.send_to_server = handle_hp;
2370 m_client_event_queue.push_back(event);
2374 Client likes to call these
2377 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2378 core::array<DistanceSortedActiveObject> &dest)
2380 for(core::map<u16, ClientActiveObject*>::Iterator
2381 i = m_active_objects.getIterator();
2382 i.atEnd()==false; i++)
2384 ClientActiveObject* obj = i.getNode()->getValue();
2386 f32 d = (obj->getPosition() - origin).getLength();
2391 DistanceSortedActiveObject dso(obj, d);
2393 dest.push_back(dso);
2397 ClientEnvEvent ClientEnvironment::getClientEvent()
2399 if(m_client_event_queue.size() == 0)
2401 ClientEnvEvent event;
2402 event.type = CEE_NONE;
2405 return m_client_event_queue.pop_front();
2408 #endif // #ifndef SERVER