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"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
57 Environment::~Environment()
60 for(core::list<Player*>::Iterator i = m_players.begin();
61 i != m_players.end(); i++)
67 void Environment::addPlayer(Player *player)
69 DSTACK(__FUNCTION_NAME);
71 Check that peer_ids are unique.
72 Also check that names are unique.
73 Exception: there can be multiple players with peer_id=0
75 // If peer id is non-zero, it has to be unique.
76 if(player->peer_id != 0)
77 assert(getPlayer(player->peer_id) == NULL);
78 // Name has to be unique.
79 assert(getPlayer(player->getName()) == NULL);
81 m_players.push_back(player);
84 void Environment::removePlayer(u16 peer_id)
86 DSTACK(__FUNCTION_NAME);
88 for(core::list<Player*>::Iterator i = m_players.begin();
89 i != m_players.end(); i++)
92 if(player->peer_id != peer_id)
97 // See if there is an another one
98 // (shouldn't be, but just to be sure)
103 Player * Environment::getPlayer(u16 peer_id)
105 for(core::list<Player*>::Iterator i = m_players.begin();
106 i != m_players.end(); i++)
109 if(player->peer_id == peer_id)
115 Player * Environment::getPlayer(const char *name)
117 for(core::list<Player*>::Iterator i = m_players.begin();
118 i != m_players.end(); i++)
121 if(strcmp(player->getName(), name) == 0)
127 Player * Environment::getRandomConnectedPlayer()
129 core::list<Player*> connected_players = getPlayers(true);
130 u32 chosen_one = myrand() % connected_players.size();
132 for(core::list<Player*>::Iterator
133 i = connected_players.begin();
134 i != connected_players.end(); i++)
146 Player * Environment::getNearestConnectedPlayer(v3f pos)
148 core::list<Player*> connected_players = getPlayers(true);
150 Player *nearest_player = NULL;
151 for(core::list<Player*>::Iterator
152 i = connected_players.begin();
153 i != connected_players.end(); i++)
156 f32 d = player->getPosition().getDistanceFrom(pos);
157 if(d < nearest_d || nearest_player == NULL)
160 nearest_player = player;
163 return nearest_player;
166 core::list<Player*> Environment::getPlayers()
171 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
173 core::list<Player*> newlist;
174 for(core::list<Player*>::Iterator
175 i = m_players.begin();
176 i != m_players.end(); i++)
180 if(ignore_disconnected)
182 // Ignore disconnected players
183 if(player->peer_id == 0)
187 newlist.push_back(player);
192 void Environment::printPlayers(std::ostream &o)
194 o<<"Players in environment:"<<std::endl;
195 for(core::list<Player*>::Iterator i = m_players.begin();
196 i != m_players.end(); i++)
199 o<<"Player peer_id="<<player->peer_id<<std::endl;
203 u32 Environment::getDayNightRatio()
205 return time_to_daynight_ratio(m_time_of_day);
208 void Environment::stepTimeOfDay(float dtime)
210 m_time_counter += dtime;
211 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
212 u32 units = (u32)(m_time_counter*speed);
213 m_time_counter -= (f32)units / speed;
217 if(m_time_of_day + units >= 24000)
219 m_time_of_day = (m_time_of_day + units) % 24000;
221 m_time_of_day_f = (float)m_time_of_day / 24000.0;
224 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
225 if(m_time_of_day_f > 1.0)
226 m_time_of_day_f -= 1.0;
227 if(m_time_of_day_f < 0.0)
228 m_time_of_day_f += 1.0;
236 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
240 // Initialize timer to random value to spread processing
241 float itv = abm->getTriggerInterval();
242 itv = MYMAX(0.001, itv); // No less than 1ms
243 int minval = MYMAX(-0.51*itv, -60); // Clamp to
244 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
245 timer = myrand_range(minval, maxval);
252 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
255 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
256 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
257 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
264 void ActiveBlockList::update(core::list<v3s16> &active_positions,
266 core::map<v3s16, bool> &blocks_removed,
267 core::map<v3s16, bool> &blocks_added)
272 core::map<v3s16, bool> newlist;
273 for(core::list<v3s16>::Iterator i = active_positions.begin();
274 i != active_positions.end(); i++)
276 fillRadiusBlock(*i, radius, newlist);
280 Find out which blocks on the old list are not on the new list
282 // Go through old list
283 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
284 i.atEnd()==false; i++)
286 v3s16 p = i.getNode()->getKey();
287 // If not on new list, it's been removed
288 if(newlist.find(p) == NULL)
289 blocks_removed.insert(p, true);
293 Find out which blocks on the new list are not on the old list
295 // Go through new list
296 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
297 i.atEnd()==false; i++)
299 v3s16 p = i.getNode()->getKey();
300 // If not on old list, it's been added
301 if(m_list.find(p) == NULL)
302 blocks_added.insert(p, true);
309 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
310 i.atEnd()==false; i++)
312 v3s16 p = i.getNode()->getKey();
313 m_list.insert(p, true);
321 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
322 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
327 m_random_spawn_timer(3),
328 m_send_recommended_timer(0),
329 m_active_block_interval_overload_skip(0),
331 m_game_time_fraction_counter(0)
335 ServerEnvironment::~ServerEnvironment()
337 // Clear active block list.
338 // This makes the next one delete all active objects.
339 m_active_blocks.clear();
341 // Convert all objects to static and delete the active objects
342 deactivateFarObjects(true);
347 // Delete ActiveBlockModifiers
348 for(core::list<ABMWithState>::Iterator
349 i = m_abms.begin(); i != m_abms.end(); i++){
354 Map & ServerEnvironment::getMap()
359 ServerMap & ServerEnvironment::getServerMap()
365 void ServerEnvironment::serializePlayers(const std::string &savedir)
367 std::string players_path = savedir + "/players";
368 fs::CreateDir(players_path);
370 core::map<Player*, bool> saved_players;
372 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
373 for(u32 i=0; i<player_files.size(); i++)
375 if(player_files[i].dir)
378 // Full path to this file
379 std::string path = players_path + "/" + player_files[i].name;
381 //infostream<<"Checking player file "<<path<<std::endl;
383 // Load player to see what is its name
384 RemotePlayer testplayer(m_gamedef);
386 // Open file and deserialize
387 std::ifstream is(path.c_str(), std::ios_base::binary);
388 if(is.good() == false)
390 infostream<<"Failed to read "<<path<<std::endl;
393 testplayer.deSerialize(is);
396 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
398 // Search for the player
399 std::string playername = testplayer.getName();
400 Player *player = getPlayer(playername.c_str());
403 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
407 //infostream<<"Found matching player, overwriting."<<std::endl;
409 // OK, found. Save player there.
411 // Open file and serialize
412 std::ofstream os(path.c_str(), std::ios_base::binary);
413 if(os.good() == false)
415 infostream<<"Failed to overwrite "<<path<<std::endl;
418 player->serialize(os);
419 saved_players.insert(player, true);
423 for(core::list<Player*>::Iterator i = m_players.begin();
424 i != m_players.end(); i++)
427 if(saved_players.find(player) != NULL)
429 /*infostream<<"Player "<<player->getName()
430 <<" was already saved."<<std::endl;*/
433 std::string playername = player->getName();
434 // Don't save unnamed player
437 //infostream<<"Not saving unnamed player."<<std::endl;
443 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
444 playername = "player";
445 std::string path = players_path + "/" + playername;
447 for(u32 i=0; i<1000; i++)
449 if(fs::PathExists(path) == false)
454 path = players_path + "/" + playername + itos(i);
458 infostream<<"Didn't find free file for player"<<std::endl;
463 /*infostream<<"Saving player "<<player->getName()<<" to "
465 // Open file and serialize
466 std::ofstream os(path.c_str(), std::ios_base::binary);
467 if(os.good() == false)
469 infostream<<"Failed to overwrite "<<path<<std::endl;
472 player->serialize(os);
473 saved_players.insert(player, true);
477 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
480 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
482 std::string players_path = savedir + "/players";
484 core::map<Player*, bool> saved_players;
486 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
487 for(u32 i=0; i<player_files.size(); i++)
489 if(player_files[i].dir)
492 // Full path to this file
493 std::string path = players_path + "/" + player_files[i].name;
495 //infostream<<"Checking player file "<<path<<std::endl;
497 // Load player to see what is its name
498 RemotePlayer testplayer(m_gamedef);
500 // Open file and deserialize
501 std::ifstream is(path.c_str(), std::ios_base::binary);
502 if(is.good() == false)
504 infostream<<"Failed to read "<<path<<std::endl;
507 testplayer.deSerialize(is);
510 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
512 infostream<<"Not loading player with invalid name: "
513 <<testplayer.getName()<<std::endl;
516 /*infostream<<"Loaded test player with name "<<testplayer.getName()
519 // Search for the player
520 std::string playername = testplayer.getName();
521 Player *player = getPlayer(playername.c_str());
522 bool newplayer = false;
525 //infostream<<"Is a new player"<<std::endl;
526 player = new RemotePlayer(m_gamedef);
532 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
534 // Open file and deserialize
535 std::ifstream is(path.c_str(), std::ios_base::binary);
536 if(is.good() == false)
538 infostream<<"Failed to read "<<path<<std::endl;
541 player->deSerialize(is);
551 void ServerEnvironment::saveMeta(const std::string &savedir)
553 std::string path = savedir + "/env_meta.txt";
555 // Open file and serialize
556 std::ofstream os(path.c_str(), std::ios_base::binary);
557 if(os.good() == false)
559 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
561 throw SerializationError("Couldn't save env meta");
565 args.setU64("game_time", m_game_time);
566 args.setU64("time_of_day", getTimeOfDay());
571 void ServerEnvironment::loadMeta(const std::string &savedir)
573 std::string path = savedir + "/env_meta.txt";
575 // Open file and deserialize
576 std::ifstream is(path.c_str(), std::ios_base::binary);
577 if(is.good() == false)
579 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
581 throw SerializationError("Couldn't load env meta");
589 throw SerializationError
590 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
592 std::getline(is, line);
593 std::string trimmedline = trim(line);
594 if(trimmedline == "EnvArgsEnd")
596 args.parseConfigLine(line);
600 m_game_time = args.getU64("game_time");
601 }catch(SettingNotFoundException &e){
602 // Getting this is crucial, otherwise timestamps are useless
603 throw SerializationError("Couldn't load env meta game_time");
607 m_time_of_day = args.getU64("time_of_day");
608 }catch(SettingNotFoundException &e){
609 // This is not as important
610 m_time_of_day = 9000;
616 ActiveBlockModifier *abm;
618 std::set<content_t> required_neighbors;
624 ServerEnvironment *m_env;
625 std::map<content_t, std::list<ActiveABM> > m_aabms;
627 ABMHandler(core::list<ABMWithState> &abms,
628 float dtime_s, ServerEnvironment *env,
634 INodeDefManager *ndef = env->getGameDef()->ndef();
635 for(core::list<ABMWithState>::Iterator
636 i = abms.begin(); i != abms.end(); i++){
637 ActiveBlockModifier *abm = i->abm;
638 float trigger_interval = abm->getTriggerInterval();
639 if(trigger_interval < 0.001)
640 trigger_interval = 0.001;
641 float actual_interval = dtime_s;
644 if(i->timer < trigger_interval)
646 i->timer -= trigger_interval;
647 actual_interval = trigger_interval;
649 float intervals = actual_interval / trigger_interval;
652 float chance = abm->getTriggerChance();
657 aabm.chance = chance / intervals;
661 std::set<std::string> required_neighbors_s
662 = abm->getRequiredNeighbors();
663 for(std::set<std::string>::iterator
664 i = required_neighbors_s.begin();
665 i != required_neighbors_s.end(); i++)
667 ndef->getIds(*i, aabm.required_neighbors);
670 std::set<std::string> contents_s = abm->getTriggerContents();
671 for(std::set<std::string>::iterator
672 i = contents_s.begin(); i != contents_s.end(); i++)
674 std::set<content_t> ids;
675 ndef->getIds(*i, ids);
676 for(std::set<content_t>::const_iterator k = ids.begin();
680 std::map<content_t, std::list<ActiveABM> >::iterator j;
682 if(j == m_aabms.end()){
683 std::list<ActiveABM> aabmlist;
684 m_aabms[c] = aabmlist;
687 j->second.push_back(aabm);
692 void apply(MapBlock *block)
697 ServerMap *map = &m_env->getServerMap();
700 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
701 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
702 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
704 MapNode n = block->getNodeNoEx(p0);
705 content_t c = n.getContent();
706 v3s16 p = p0 + block->getPosRelative();
708 std::map<content_t, std::list<ActiveABM> >::iterator j;
710 if(j == m_aabms.end())
713 for(std::list<ActiveABM>::iterator
714 i = j->second.begin(); i != j->second.end(); i++)
716 if(myrand() % i->chance != 0)
720 if(!i->required_neighbors.empty())
723 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
724 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
725 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
729 MapNode n = map->getNodeNoEx(p1);
730 content_t c = n.getContent();
731 std::set<content_t>::const_iterator k;
732 k = i->required_neighbors.find(c);
733 if(k != i->required_neighbors.end()){
737 // No required neighbor found
742 // Find out how many objects the block contains
743 u32 active_object_count = block->m_static_objects.m_active.size();
744 // Find out how many objects this and all the neighbors contain
745 u32 active_object_count_wider = 0;
746 u32 wider_unknown_count = 0;
747 for(s16 x=-1; x<=1; x++)
748 for(s16 y=-1; y<=1; y++)
749 for(s16 z=-1; z<=1; z++)
751 MapBlock *block2 = map->getBlockNoCreateNoEx(
752 block->getPos() + v3s16(x,y,z));
754 wider_unknown_count = 0;
757 active_object_count_wider +=
758 block2->m_static_objects.m_active.size()
759 + block2->m_static_objects.m_stored.size();
762 u32 wider_known_count = 3*3*3 - wider_unknown_count;
763 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
765 // Call all the trigger variations
766 i->abm->trigger(m_env, p, n);
767 i->abm->trigger(m_env, p, n,
768 active_object_count, active_object_count_wider);
774 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
776 // Get time difference
778 u32 stamp = block->getTimestamp();
779 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
780 dtime_s = m_game_time - block->getTimestamp();
781 dtime_s += additional_dtime;
783 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
784 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
786 // Set current time as timestamp
787 block->setTimestampNoChangedFlag(m_game_time);
789 /*infostream<<"ServerEnvironment::activateBlock(): block is "
790 <<dtime_s<<" seconds old."<<std::endl;*/
792 // Activate stored objects
793 activateObjects(block, dtime_s);
796 std::map<v3s16, NodeTimer> elapsed_timers =
797 block->m_node_timers.step((float)dtime_s);
798 if(!elapsed_timers.empty()){
800 for(std::map<v3s16, NodeTimer>::iterator
801 i = elapsed_timers.begin();
802 i != elapsed_timers.end(); i++){
803 n = block->getNodeNoEx(i->first);
804 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
805 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
809 /* Handle ActiveBlockModifiers */
810 ABMHandler abmhandler(m_abms, dtime_s, this, false);
811 abmhandler.apply(block);
814 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
816 m_abms.push_back(ABMWithState(abm));
819 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
821 std::set<u16> objects;
822 for(core::map<u16, ServerActiveObject*>::Iterator
823 i = m_active_objects.getIterator();
824 i.atEnd()==false; i++)
826 ServerActiveObject* obj = i.getNode()->getValue();
827 u16 id = i.getNode()->getKey();
828 v3f objectpos = obj->getBasePosition();
829 if(objectpos.getDistanceFrom(pos) > radius)
836 void ServerEnvironment::clearAllObjects()
838 infostream<<"ServerEnvironment::clearAllObjects(): "
839 <<"Removing all active objects"<<std::endl;
840 core::list<u16> objects_to_remove;
841 for(core::map<u16, ServerActiveObject*>::Iterator
842 i = m_active_objects.getIterator();
843 i.atEnd()==false; i++)
845 ServerActiveObject* obj = i.getNode()->getValue();
846 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
848 u16 id = i.getNode()->getKey();
849 v3f objectpos = obj->getBasePosition();
850 // Delete static object if block is loaded
851 if(obj->m_static_exists){
852 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
854 block->m_static_objects.remove(id);
855 block->raiseModified(MOD_STATE_WRITE_NEEDED,
857 obj->m_static_exists = false;
860 // If known by some client, don't delete immediately
861 if(obj->m_known_by_count > 0){
862 obj->m_pending_deactivation = true;
863 obj->m_removed = true;
867 // Tell the object about removal
868 obj->removingFromEnvironment();
869 // Deregister in scripting api
870 scriptapi_rm_object_reference(m_lua, obj);
872 // Delete active object
873 if(obj->environmentDeletes())
875 // Id to be removed from m_active_objects
876 objects_to_remove.push_back(id);
878 // Remove references from m_active_objects
879 for(core::list<u16>::Iterator i = objects_to_remove.begin();
880 i != objects_to_remove.end(); i++)
882 m_active_objects.remove(*i);
885 core::list<v3s16> loadable_blocks;
886 infostream<<"ServerEnvironment::clearAllObjects(): "
887 <<"Listing all loadable blocks"<<std::endl;
888 m_map->listAllLoadableBlocks(loadable_blocks);
889 infostream<<"ServerEnvironment::clearAllObjects(): "
890 <<"Done listing all loadable blocks: "
891 <<loadable_blocks.size()
892 <<", now clearing"<<std::endl;
893 u32 report_interval = loadable_blocks.size() / 10;
894 u32 num_blocks_checked = 0;
895 u32 num_blocks_cleared = 0;
896 u32 num_objs_cleared = 0;
897 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
898 i != loadable_blocks.end(); i++)
901 MapBlock *block = m_map->emergeBlock(p, false);
903 errorstream<<"ServerEnvironment::clearAllObjects(): "
904 <<"Failed to emerge block "<<PP(p)<<std::endl;
907 u32 num_stored = block->m_static_objects.m_stored.size();
908 u32 num_active = block->m_static_objects.m_active.size();
909 if(num_stored != 0 || num_active != 0){
910 block->m_static_objects.m_stored.clear();
911 block->m_static_objects.m_active.clear();
912 block->raiseModified(MOD_STATE_WRITE_NEEDED,
914 num_objs_cleared += num_stored + num_active;
915 num_blocks_cleared++;
917 num_blocks_checked++;
919 if(num_blocks_checked % report_interval == 0){
920 float percent = 100.0 * (float)num_blocks_checked /
921 loadable_blocks.size();
922 infostream<<"ServerEnvironment::clearAllObjects(): "
923 <<"Cleared "<<num_objs_cleared<<" objects"
924 <<" in "<<num_blocks_cleared<<" blocks ("
925 <<percent<<"%)"<<std::endl;
928 infostream<<"ServerEnvironment::clearAllObjects(): "
929 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
930 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
933 void ServerEnvironment::step(float dtime)
935 DSTACK(__FUNCTION_NAME);
937 //TimeTaker timer("ServerEnv step");
939 /* Step time of day */
940 stepTimeOfDay(dtime);
946 m_game_time_fraction_counter += dtime;
947 u32 inc_i = (u32)m_game_time_fraction_counter;
948 m_game_time += inc_i;
949 m_game_time_fraction_counter -= (float)inc_i;
956 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
957 for(core::list<Player*>::Iterator i = m_players.begin();
958 i != m_players.end(); i++)
962 // Ignore disconnected players
963 if(player->peer_id == 0)
966 v3f playerpos = player->getPosition();
969 player->move(dtime, *m_map, 100*BS);
974 Manage active block list
976 if(m_active_blocks_management_interval.step(dtime, 2.0))
978 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
980 Get player block positions
982 core::list<v3s16> players_blockpos;
983 for(core::list<Player*>::Iterator
984 i = m_players.begin();
985 i != m_players.end(); i++)
988 // Ignore disconnected players
989 if(player->peer_id == 0)
991 v3s16 blockpos = getNodeBlockPos(
992 floatToInt(player->getPosition(), BS));
993 players_blockpos.push_back(blockpos);
997 Update list of active blocks, collecting changes
999 const s16 active_block_range = g_settings->getS16("active_block_range");
1000 core::map<v3s16, bool> blocks_removed;
1001 core::map<v3s16, bool> blocks_added;
1002 m_active_blocks.update(players_blockpos, active_block_range,
1003 blocks_removed, blocks_added);
1006 Handle removed blocks
1009 // Convert active objects that are no more in active blocks to static
1010 deactivateFarObjects(false);
1012 for(core::map<v3s16, bool>::Iterator
1013 i = blocks_removed.getIterator();
1014 i.atEnd()==false; i++)
1016 v3s16 p = i.getNode()->getKey();
1018 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1019 <<") became inactive"<<std::endl;*/
1021 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1025 // Set current time as timestamp (and let it set ChangedFlag)
1026 block->setTimestamp(m_game_time);
1033 for(core::map<v3s16, bool>::Iterator
1034 i = blocks_added.getIterator();
1035 i.atEnd()==false; i++)
1037 v3s16 p = i.getNode()->getKey();
1039 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1040 <<") became active"<<std::endl;*/
1042 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1044 // Block needs to be fetched first
1045 m_emerger->queueBlockEmerge(p, false);
1046 m_active_blocks.m_list.remove(p);
1050 activateBlock(block);
1055 Mess around in active blocks
1057 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1059 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1063 for(core::map<v3s16, bool>::Iterator
1064 i = m_active_blocks.m_list.getIterator();
1065 i.atEnd()==false; i++)
1067 v3s16 p = i.getNode()->getKey();
1069 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1070 <<") being handled"<<std::endl;*/
1072 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1076 // Reset block usage timer
1077 block->resetUsageTimer();
1079 // Set current time as timestamp
1080 block->setTimestampNoChangedFlag(m_game_time);
1081 // If time has changed much from the one on disk,
1082 // set block to be saved when it is unloaded
1083 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1084 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1085 "Timestamp older than 60s (step)");
1088 std::map<v3s16, NodeTimer> elapsed_timers =
1089 block->m_node_timers.step((float)dtime);
1090 if(!elapsed_timers.empty()){
1092 for(std::map<v3s16, NodeTimer>::iterator
1093 i = elapsed_timers.begin();
1094 i != elapsed_timers.end(); i++){
1095 n = block->getNodeNoEx(i->first);
1096 p = i->first + block->getPosRelative();
1097 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1098 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1104 const float abm_interval = 1.0;
1105 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1107 if(m_active_block_interval_overload_skip > 0){
1108 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1109 m_active_block_interval_overload_skip--;
1112 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1113 TimeTaker timer("modify in active blocks");
1115 // Initialize handling of ActiveBlockModifiers
1116 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1118 for(core::map<v3s16, bool>::Iterator
1119 i = m_active_blocks.m_list.getIterator();
1120 i.atEnd()==false; i++)
1122 v3s16 p = i.getNode()->getKey();
1124 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1125 <<") being handled"<<std::endl;*/
1127 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1131 // Set current time as timestamp
1132 block->setTimestampNoChangedFlag(m_game_time);
1134 /* Handle ActiveBlockModifiers */
1135 abmhandler.apply(block);
1138 u32 time_ms = timer.stop(true);
1139 u32 max_time_ms = 200;
1140 if(time_ms > max_time_ms){
1141 infostream<<"WARNING: active block modifiers took "
1142 <<time_ms<<"ms (longer than "
1143 <<max_time_ms<<"ms)"<<std::endl;
1144 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1149 Step script environment (run global on_step())
1151 scriptapi_environment_step(m_lua, dtime);
1157 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1158 //TimeTaker timer("Step active objects");
1160 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1162 // This helps the objects to send data at the same time
1163 bool send_recommended = false;
1164 m_send_recommended_timer += dtime;
1165 if(m_send_recommended_timer > getSendRecommendedInterval())
1167 m_send_recommended_timer -= getSendRecommendedInterval();
1168 send_recommended = true;
1171 for(core::map<u16, ServerActiveObject*>::Iterator
1172 i = m_active_objects.getIterator();
1173 i.atEnd()==false; i++)
1175 ServerActiveObject* obj = i.getNode()->getValue();
1176 // Remove non-peaceful mobs on peaceful mode
1177 if(g_settings->getBool("only_peaceful_mobs")){
1178 if(!obj->isPeaceful())
1179 obj->m_removed = true;
1181 // Don't step if is to be removed or stored statically
1182 if(obj->m_removed || obj->m_pending_deactivation)
1185 obj->step(dtime, send_recommended);
1186 // Read messages from object
1187 while(obj->m_messages_out.size() > 0)
1189 m_active_object_messages.push_back(
1190 obj->m_messages_out.pop_front());
1196 Manage active objects
1198 if(m_object_management_interval.step(dtime, 0.5))
1200 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1202 Remove objects that satisfy (m_removed && m_known_by_count==0)
1204 removeRemovedObjects();
1208 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1210 core::map<u16, ServerActiveObject*>::Node *n;
1211 n = m_active_objects.find(id);
1214 return n->getValue();
1217 bool isFreeServerActiveObjectId(u16 id,
1218 core::map<u16, ServerActiveObject*> &objects)
1223 for(core::map<u16, ServerActiveObject*>::Iterator
1224 i = objects.getIterator();
1225 i.atEnd()==false; i++)
1227 if(i.getNode()->getKey() == id)
1233 u16 getFreeServerActiveObjectId(
1234 core::map<u16, ServerActiveObject*> &objects)
1239 if(isFreeServerActiveObjectId(new_id, objects))
1249 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1252 u16 id = addActiveObjectRaw(object, true, 0);
1256 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1260 v3f objectpos = obj->getBasePosition();
1262 // The block in which the object resides in
1263 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1266 Update the static data
1269 // Create new static object
1270 std::string staticdata = obj->getStaticData();
1271 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1272 // Add to the block where the object is located in
1273 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1274 // Get or generate the block
1275 MapBlock *block = m_map->emergeBlock(blockpos);
1277 bool succeeded = false;
1281 block->m_static_objects.insert(0, s_obj);
1282 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1283 "addActiveObjectAsStatic");
1287 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1288 <<"Could not find or generate "
1289 <<"a block for storing static object"<<std::endl;
1293 if(obj->environmentDeletes())
1300 Finds out what new objects have been added to
1301 inside a radius around a position
1303 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1304 core::map<u16, bool> ¤t_objects,
1305 core::map<u16, bool> &added_objects)
1307 v3f pos_f = intToFloat(pos, BS);
1308 f32 radius_f = radius * BS;
1310 Go through the object list,
1311 - discard m_removed objects,
1312 - discard objects that are too far away,
1313 - discard objects that are found in current_objects.
1314 - add remaining objects to added_objects
1316 for(core::map<u16, ServerActiveObject*>::Iterator
1317 i = m_active_objects.getIterator();
1318 i.atEnd()==false; i++)
1320 u16 id = i.getNode()->getKey();
1322 ServerActiveObject *object = i.getNode()->getValue();
1325 // Discard if removed
1326 if(object->m_removed)
1328 if(object->unlimitedTransferDistance() == false){
1329 // Discard if too far
1330 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1331 if(distance_f > radius_f)
1334 // Discard if already on current_objects
1335 core::map<u16, bool>::Node *n;
1336 n = current_objects.find(id);
1339 // Add to added_objects
1340 added_objects.insert(id, false);
1345 Finds out what objects have been removed from
1346 inside a radius around a position
1348 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1349 core::map<u16, bool> ¤t_objects,
1350 core::map<u16, bool> &removed_objects)
1352 v3f pos_f = intToFloat(pos, BS);
1353 f32 radius_f = radius * BS;
1355 Go through current_objects; object is removed if:
1356 - object is not found in m_active_objects (this is actually an
1357 error condition; objects should be set m_removed=true and removed
1358 only after all clients have been informed about removal), or
1359 - object has m_removed=true, or
1360 - object is too far away
1362 for(core::map<u16, bool>::Iterator
1363 i = current_objects.getIterator();
1364 i.atEnd()==false; i++)
1366 u16 id = i.getNode()->getKey();
1367 ServerActiveObject *object = getActiveObject(id);
1370 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1371 <<" object in current_objects is NULL"<<std::endl;
1372 removed_objects.insert(id, false);
1376 if(object->m_removed)
1378 removed_objects.insert(id, false);
1382 // If transfer distance is unlimited, don't remove
1383 if(object->unlimitedTransferDistance())
1386 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1388 if(distance_f >= radius_f)
1390 removed_objects.insert(id, false);
1398 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1400 if(m_active_object_messages.size() == 0)
1401 return ActiveObjectMessage(0);
1403 return m_active_object_messages.pop_front();
1407 ************ Private methods *************
1410 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1411 bool set_changed, u32 dtime_s)
1414 if(object->getId() == 0){
1415 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1418 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1419 <<"no free ids available"<<std::endl;
1420 if(object->environmentDeletes())
1424 object->setId(new_id);
1427 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1428 <<"supplied with id "<<object->getId()<<std::endl;
1430 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1432 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1433 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1434 if(object->environmentDeletes())
1438 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1439 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1441 m_active_objects.insert(object->getId(), object);
1443 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1444 <<"Added id="<<object->getId()<<"; there are now "
1445 <<m_active_objects.size()<<" active objects."
1448 // Register reference in scripting api (must be done before post-init)
1449 scriptapi_add_object_reference(m_lua, object);
1450 // Post-initialize object
1451 object->addedToEnvironment(dtime_s);
1453 // Add static data to block
1454 if(object->isStaticAllowed())
1456 // Add static object to active static list of the block
1457 v3f objectpos = object->getBasePosition();
1458 std::string staticdata = object->getStaticData();
1459 StaticObject s_obj(object->getType(), objectpos, staticdata);
1460 // Add to the block where the object is located in
1461 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1462 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1465 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1466 object->m_static_exists = true;
1467 object->m_static_block = blockpos;
1470 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1471 "addActiveObjectRaw");
1474 v3s16 p = floatToInt(objectpos, BS);
1475 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1476 <<"could not find block for storing id="<<object->getId()
1477 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1481 return object->getId();
1485 Remove objects that satisfy (m_removed && m_known_by_count==0)
1487 void ServerEnvironment::removeRemovedObjects()
1489 core::list<u16> objects_to_remove;
1490 for(core::map<u16, ServerActiveObject*>::Iterator
1491 i = m_active_objects.getIterator();
1492 i.atEnd()==false; i++)
1494 u16 id = i.getNode()->getKey();
1495 ServerActiveObject* obj = i.getNode()->getValue();
1496 // This shouldn't happen but check it
1499 infostream<<"NULL object found in ServerEnvironment"
1500 <<" while finding removed objects. id="<<id<<std::endl;
1501 // Id to be removed from m_active_objects
1502 objects_to_remove.push_back(id);
1507 We will delete objects that are marked as removed or thatare
1508 waiting for deletion after deactivation
1510 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1514 Delete static data from block if is marked as removed
1516 if(obj->m_static_exists && obj->m_removed)
1518 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1521 block->m_static_objects.remove(id);
1522 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1523 "removeRemovedObjects");
1524 obj->m_static_exists = false;
1528 // If m_known_by_count > 0, don't actually remove.
1529 if(obj->m_known_by_count > 0)
1532 // Tell the object about removal
1533 obj->removingFromEnvironment();
1534 // Deregister in scripting api
1535 scriptapi_rm_object_reference(m_lua, obj);
1538 if(obj->environmentDeletes())
1540 // Id to be removed from m_active_objects
1541 objects_to_remove.push_back(id);
1543 // Remove references from m_active_objects
1544 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1545 i != objects_to_remove.end(); i++)
1547 m_active_objects.remove(*i);
1551 static void print_hexdump(std::ostream &o, const std::string &data)
1553 const int linelength = 16;
1554 for(int l=0; ; l++){
1555 int i0 = linelength * l;
1556 bool at_end = false;
1557 int thislinelength = linelength;
1558 if(i0 + thislinelength > (int)data.size()){
1559 thislinelength = data.size() - i0;
1562 for(int di=0; di<linelength; di++){
1565 if(di<thislinelength)
1566 snprintf(buf, 4, "%.2x ", data[i]);
1568 snprintf(buf, 4, " ");
1572 for(int di=0; di<thislinelength; di++){
1586 Convert stored objects from blocks near the players to active.
1588 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1592 // Ignore if no stored objects (to not set changed flag)
1593 if(block->m_static_objects.m_stored.size() == 0)
1595 verbosestream<<"ServerEnvironment::activateObjects(): "
1596 <<"activating objects of block "<<PP(block->getPos())
1597 <<" ("<<block->m_static_objects.m_stored.size()
1598 <<" objects)"<<std::endl;
1599 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1601 errorstream<<"suspiciously large amount of objects detected: "
1602 <<block->m_static_objects.m_stored.size()<<" in "
1603 <<PP(block->getPos())
1604 <<"; removing all of them."<<std::endl;
1605 // Clear stored list
1606 block->m_static_objects.m_stored.clear();
1607 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1608 "stored list cleared in activateObjects due to "
1609 "large amount of objects");
1612 // A list for objects that couldn't be converted to active for some
1613 // reason. They will be stored back.
1614 core::list<StaticObject> new_stored;
1615 // Loop through stored static objects
1616 for(core::list<StaticObject>::Iterator
1617 i = block->m_static_objects.m_stored.begin();
1618 i != block->m_static_objects.m_stored.end(); i++)
1620 /*infostream<<"Server: Creating an active object from "
1621 <<"static data"<<std::endl;*/
1622 StaticObject &s_obj = *i;
1623 // Create an active object from the data
1624 ServerActiveObject *obj = ServerActiveObject::create
1625 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1626 // If couldn't create object, store static data back.
1629 errorstream<<"ServerEnvironment::activateObjects(): "
1630 <<"failed to create active object from static object "
1631 <<"in block "<<PP(s_obj.pos/BS)
1632 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1633 print_hexdump(verbosestream, s_obj.data);
1635 new_stored.push_back(s_obj);
1638 verbosestream<<"ServerEnvironment::activateObjects(): "
1639 <<"activated static object pos="<<PP(s_obj.pos/BS)
1640 <<" type="<<(int)s_obj.type<<std::endl;
1641 // This will also add the object to the active static list
1642 addActiveObjectRaw(obj, false, dtime_s);
1644 // Clear stored list
1645 block->m_static_objects.m_stored.clear();
1646 // Add leftover failed stuff to stored list
1647 for(core::list<StaticObject>::Iterator
1648 i = new_stored.begin();
1649 i != new_stored.end(); i++)
1651 StaticObject &s_obj = *i;
1652 block->m_static_objects.m_stored.push_back(s_obj);
1655 Note: Block hasn't really been modified here.
1656 The objects have just been activated and moved from the stored
1657 static list to the active static list.
1658 As such, the block is essentially the same.
1659 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1660 Otherwise there would be a huge amount of unnecessary I/O.
1665 Convert objects that are not standing inside active blocks to static.
1667 If m_known_by_count != 0, active object is not deleted, but static
1668 data is still updated.
1670 If force_delete is set, active object is deleted nevertheless. It
1671 shall only be set so in the destructor of the environment.
1673 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1675 core::list<u16> objects_to_remove;
1676 for(core::map<u16, ServerActiveObject*>::Iterator
1677 i = m_active_objects.getIterator();
1678 i.atEnd()==false; i++)
1680 ServerActiveObject* obj = i.getNode()->getValue();
1683 // Do not deactivate if static data creation not allowed
1684 if(!force_delete && !obj->isStaticAllowed())
1687 // If pending deactivation, let removeRemovedObjects() do it
1688 if(!force_delete && obj->m_pending_deactivation)
1691 u16 id = i.getNode()->getKey();
1692 v3f objectpos = obj->getBasePosition();
1694 // The block in which the object resides in
1695 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1697 // If block is active, don't remove
1698 if(!force_delete && m_active_blocks.contains(blockpos_o))
1701 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1702 <<"deactivating object id="<<id<<" on inactive block "
1703 <<PP(blockpos_o)<<std::endl;
1705 // If known by some client, don't immediately delete.
1706 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1709 Update the static data
1712 if(obj->isStaticAllowed())
1714 // Create new static object
1715 std::string staticdata_new = obj->getStaticData();
1716 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1718 bool stays_in_same_block = false;
1719 bool data_changed = true;
1721 if(obj->m_static_exists){
1722 if(obj->m_static_block == blockpos_o)
1723 stays_in_same_block = true;
1725 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1727 core::map<u16, StaticObject>::Node *n =
1728 block->m_static_objects.m_active.find(id);
1730 StaticObject static_old = n->getValue();
1732 float save_movem = obj->getMinimumSavedMovement();
1734 if(static_old.data == staticdata_new &&
1735 (static_old.pos - objectpos).getLength() < save_movem)
1736 data_changed = false;
1738 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1739 <<"id="<<id<<" m_static_exists=true but "
1740 <<"static data doesn't actually exist in "
1741 <<PP(obj->m_static_block)<<std::endl;
1745 bool shall_be_written = (!stays_in_same_block || data_changed);
1747 // Delete old static object
1748 if(obj->m_static_exists)
1750 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1753 block->m_static_objects.remove(id);
1754 obj->m_static_exists = false;
1755 // Only mark block as modified if data changed considerably
1756 if(shall_be_written)
1757 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1758 "deactivateFarObjects: Static data "
1759 "changed considerably");
1763 // Add to the block where the object is located in
1764 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1765 // Get or generate the block
1766 MapBlock *block = NULL;
1768 block = m_map->emergeBlock(blockpos);
1769 } catch(InvalidPositionException &e){
1770 // Handled via NULL pointer
1775 if(block->m_static_objects.m_stored.size() >= 49){
1776 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1777 <<" statically but block "<<PP(blockpos)
1778 <<" already contains "
1779 <<block->m_static_objects.m_stored.size()
1780 <<" (over 49) objects."
1781 <<" Forcing delete."<<std::endl;
1782 force_delete = true;
1784 u16 new_id = pending_delete ? id : 0;
1785 // If static counterpart already exists, remove it first.
1786 // This shouldn't happen, but happens rarely for some
1787 // unknown reason. Unsuccessful attempts have been made to
1788 // find said reason.
1789 if(new_id && block->m_static_objects.m_active.find(new_id)){
1790 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1792 block->m_static_objects.remove(new_id);
1794 block->m_static_objects.insert(new_id, s_obj);
1796 // Only mark block as modified if data changed considerably
1797 if(shall_be_written)
1798 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1799 "deactivateFarObjects: Static data "
1800 "changed considerably");
1802 obj->m_static_exists = true;
1803 obj->m_static_block = block->getPos();
1808 v3s16 p = floatToInt(objectpos, BS);
1809 errorstream<<"ServerEnv: Could not find or generate "
1810 <<"a block for storing id="<<obj->getId()
1811 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1818 If known by some client, set pending deactivation.
1819 Otherwise delete it immediately.
1822 if(pending_delete && !force_delete)
1824 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1825 <<"object id="<<id<<" is known by clients"
1826 <<"; not deleting yet"<<std::endl;
1828 obj->m_pending_deactivation = true;
1832 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1833 <<"object id="<<id<<" is not known by clients"
1834 <<"; deleting"<<std::endl;
1836 // Tell the object about removal
1837 obj->removingFromEnvironment();
1838 // Deregister in scripting api
1839 scriptapi_rm_object_reference(m_lua, obj);
1841 // Delete active object
1842 if(obj->environmentDeletes())
1844 // Id to be removed from m_active_objects
1845 objects_to_remove.push_back(id);
1848 // Remove references from m_active_objects
1849 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1850 i != objects_to_remove.end(); i++)
1852 m_active_objects.remove(*i);
1859 #include "clientsimpleobject.h"
1865 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1866 ITextureSource *texturesource, IGameDef *gamedef,
1867 IrrlichtDevice *irr):
1870 m_texturesource(texturesource),
1876 ClientEnvironment::~ClientEnvironment()
1878 // delete active objects
1879 for(core::map<u16, ClientActiveObject*>::Iterator
1880 i = m_active_objects.getIterator();
1881 i.atEnd()==false; i++)
1883 delete i.getNode()->getValue();
1886 for(core::list<ClientSimpleObject*>::Iterator
1887 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1896 Map & ClientEnvironment::getMap()
1901 ClientMap & ClientEnvironment::getClientMap()
1906 void ClientEnvironment::addPlayer(Player *player)
1908 DSTACK(__FUNCTION_NAME);
1910 It is a failure if player is local and there already is a local
1913 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1915 Environment::addPlayer(player);
1918 LocalPlayer * ClientEnvironment::getLocalPlayer()
1920 for(core::list<Player*>::Iterator i = m_players.begin();
1921 i != m_players.end(); i++)
1923 Player *player = *i;
1924 if(player->isLocal())
1925 return (LocalPlayer*)player;
1930 void ClientEnvironment::step(float dtime)
1932 DSTACK(__FUNCTION_NAME);
1934 /* Step time of day */
1935 stepTimeOfDay(dtime);
1937 // Get some settings
1938 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1939 bool free_move = fly_allowed && g_settings->getBool("free_move");
1942 LocalPlayer *lplayer = getLocalPlayer();
1944 // collision info queue
1945 core::list<CollisionInfo> player_collisions;
1948 Get the speed the player is going
1950 bool is_climbing = lplayer->is_climbing;
1952 f32 player_speed = lplayer->getSpeed().getLength();
1955 Maximum position increment
1957 //f32 position_max_increment = 0.05*BS;
1958 f32 position_max_increment = 0.1*BS;
1960 // Maximum time increment (for collision detection etc)
1961 // time = distance / speed
1962 f32 dtime_max_increment = 1;
1963 if(player_speed > 0.001)
1964 dtime_max_increment = position_max_increment / player_speed;
1966 // Maximum time increment is 10ms or lower
1967 if(dtime_max_increment > 0.01)
1968 dtime_max_increment = 0.01;
1970 // Don't allow overly huge dtime
1974 f32 dtime_downcount = dtime;
1977 Stuff that has a maximum time increment
1986 if(dtime_downcount > dtime_max_increment)
1988 dtime_part = dtime_max_increment;
1989 dtime_downcount -= dtime_part;
1993 dtime_part = dtime_downcount;
1995 Setting this to 0 (no -=dtime_part) disables an infinite loop
1996 when dtime_part is so small that dtime_downcount -= dtime_part
1999 dtime_downcount = 0;
2007 v3f lplayerpos = lplayer->getPosition();
2010 if(free_move == false && is_climbing == false)
2013 v3f speed = lplayer->getSpeed();
2014 if(lplayer->swimming_up == false)
2015 speed.Y -= 9.81 * BS * dtime_part * 2;
2018 if(lplayer->in_water_stable || lplayer->in_water)
2020 f32 max_down = 2.0*BS;
2021 if(speed.Y < -max_down) speed.Y = -max_down;
2024 if(speed.getLength() > max)
2026 speed = speed / speed.getLength() * max;
2030 lplayer->setSpeed(speed);
2035 This also does collision detection.
2037 lplayer->move(dtime_part, *m_map, position_max_increment,
2038 &player_collisions);
2041 while(dtime_downcount > 0.001);
2043 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2045 for(core::list<CollisionInfo>::Iterator
2046 i = player_collisions.begin();
2047 i != player_collisions.end(); i++)
2049 CollisionInfo &info = *i;
2050 v3f speed_diff = info.new_speed - info.old_speed;;
2051 // Handle only fall damage
2052 // (because otherwise walking against something in fast_move kills you)
2053 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2055 // Get rid of other components
2058 f32 pre_factor = 1; // 1 hp per node/s
2059 f32 tolerance = BS*14; // 5 without damage
2060 f32 post_factor = 1; // 1 hp per node/s
2061 if(info.type == COLLISION_NODE)
2063 const ContentFeatures &f = m_gamedef->ndef()->
2064 get(m_map->getNodeNoEx(info.node_p));
2065 // Determine fall damage multiplier
2066 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2067 pre_factor = 1.0 + (float)addp/100.0;
2069 float speed = pre_factor * speed_diff.getLength();
2070 if(speed > tolerance)
2072 f32 damage_f = (speed - tolerance)/BS * post_factor;
2073 u16 damage = (u16)(damage_f+0.5);
2075 damageLocalPlayer(damage, true);
2080 A quick draft of lava damage
2082 if(m_lava_hurt_interval.step(dtime, 1.0))
2084 v3f pf = lplayer->getPosition();
2086 // Feet, middle and head
2087 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2088 MapNode n1 = m_map->getNodeNoEx(p1);
2089 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2090 MapNode n2 = m_map->getNodeNoEx(p2);
2091 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2092 MapNode n3 = m_map->getNodeNoEx(p2);
2094 u32 damage_per_second = 0;
2095 damage_per_second = MYMAX(damage_per_second,
2096 m_gamedef->ndef()->get(n1).damage_per_second);
2097 damage_per_second = MYMAX(damage_per_second,
2098 m_gamedef->ndef()->get(n2).damage_per_second);
2099 damage_per_second = MYMAX(damage_per_second,
2100 m_gamedef->ndef()->get(n3).damage_per_second);
2102 if(damage_per_second != 0)
2104 damageLocalPlayer(damage_per_second, true);
2109 Stuff that can be done in an arbitarily large dtime
2111 for(core::list<Player*>::Iterator i = m_players.begin();
2112 i != m_players.end(); i++)
2114 Player *player = *i;
2115 v3f playerpos = player->getPosition();
2118 Handle non-local players
2120 if(player->isLocal() == false)
2123 player->move(dtime, *m_map, 100*BS);
2127 // Update lighting on all players on client
2128 u8 light = LIGHT_MAX;
2131 v3s16 p = player->getLightPosition();
2132 MapNode n = m_map->getNode(p);
2133 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2135 catch(InvalidPositionException &e){
2136 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2138 player->light = light;
2142 Step active objects and update lighting of them
2145 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2146 for(core::map<u16, ClientActiveObject*>::Iterator
2147 i = m_active_objects.getIterator();
2148 i.atEnd()==false; i++)
2150 ClientActiveObject* obj = i.getNode()->getValue();
2152 obj->step(dtime, this);
2160 v3s16 p = obj->getLightPosition();
2161 MapNode n = m_map->getNode(p);
2162 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2164 catch(InvalidPositionException &e){
2165 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2167 obj->updateLight(light);
2172 Step and handle simple objects
2174 for(core::list<ClientSimpleObject*>::Iterator
2175 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2177 ClientSimpleObject *simple = *i;
2178 core::list<ClientSimpleObject*>::Iterator cur = i;
2180 simple->step(dtime);
2181 if(simple->m_to_be_removed){
2183 m_simple_objects.erase(cur);
2188 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2190 m_simple_objects.push_back(simple);
2193 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2195 core::map<u16, ClientActiveObject*>::Node *n;
2196 n = m_active_objects.find(id);
2199 return n->getValue();
2202 bool isFreeClientActiveObjectId(u16 id,
2203 core::map<u16, ClientActiveObject*> &objects)
2208 for(core::map<u16, ClientActiveObject*>::Iterator
2209 i = objects.getIterator();
2210 i.atEnd()==false; i++)
2212 if(i.getNode()->getKey() == id)
2218 u16 getFreeClientActiveObjectId(
2219 core::map<u16, ClientActiveObject*> &objects)
2224 if(isFreeClientActiveObjectId(new_id, objects))
2234 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2237 if(object->getId() == 0)
2239 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2242 infostream<<"ClientEnvironment::addActiveObject(): "
2243 <<"no free ids available"<<std::endl;
2247 object->setId(new_id);
2249 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2251 infostream<<"ClientEnvironment::addActiveObject(): "
2252 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2256 infostream<<"ClientEnvironment::addActiveObject(): "
2257 <<"added (id="<<object->getId()<<")"<<std::endl;
2258 m_active_objects.insert(object->getId(), object);
2259 object->addToScene(m_smgr, m_texturesource, m_irr);
2260 { // Update lighting immediately
2264 v3s16 p = object->getLightPosition();
2265 MapNode n = m_map->getNode(p);
2266 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2268 catch(InvalidPositionException &e){
2269 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2271 object->updateLight(light);
2273 return object->getId();
2276 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2277 const std::string &init_data)
2279 ClientActiveObject* obj =
2280 ClientActiveObject::create(type, m_gamedef, this);
2283 infostream<<"ClientEnvironment::addActiveObject(): "
2284 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2293 obj->initialize(init_data);
2295 catch(SerializationError &e)
2297 errorstream<<"ClientEnvironment::addActiveObject():"
2298 <<" id="<<id<<" type="<<type
2299 <<": SerializationError in initialize(),"
2300 <<" init_data="<<serializeJsonString(init_data)
2304 addActiveObject(obj);
2307 void ClientEnvironment::removeActiveObject(u16 id)
2309 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2310 <<"id="<<id<<std::endl;
2311 ClientActiveObject* obj = getActiveObject(id);
2314 infostream<<"ClientEnvironment::removeActiveObject(): "
2315 <<"id="<<id<<" not found"<<std::endl;
2318 obj->removeFromScene();
2320 m_active_objects.remove(id);
2323 void ClientEnvironment::processActiveObjectMessage(u16 id,
2324 const std::string &data)
2326 ClientActiveObject* obj = getActiveObject(id);
2329 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2330 <<" got message for id="<<id<<", which doesn't exist."
2336 obj->processMessage(data);
2338 catch(SerializationError &e)
2340 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2341 <<" id="<<id<<" type="<<obj->getType()
2342 <<" SerializationError in processMessage(),"
2343 <<" message="<<serializeJsonString(data)
2349 Callbacks for activeobjects
2352 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2354 LocalPlayer *lplayer = getLocalPlayer();
2358 if(lplayer->hp > damage)
2359 lplayer->hp -= damage;
2364 ClientEnvEvent event;
2365 event.type = CEE_PLAYER_DAMAGE;
2366 event.player_damage.amount = damage;
2367 event.player_damage.send_to_server = handle_hp;
2368 m_client_event_queue.push_back(event);
2372 Client likes to call these
2375 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2376 core::array<DistanceSortedActiveObject> &dest)
2378 for(core::map<u16, ClientActiveObject*>::Iterator
2379 i = m_active_objects.getIterator();
2380 i.atEnd()==false; i++)
2382 ClientActiveObject* obj = i.getNode()->getValue();
2384 f32 d = (obj->getPosition() - origin).getLength();
2389 DistanceSortedActiveObject dso(obj, d);
2391 dest.push_back(dso);
2395 ClientEnvEvent ClientEnvironment::getClientEvent()
2397 if(m_client_event_queue.size() == 0)
2399 ClientEnvEvent event;
2400 event.type = CEE_NONE;
2403 return m_client_event_queue.pop_front();
2406 #endif // #ifndef SERVER