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 bool smooth = (g_settings->getS32("enable_shaders") != 0);
207 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
210 void Environment::stepTimeOfDay(float dtime)
212 m_time_counter += dtime;
213 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
214 u32 units = (u32)(m_time_counter*speed);
215 m_time_counter -= (f32)units / speed;
219 if(m_time_of_day + units >= 24000)
221 m_time_of_day = (m_time_of_day + units) % 24000;
223 m_time_of_day_f = (float)m_time_of_day / 24000.0;
226 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
227 if(m_time_of_day_f > 1.0)
228 m_time_of_day_f -= 1.0;
229 if(m_time_of_day_f < 0.0)
230 m_time_of_day_f += 1.0;
238 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
242 // Initialize timer to random value to spread processing
243 float itv = abm->getTriggerInterval();
244 itv = MYMAX(0.001, itv); // No less than 1ms
245 int minval = MYMAX(-0.51*itv, -60); // Clamp to
246 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
247 timer = myrand_range(minval, maxval);
254 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
257 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
258 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
259 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
266 void ActiveBlockList::update(core::list<v3s16> &active_positions,
268 core::map<v3s16, bool> &blocks_removed,
269 core::map<v3s16, bool> &blocks_added)
274 core::map<v3s16, bool> newlist;
275 for(core::list<v3s16>::Iterator i = active_positions.begin();
276 i != active_positions.end(); i++)
278 fillRadiusBlock(*i, radius, newlist);
282 Find out which blocks on the old list are not on the new list
284 // Go through old list
285 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
286 i.atEnd()==false; i++)
288 v3s16 p = i.getNode()->getKey();
289 // If not on new list, it's been removed
290 if(newlist.find(p) == NULL)
291 blocks_removed.insert(p, true);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
299 i.atEnd()==false; i++)
301 v3s16 p = i.getNode()->getKey();
302 // If not on old list, it's been added
303 if(m_list.find(p) == NULL)
304 blocks_added.insert(p, true);
311 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
312 i.atEnd()==false; i++)
314 v3s16 p = i.getNode()->getKey();
315 m_list.insert(p, true);
323 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
324 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
329 m_random_spawn_timer(3),
330 m_send_recommended_timer(0),
331 m_active_block_interval_overload_skip(0),
333 m_game_time_fraction_counter(0),
334 m_recommended_send_interval(0.1)
338 ServerEnvironment::~ServerEnvironment()
340 // Clear active block list.
341 // This makes the next one delete all active objects.
342 m_active_blocks.clear();
344 // Convert all objects to static and delete the active objects
345 deactivateFarObjects(true);
350 // Delete ActiveBlockModifiers
351 for(core::list<ABMWithState>::Iterator
352 i = m_abms.begin(); i != m_abms.end(); i++){
357 Map & ServerEnvironment::getMap()
362 ServerMap & ServerEnvironment::getServerMap()
368 void ServerEnvironment::serializePlayers(const std::string &savedir)
370 std::string players_path = savedir + "/players";
371 fs::CreateDir(players_path);
373 core::map<Player*, bool> saved_players;
375 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
376 for(u32 i=0; i<player_files.size(); i++)
378 if(player_files[i].dir)
381 // Full path to this file
382 std::string path = players_path + "/" + player_files[i].name;
384 //infostream<<"Checking player file "<<path<<std::endl;
386 // Load player to see what is its name
387 RemotePlayer testplayer(m_gamedef);
389 // Open file and deserialize
390 std::ifstream is(path.c_str(), std::ios_base::binary);
391 if(is.good() == false)
393 infostream<<"Failed to read "<<path<<std::endl;
396 testplayer.deSerialize(is);
399 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
401 // Search for the player
402 std::string playername = testplayer.getName();
403 Player *player = getPlayer(playername.c_str());
406 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
410 //infostream<<"Found matching player, overwriting."<<std::endl;
412 // OK, found. Save player there.
414 // Open file and serialize
415 std::ofstream os(path.c_str(), std::ios_base::binary);
416 if(os.good() == false)
418 infostream<<"Failed to overwrite "<<path<<std::endl;
421 player->serialize(os);
422 saved_players.insert(player, true);
426 for(core::list<Player*>::Iterator i = m_players.begin();
427 i != m_players.end(); i++)
430 if(saved_players.find(player) != NULL)
432 /*infostream<<"Player "<<player->getName()
433 <<" was already saved."<<std::endl;*/
436 std::string playername = player->getName();
437 // Don't save unnamed player
440 //infostream<<"Not saving unnamed player."<<std::endl;
446 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
447 playername = "player";
448 std::string path = players_path + "/" + playername;
450 for(u32 i=0; i<1000; i++)
452 if(fs::PathExists(path) == false)
457 path = players_path + "/" + playername + itos(i);
461 infostream<<"Didn't find free file for player"<<std::endl;
466 /*infostream<<"Saving player "<<player->getName()<<" to "
468 // Open file and serialize
469 std::ofstream os(path.c_str(), std::ios_base::binary);
470 if(os.good() == false)
472 infostream<<"Failed to overwrite "<<path<<std::endl;
475 player->serialize(os);
476 saved_players.insert(player, true);
480 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
483 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
485 std::string players_path = savedir + "/players";
487 core::map<Player*, bool> saved_players;
489 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
490 for(u32 i=0; i<player_files.size(); i++)
492 if(player_files[i].dir)
495 // Full path to this file
496 std::string path = players_path + "/" + player_files[i].name;
498 //infostream<<"Checking player file "<<path<<std::endl;
500 // Load player to see what is its name
501 RemotePlayer testplayer(m_gamedef);
503 // Open file and deserialize
504 std::ifstream is(path.c_str(), std::ios_base::binary);
505 if(is.good() == false)
507 infostream<<"Failed to read "<<path<<std::endl;
510 testplayer.deSerialize(is);
513 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
515 infostream<<"Not loading player with invalid name: "
516 <<testplayer.getName()<<std::endl;
519 /*infostream<<"Loaded test player with name "<<testplayer.getName()
522 // Search for the player
523 std::string playername = testplayer.getName();
524 Player *player = getPlayer(playername.c_str());
525 bool newplayer = false;
528 //infostream<<"Is a new player"<<std::endl;
529 player = new RemotePlayer(m_gamedef);
535 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
537 // Open file and deserialize
538 std::ifstream is(path.c_str(), std::ios_base::binary);
539 if(is.good() == false)
541 infostream<<"Failed to read "<<path<<std::endl;
544 player->deSerialize(is);
554 void ServerEnvironment::saveMeta(const std::string &savedir)
556 std::string path = savedir + "/env_meta.txt";
558 // Open file and serialize
559 std::ofstream os(path.c_str(), std::ios_base::binary);
560 if(os.good() == false)
562 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
564 throw SerializationError("Couldn't save env meta");
568 args.setU64("game_time", m_game_time);
569 args.setU64("time_of_day", getTimeOfDay());
574 void ServerEnvironment::loadMeta(const std::string &savedir)
576 std::string path = savedir + "/env_meta.txt";
578 // Open file and deserialize
579 std::ifstream is(path.c_str(), std::ios_base::binary);
580 if(is.good() == false)
582 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
584 throw SerializationError("Couldn't load env meta");
592 throw SerializationError
593 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
595 std::getline(is, line);
596 std::string trimmedline = trim(line);
597 if(trimmedline == "EnvArgsEnd")
599 args.parseConfigLine(line);
603 m_game_time = args.getU64("game_time");
604 }catch(SettingNotFoundException &e){
605 // Getting this is crucial, otherwise timestamps are useless
606 throw SerializationError("Couldn't load env meta game_time");
610 m_time_of_day = args.getU64("time_of_day");
611 }catch(SettingNotFoundException &e){
612 // This is not as important
613 m_time_of_day = 9000;
619 ActiveBlockModifier *abm;
621 std::set<content_t> required_neighbors;
627 ServerEnvironment *m_env;
628 std::map<content_t, std::list<ActiveABM> > m_aabms;
630 ABMHandler(core::list<ABMWithState> &abms,
631 float dtime_s, ServerEnvironment *env,
637 INodeDefManager *ndef = env->getGameDef()->ndef();
638 for(core::list<ABMWithState>::Iterator
639 i = abms.begin(); i != abms.end(); i++){
640 ActiveBlockModifier *abm = i->abm;
641 float trigger_interval = abm->getTriggerInterval();
642 if(trigger_interval < 0.001)
643 trigger_interval = 0.001;
644 float actual_interval = dtime_s;
647 if(i->timer < trigger_interval)
649 i->timer -= trigger_interval;
650 actual_interval = trigger_interval;
652 float intervals = actual_interval / trigger_interval;
655 float chance = abm->getTriggerChance();
660 aabm.chance = chance / intervals;
664 std::set<std::string> required_neighbors_s
665 = abm->getRequiredNeighbors();
666 for(std::set<std::string>::iterator
667 i = required_neighbors_s.begin();
668 i != required_neighbors_s.end(); i++)
670 ndef->getIds(*i, aabm.required_neighbors);
673 std::set<std::string> contents_s = abm->getTriggerContents();
674 for(std::set<std::string>::iterator
675 i = contents_s.begin(); i != contents_s.end(); i++)
677 std::set<content_t> ids;
678 ndef->getIds(*i, ids);
679 for(std::set<content_t>::const_iterator k = ids.begin();
683 std::map<content_t, std::list<ActiveABM> >::iterator j;
685 if(j == m_aabms.end()){
686 std::list<ActiveABM> aabmlist;
687 m_aabms[c] = aabmlist;
690 j->second.push_back(aabm);
695 void apply(MapBlock *block)
700 ServerMap *map = &m_env->getServerMap();
703 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
704 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
705 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
707 MapNode n = block->getNodeNoEx(p0);
708 content_t c = n.getContent();
709 v3s16 p = p0 + block->getPosRelative();
711 std::map<content_t, std::list<ActiveABM> >::iterator j;
713 if(j == m_aabms.end())
716 for(std::list<ActiveABM>::iterator
717 i = j->second.begin(); i != j->second.end(); i++)
719 if(myrand() % i->chance != 0)
723 if(!i->required_neighbors.empty())
726 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
727 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
728 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
732 MapNode n = map->getNodeNoEx(p1);
733 content_t c = n.getContent();
734 std::set<content_t>::const_iterator k;
735 k = i->required_neighbors.find(c);
736 if(k != i->required_neighbors.end()){
740 // No required neighbor found
745 // Find out how many objects the block contains
746 u32 active_object_count = block->m_static_objects.m_active.size();
747 // Find out how many objects this and all the neighbors contain
748 u32 active_object_count_wider = 0;
749 u32 wider_unknown_count = 0;
750 for(s16 x=-1; x<=1; x++)
751 for(s16 y=-1; y<=1; y++)
752 for(s16 z=-1; z<=1; z++)
754 MapBlock *block2 = map->getBlockNoCreateNoEx(
755 block->getPos() + v3s16(x,y,z));
757 wider_unknown_count = 0;
760 active_object_count_wider +=
761 block2->m_static_objects.m_active.size()
762 + block2->m_static_objects.m_stored.size();
765 u32 wider_known_count = 3*3*3 - wider_unknown_count;
766 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
768 // Call all the trigger variations
769 i->abm->trigger(m_env, p, n);
770 i->abm->trigger(m_env, p, n,
771 active_object_count, active_object_count_wider);
777 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
779 // Get time difference
781 u32 stamp = block->getTimestamp();
782 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
783 dtime_s = m_game_time - block->getTimestamp();
784 dtime_s += additional_dtime;
786 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
787 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
789 // Set current time as timestamp
790 block->setTimestampNoChangedFlag(m_game_time);
792 /*infostream<<"ServerEnvironment::activateBlock(): block is "
793 <<dtime_s<<" seconds old."<<std::endl;*/
795 // Activate stored objects
796 activateObjects(block, dtime_s);
799 std::map<v3s16, NodeTimer> elapsed_timers =
800 block->m_node_timers.step((float)dtime_s);
801 if(!elapsed_timers.empty()){
803 for(std::map<v3s16, NodeTimer>::iterator
804 i = elapsed_timers.begin();
805 i != elapsed_timers.end(); i++){
806 n = block->getNodeNoEx(i->first);
807 v3s16 p = i->first + block->getPosRelative();
808 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
809 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
813 /* Handle ActiveBlockModifiers */
814 ABMHandler abmhandler(m_abms, dtime_s, this, false);
815 abmhandler.apply(block);
818 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
820 m_abms.push_back(ABMWithState(abm));
823 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
825 INodeDefManager *ndef = m_gamedef->ndef();
826 MapNode n_old = m_map->getNodeNoEx(p);
828 if(ndef->get(n_old).has_on_destruct)
829 scriptapi_node_on_destruct(m_lua, p, n_old);
831 bool succeeded = m_map->addNodeWithEvent(p, n);
834 // Call post-destructor
835 if(ndef->get(n_old).has_after_destruct)
836 scriptapi_node_after_destruct(m_lua, p, n_old);
838 if(ndef->get(n).has_on_construct)
839 scriptapi_node_on_construct(m_lua, p, n);
843 bool ServerEnvironment::removeNode(v3s16 p)
845 INodeDefManager *ndef = m_gamedef->ndef();
846 MapNode n_old = m_map->getNodeNoEx(p);
848 if(ndef->get(n_old).has_on_destruct)
849 scriptapi_node_on_destruct(m_lua, p, n_old);
851 // This is slightly optimized compared to addNodeWithEvent(air)
852 bool succeeded = m_map->removeNodeWithEvent(p);
855 // Call post-destructor
856 if(ndef->get(n_old).has_after_destruct)
857 scriptapi_node_after_destruct(m_lua, p, n_old);
858 // Air doesn't require constructor
862 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
864 std::set<u16> objects;
865 for(core::map<u16, ServerActiveObject*>::Iterator
866 i = m_active_objects.getIterator();
867 i.atEnd()==false; i++)
869 ServerActiveObject* obj = i.getNode()->getValue();
870 u16 id = i.getNode()->getKey();
871 v3f objectpos = obj->getBasePosition();
872 if(objectpos.getDistanceFrom(pos) > radius)
879 void ServerEnvironment::clearAllObjects()
881 infostream<<"ServerEnvironment::clearAllObjects(): "
882 <<"Removing all active objects"<<std::endl;
883 core::list<u16> objects_to_remove;
884 for(core::map<u16, ServerActiveObject*>::Iterator
885 i = m_active_objects.getIterator();
886 i.atEnd()==false; i++)
888 ServerActiveObject* obj = i.getNode()->getValue();
889 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
891 u16 id = i.getNode()->getKey();
892 v3f objectpos = obj->getBasePosition();
893 // Delete static object if block is loaded
894 if(obj->m_static_exists){
895 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
897 block->m_static_objects.remove(id);
898 block->raiseModified(MOD_STATE_WRITE_NEEDED,
900 obj->m_static_exists = false;
903 // If known by some client, don't delete immediately
904 if(obj->m_known_by_count > 0){
905 obj->m_pending_deactivation = true;
906 obj->m_removed = true;
910 // Tell the object about removal
911 obj->removingFromEnvironment();
912 // Deregister in scripting api
913 scriptapi_rm_object_reference(m_lua, obj);
915 // Delete active object
916 if(obj->environmentDeletes())
918 // Id to be removed from m_active_objects
919 objects_to_remove.push_back(id);
921 // Remove references from m_active_objects
922 for(core::list<u16>::Iterator i = objects_to_remove.begin();
923 i != objects_to_remove.end(); i++)
925 m_active_objects.remove(*i);
928 core::list<v3s16> loadable_blocks;
929 infostream<<"ServerEnvironment::clearAllObjects(): "
930 <<"Listing all loadable blocks"<<std::endl;
931 m_map->listAllLoadableBlocks(loadable_blocks);
932 infostream<<"ServerEnvironment::clearAllObjects(): "
933 <<"Done listing all loadable blocks: "
934 <<loadable_blocks.size()
935 <<", now clearing"<<std::endl;
936 u32 report_interval = loadable_blocks.size() / 10;
937 u32 num_blocks_checked = 0;
938 u32 num_blocks_cleared = 0;
939 u32 num_objs_cleared = 0;
940 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
941 i != loadable_blocks.end(); i++)
944 MapBlock *block = m_map->emergeBlock(p, false);
946 errorstream<<"ServerEnvironment::clearAllObjects(): "
947 <<"Failed to emerge block "<<PP(p)<<std::endl;
950 u32 num_stored = block->m_static_objects.m_stored.size();
951 u32 num_active = block->m_static_objects.m_active.size();
952 if(num_stored != 0 || num_active != 0){
953 block->m_static_objects.m_stored.clear();
954 block->m_static_objects.m_active.clear();
955 block->raiseModified(MOD_STATE_WRITE_NEEDED,
957 num_objs_cleared += num_stored + num_active;
958 num_blocks_cleared++;
960 num_blocks_checked++;
962 if(num_blocks_checked % report_interval == 0){
963 float percent = 100.0 * (float)num_blocks_checked /
964 loadable_blocks.size();
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Cleared "<<num_objs_cleared<<" objects"
967 <<" in "<<num_blocks_cleared<<" blocks ("
968 <<percent<<"%)"<<std::endl;
971 infostream<<"ServerEnvironment::clearAllObjects(): "
972 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
973 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
976 void ServerEnvironment::step(float dtime)
978 DSTACK(__FUNCTION_NAME);
980 //TimeTaker timer("ServerEnv step");
982 /* Step time of day */
983 stepTimeOfDay(dtime);
986 // NOTE: This is kind of funny on a singleplayer game, but doesn't
987 // really matter that much.
988 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
994 m_game_time_fraction_counter += dtime;
995 u32 inc_i = (u32)m_game_time_fraction_counter;
996 m_game_time += inc_i;
997 m_game_time_fraction_counter -= (float)inc_i;
1004 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1005 for(core::list<Player*>::Iterator i = m_players.begin();
1006 i != m_players.end(); i++)
1008 Player *player = *i;
1010 // Ignore disconnected players
1011 if(player->peer_id == 0)
1014 v3f playerpos = player->getPosition();
1017 player->move(dtime, *m_map, 100*BS);
1022 Manage active block list
1024 if(m_active_blocks_management_interval.step(dtime, 2.0))
1026 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1028 Get player block positions
1030 core::list<v3s16> players_blockpos;
1031 for(core::list<Player*>::Iterator
1032 i = m_players.begin();
1033 i != m_players.end(); i++)
1035 Player *player = *i;
1036 // Ignore disconnected players
1037 if(player->peer_id == 0)
1039 v3s16 blockpos = getNodeBlockPos(
1040 floatToInt(player->getPosition(), BS));
1041 players_blockpos.push_back(blockpos);
1045 Update list of active blocks, collecting changes
1047 const s16 active_block_range = g_settings->getS16("active_block_range");
1048 core::map<v3s16, bool> blocks_removed;
1049 core::map<v3s16, bool> blocks_added;
1050 m_active_blocks.update(players_blockpos, active_block_range,
1051 blocks_removed, blocks_added);
1054 Handle removed blocks
1057 // Convert active objects that are no more in active blocks to static
1058 deactivateFarObjects(false);
1060 for(core::map<v3s16, bool>::Iterator
1061 i = blocks_removed.getIterator();
1062 i.atEnd()==false; i++)
1064 v3s16 p = i.getNode()->getKey();
1066 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1067 <<") became inactive"<<std::endl;*/
1069 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1073 // Set current time as timestamp (and let it set ChangedFlag)
1074 block->setTimestamp(m_game_time);
1081 for(core::map<v3s16, bool>::Iterator
1082 i = blocks_added.getIterator();
1083 i.atEnd()==false; i++)
1085 v3s16 p = i.getNode()->getKey();
1087 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1088 <<") became active"<<std::endl;*/
1090 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1092 // Block needs to be fetched first
1093 m_emerger->queueBlockEmerge(p, false);
1094 m_active_blocks.m_list.remove(p);
1098 activateBlock(block);
1103 Mess around in active blocks
1105 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1107 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1111 for(core::map<v3s16, bool>::Iterator
1112 i = m_active_blocks.m_list.getIterator();
1113 i.atEnd()==false; i++)
1115 v3s16 p = i.getNode()->getKey();
1117 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1118 <<") being handled"<<std::endl;*/
1120 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1124 // Reset block usage timer
1125 block->resetUsageTimer();
1127 // Set current time as timestamp
1128 block->setTimestampNoChangedFlag(m_game_time);
1129 // If time has changed much from the one on disk,
1130 // set block to be saved when it is unloaded
1131 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1132 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1133 "Timestamp older than 60s (step)");
1136 std::map<v3s16, NodeTimer> elapsed_timers =
1137 block->m_node_timers.step((float)dtime);
1138 if(!elapsed_timers.empty()){
1140 for(std::map<v3s16, NodeTimer>::iterator
1141 i = elapsed_timers.begin();
1142 i != elapsed_timers.end(); i++){
1143 n = block->getNodeNoEx(i->first);
1144 p = i->first + block->getPosRelative();
1145 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1146 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1152 const float abm_interval = 1.0;
1153 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1155 if(m_active_block_interval_overload_skip > 0){
1156 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1157 m_active_block_interval_overload_skip--;
1160 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1161 TimeTaker timer("modify in active blocks");
1163 // Initialize handling of ActiveBlockModifiers
1164 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1166 for(core::map<v3s16, bool>::Iterator
1167 i = m_active_blocks.m_list.getIterator();
1168 i.atEnd()==false; i++)
1170 v3s16 p = i.getNode()->getKey();
1172 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1173 <<") being handled"<<std::endl;*/
1175 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1179 // Set current time as timestamp
1180 block->setTimestampNoChangedFlag(m_game_time);
1182 /* Handle ActiveBlockModifiers */
1183 abmhandler.apply(block);
1186 u32 time_ms = timer.stop(true);
1187 u32 max_time_ms = 200;
1188 if(time_ms > max_time_ms){
1189 infostream<<"WARNING: active block modifiers took "
1190 <<time_ms<<"ms (longer than "
1191 <<max_time_ms<<"ms)"<<std::endl;
1192 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1197 Step script environment (run global on_step())
1199 scriptapi_environment_step(m_lua, dtime);
1205 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1206 //TimeTaker timer("Step active objects");
1208 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1210 // This helps the objects to send data at the same time
1211 bool send_recommended = false;
1212 m_send_recommended_timer += dtime;
1213 if(m_send_recommended_timer > getSendRecommendedInterval())
1215 m_send_recommended_timer -= getSendRecommendedInterval();
1216 send_recommended = true;
1219 for(core::map<u16, ServerActiveObject*>::Iterator
1220 i = m_active_objects.getIterator();
1221 i.atEnd()==false; i++)
1223 ServerActiveObject* obj = i.getNode()->getValue();
1224 // Remove non-peaceful mobs on peaceful mode
1225 if(g_settings->getBool("only_peaceful_mobs")){
1226 if(!obj->isPeaceful())
1227 obj->m_removed = true;
1229 // Don't step if is to be removed or stored statically
1230 if(obj->m_removed || obj->m_pending_deactivation)
1233 obj->step(dtime, send_recommended);
1234 // Read messages from object
1235 while(obj->m_messages_out.size() > 0)
1237 m_active_object_messages.push_back(
1238 obj->m_messages_out.pop_front());
1244 Manage active objects
1246 if(m_object_management_interval.step(dtime, 0.5))
1248 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1250 Remove objects that satisfy (m_removed && m_known_by_count==0)
1252 removeRemovedObjects();
1256 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1258 core::map<u16, ServerActiveObject*>::Node *n;
1259 n = m_active_objects.find(id);
1262 return n->getValue();
1265 bool isFreeServerActiveObjectId(u16 id,
1266 core::map<u16, ServerActiveObject*> &objects)
1271 for(core::map<u16, ServerActiveObject*>::Iterator
1272 i = objects.getIterator();
1273 i.atEnd()==false; i++)
1275 if(i.getNode()->getKey() == id)
1281 u16 getFreeServerActiveObjectId(
1282 core::map<u16, ServerActiveObject*> &objects)
1287 if(isFreeServerActiveObjectId(new_id, objects))
1297 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1300 u16 id = addActiveObjectRaw(object, true, 0);
1304 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1308 v3f objectpos = obj->getBasePosition();
1310 // The block in which the object resides in
1311 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1314 Update the static data
1317 // Create new static object
1318 std::string staticdata = obj->getStaticData();
1319 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1320 // Add to the block where the object is located in
1321 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1322 // Get or generate the block
1323 MapBlock *block = m_map->emergeBlock(blockpos);
1325 bool succeeded = false;
1329 block->m_static_objects.insert(0, s_obj);
1330 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1331 "addActiveObjectAsStatic");
1335 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1336 <<"Could not find or generate "
1337 <<"a block for storing static object"<<std::endl;
1341 if(obj->environmentDeletes())
1348 Finds out what new objects have been added to
1349 inside a radius around a position
1351 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1352 core::map<u16, bool> ¤t_objects,
1353 core::map<u16, bool> &added_objects)
1355 v3f pos_f = intToFloat(pos, BS);
1356 f32 radius_f = radius * BS;
1358 Go through the object list,
1359 - discard m_removed objects,
1360 - discard objects that are too far away,
1361 - discard objects that are found in current_objects.
1362 - add remaining objects to added_objects
1364 for(core::map<u16, ServerActiveObject*>::Iterator
1365 i = m_active_objects.getIterator();
1366 i.atEnd()==false; i++)
1368 u16 id = i.getNode()->getKey();
1370 ServerActiveObject *object = i.getNode()->getValue();
1373 // Discard if removed
1374 if(object->m_removed)
1376 if(object->unlimitedTransferDistance() == false){
1377 // Discard if too far
1378 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1379 if(distance_f > radius_f)
1382 // Discard if already on current_objects
1383 core::map<u16, bool>::Node *n;
1384 n = current_objects.find(id);
1387 // Add to added_objects
1388 added_objects.insert(id, false);
1393 Finds out what objects have been removed from
1394 inside a radius around a position
1396 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1397 core::map<u16, bool> ¤t_objects,
1398 core::map<u16, bool> &removed_objects)
1400 v3f pos_f = intToFloat(pos, BS);
1401 f32 radius_f = radius * BS;
1403 Go through current_objects; object is removed if:
1404 - object is not found in m_active_objects (this is actually an
1405 error condition; objects should be set m_removed=true and removed
1406 only after all clients have been informed about removal), or
1407 - object has m_removed=true, or
1408 - object is too far away
1410 for(core::map<u16, bool>::Iterator
1411 i = current_objects.getIterator();
1412 i.atEnd()==false; i++)
1414 u16 id = i.getNode()->getKey();
1415 ServerActiveObject *object = getActiveObject(id);
1418 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1419 <<" object in current_objects is NULL"<<std::endl;
1420 removed_objects.insert(id, false);
1424 if(object->m_removed)
1426 removed_objects.insert(id, false);
1430 // If transfer distance is unlimited, don't remove
1431 if(object->unlimitedTransferDistance())
1434 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1436 if(distance_f >= radius_f)
1438 removed_objects.insert(id, false);
1446 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1448 if(m_active_object_messages.size() == 0)
1449 return ActiveObjectMessage(0);
1451 return m_active_object_messages.pop_front();
1455 ************ Private methods *************
1458 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1459 bool set_changed, u32 dtime_s)
1462 if(object->getId() == 0){
1463 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1466 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1467 <<"no free ids available"<<std::endl;
1468 if(object->environmentDeletes())
1472 object->setId(new_id);
1475 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1476 <<"supplied with id "<<object->getId()<<std::endl;
1478 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1480 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1481 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1482 if(object->environmentDeletes())
1486 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1487 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1489 m_active_objects.insert(object->getId(), object);
1491 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1492 <<"Added id="<<object->getId()<<"; there are now "
1493 <<m_active_objects.size()<<" active objects."
1496 // Register reference in scripting api (must be done before post-init)
1497 scriptapi_add_object_reference(m_lua, object);
1498 // Post-initialize object
1499 object->addedToEnvironment(dtime_s);
1501 // Add static data to block
1502 if(object->isStaticAllowed())
1504 // Add static object to active static list of the block
1505 v3f objectpos = object->getBasePosition();
1506 std::string staticdata = object->getStaticData();
1507 StaticObject s_obj(object->getType(), objectpos, staticdata);
1508 // Add to the block where the object is located in
1509 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1510 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1513 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1514 object->m_static_exists = true;
1515 object->m_static_block = blockpos;
1518 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1519 "addActiveObjectRaw");
1522 v3s16 p = floatToInt(objectpos, BS);
1523 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"could not find block for storing id="<<object->getId()
1525 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1529 return object->getId();
1533 Remove objects that satisfy (m_removed && m_known_by_count==0)
1535 void ServerEnvironment::removeRemovedObjects()
1537 core::list<u16> objects_to_remove;
1538 for(core::map<u16, ServerActiveObject*>::Iterator
1539 i = m_active_objects.getIterator();
1540 i.atEnd()==false; i++)
1542 u16 id = i.getNode()->getKey();
1543 ServerActiveObject* obj = i.getNode()->getValue();
1544 // This shouldn't happen but check it
1547 infostream<<"NULL object found in ServerEnvironment"
1548 <<" while finding removed objects. id="<<id<<std::endl;
1549 // Id to be removed from m_active_objects
1550 objects_to_remove.push_back(id);
1555 We will delete objects that are marked as removed or thatare
1556 waiting for deletion after deactivation
1558 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1562 Delete static data from block if is marked as removed
1564 if(obj->m_static_exists && obj->m_removed)
1566 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1569 block->m_static_objects.remove(id);
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 "removeRemovedObjects");
1572 obj->m_static_exists = false;
1576 // If m_known_by_count > 0, don't actually remove.
1577 if(obj->m_known_by_count > 0)
1580 // Tell the object about removal
1581 obj->removingFromEnvironment();
1582 // Deregister in scripting api
1583 scriptapi_rm_object_reference(m_lua, obj);
1586 if(obj->environmentDeletes())
1588 // Id to be removed from m_active_objects
1589 objects_to_remove.push_back(id);
1591 // Remove references from m_active_objects
1592 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1593 i != objects_to_remove.end(); i++)
1595 m_active_objects.remove(*i);
1599 static void print_hexdump(std::ostream &o, const std::string &data)
1601 const int linelength = 16;
1602 for(int l=0; ; l++){
1603 int i0 = linelength * l;
1604 bool at_end = false;
1605 int thislinelength = linelength;
1606 if(i0 + thislinelength > (int)data.size()){
1607 thislinelength = data.size() - i0;
1610 for(int di=0; di<linelength; di++){
1613 if(di<thislinelength)
1614 snprintf(buf, 4, "%.2x ", data[i]);
1616 snprintf(buf, 4, " ");
1620 for(int di=0; di<thislinelength; di++){
1634 Convert stored objects from blocks near the players to active.
1636 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1640 // Ignore if no stored objects (to not set changed flag)
1641 if(block->m_static_objects.m_stored.size() == 0)
1643 verbosestream<<"ServerEnvironment::activateObjects(): "
1644 <<"activating objects of block "<<PP(block->getPos())
1645 <<" ("<<block->m_static_objects.m_stored.size()
1646 <<" objects)"<<std::endl;
1647 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1649 errorstream<<"suspiciously large amount of objects detected: "
1650 <<block->m_static_objects.m_stored.size()<<" in "
1651 <<PP(block->getPos())
1652 <<"; removing all of them."<<std::endl;
1653 // Clear stored list
1654 block->m_static_objects.m_stored.clear();
1655 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1656 "stored list cleared in activateObjects due to "
1657 "large amount of objects");
1660 // A list for objects that couldn't be converted to active for some
1661 // reason. They will be stored back.
1662 core::list<StaticObject> new_stored;
1663 // Loop through stored static objects
1664 for(core::list<StaticObject>::Iterator
1665 i = block->m_static_objects.m_stored.begin();
1666 i != block->m_static_objects.m_stored.end(); i++)
1668 /*infostream<<"Server: Creating an active object from "
1669 <<"static data"<<std::endl;*/
1670 StaticObject &s_obj = *i;
1671 // Create an active object from the data
1672 ServerActiveObject *obj = ServerActiveObject::create
1673 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1674 // If couldn't create object, store static data back.
1677 errorstream<<"ServerEnvironment::activateObjects(): "
1678 <<"failed to create active object from static object "
1679 <<"in block "<<PP(s_obj.pos/BS)
1680 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1681 print_hexdump(verbosestream, s_obj.data);
1683 new_stored.push_back(s_obj);
1686 verbosestream<<"ServerEnvironment::activateObjects(): "
1687 <<"activated static object pos="<<PP(s_obj.pos/BS)
1688 <<" type="<<(int)s_obj.type<<std::endl;
1689 // This will also add the object to the active static list
1690 addActiveObjectRaw(obj, false, dtime_s);
1692 // Clear stored list
1693 block->m_static_objects.m_stored.clear();
1694 // Add leftover failed stuff to stored list
1695 for(core::list<StaticObject>::Iterator
1696 i = new_stored.begin();
1697 i != new_stored.end(); i++)
1699 StaticObject &s_obj = *i;
1700 block->m_static_objects.m_stored.push_back(s_obj);
1703 Note: Block hasn't really been modified here.
1704 The objects have just been activated and moved from the stored
1705 static list to the active static list.
1706 As such, the block is essentially the same.
1707 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1708 Otherwise there would be a huge amount of unnecessary I/O.
1713 Convert objects that are not standing inside active blocks to static.
1715 If m_known_by_count != 0, active object is not deleted, but static
1716 data is still updated.
1718 If force_delete is set, active object is deleted nevertheless. It
1719 shall only be set so in the destructor of the environment.
1721 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1723 core::list<u16> objects_to_remove;
1724 for(core::map<u16, ServerActiveObject*>::Iterator
1725 i = m_active_objects.getIterator();
1726 i.atEnd()==false; i++)
1728 ServerActiveObject* obj = i.getNode()->getValue();
1731 // Do not deactivate if static data creation not allowed
1732 if(!force_delete && !obj->isStaticAllowed())
1735 // If pending deactivation, let removeRemovedObjects() do it
1736 if(!force_delete && obj->m_pending_deactivation)
1739 u16 id = i.getNode()->getKey();
1740 v3f objectpos = obj->getBasePosition();
1742 // The block in which the object resides in
1743 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1745 // If block is active, don't remove
1746 if(!force_delete && m_active_blocks.contains(blockpos_o))
1749 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1750 <<"deactivating object id="<<id<<" on inactive block "
1751 <<PP(blockpos_o)<<std::endl;
1753 // If known by some client, don't immediately delete.
1754 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1757 Update the static data
1760 if(obj->isStaticAllowed())
1762 // Create new static object
1763 std::string staticdata_new = obj->getStaticData();
1764 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1766 bool stays_in_same_block = false;
1767 bool data_changed = true;
1769 if(obj->m_static_exists){
1770 if(obj->m_static_block == blockpos_o)
1771 stays_in_same_block = true;
1773 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1775 core::map<u16, StaticObject>::Node *n =
1776 block->m_static_objects.m_active.find(id);
1778 StaticObject static_old = n->getValue();
1780 float save_movem = obj->getMinimumSavedMovement();
1782 if(static_old.data == staticdata_new &&
1783 (static_old.pos - objectpos).getLength() < save_movem)
1784 data_changed = false;
1786 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1787 <<"id="<<id<<" m_static_exists=true but "
1788 <<"static data doesn't actually exist in "
1789 <<PP(obj->m_static_block)<<std::endl;
1793 bool shall_be_written = (!stays_in_same_block || data_changed);
1795 // Delete old static object
1796 if(obj->m_static_exists)
1798 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1801 block->m_static_objects.remove(id);
1802 obj->m_static_exists = false;
1803 // Only mark block as modified if data changed considerably
1804 if(shall_be_written)
1805 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1806 "deactivateFarObjects: Static data "
1807 "changed considerably");
1811 // Add to the block where the object is located in
1812 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1813 // Get or generate the block
1814 MapBlock *block = NULL;
1816 block = m_map->emergeBlock(blockpos);
1817 } catch(InvalidPositionException &e){
1818 // Handled via NULL pointer
1823 if(block->m_static_objects.m_stored.size() >= 49){
1824 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1825 <<" statically but block "<<PP(blockpos)
1826 <<" already contains "
1827 <<block->m_static_objects.m_stored.size()
1828 <<" (over 49) objects."
1829 <<" Forcing delete."<<std::endl;
1830 force_delete = true;
1832 u16 new_id = pending_delete ? id : 0;
1833 // If static counterpart already exists, remove it first.
1834 // This shouldn't happen, but happens rarely for some
1835 // unknown reason. Unsuccessful attempts have been made to
1836 // find said reason.
1837 if(new_id && block->m_static_objects.m_active.find(new_id)){
1838 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1840 block->m_static_objects.remove(new_id);
1842 block->m_static_objects.insert(new_id, s_obj);
1844 // Only mark block as modified if data changed considerably
1845 if(shall_be_written)
1846 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1847 "deactivateFarObjects: Static data "
1848 "changed considerably");
1850 obj->m_static_exists = true;
1851 obj->m_static_block = block->getPos();
1856 v3s16 p = floatToInt(objectpos, BS);
1857 errorstream<<"ServerEnv: Could not find or generate "
1858 <<"a block for storing id="<<obj->getId()
1859 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1866 If known by some client, set pending deactivation.
1867 Otherwise delete it immediately.
1870 if(pending_delete && !force_delete)
1872 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1873 <<"object id="<<id<<" is known by clients"
1874 <<"; not deleting yet"<<std::endl;
1876 obj->m_pending_deactivation = true;
1880 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1881 <<"object id="<<id<<" is not known by clients"
1882 <<"; deleting"<<std::endl;
1884 // Tell the object about removal
1885 obj->removingFromEnvironment();
1886 // Deregister in scripting api
1887 scriptapi_rm_object_reference(m_lua, obj);
1889 // Delete active object
1890 if(obj->environmentDeletes())
1892 // Id to be removed from m_active_objects
1893 objects_to_remove.push_back(id);
1896 // Remove references from m_active_objects
1897 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1898 i != objects_to_remove.end(); i++)
1900 m_active_objects.remove(*i);
1907 #include "clientsimpleobject.h"
1913 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1914 ITextureSource *texturesource, IGameDef *gamedef,
1915 IrrlichtDevice *irr):
1918 m_texturesource(texturesource),
1924 ClientEnvironment::~ClientEnvironment()
1926 // delete active objects
1927 for(core::map<u16, ClientActiveObject*>::Iterator
1928 i = m_active_objects.getIterator();
1929 i.atEnd()==false; i++)
1931 delete i.getNode()->getValue();
1934 for(core::list<ClientSimpleObject*>::Iterator
1935 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1944 Map & ClientEnvironment::getMap()
1949 ClientMap & ClientEnvironment::getClientMap()
1954 void ClientEnvironment::addPlayer(Player *player)
1956 DSTACK(__FUNCTION_NAME);
1958 It is a failure if player is local and there already is a local
1961 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1963 Environment::addPlayer(player);
1966 LocalPlayer * ClientEnvironment::getLocalPlayer()
1968 for(core::list<Player*>::Iterator i = m_players.begin();
1969 i != m_players.end(); i++)
1971 Player *player = *i;
1972 if(player->isLocal())
1973 return (LocalPlayer*)player;
1978 void ClientEnvironment::step(float dtime)
1980 DSTACK(__FUNCTION_NAME);
1982 /* Step time of day */
1983 stepTimeOfDay(dtime);
1985 // Get some settings
1986 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1987 bool free_move = fly_allowed && g_settings->getBool("free_move");
1990 LocalPlayer *lplayer = getLocalPlayer();
1992 // collision info queue
1993 core::list<CollisionInfo> player_collisions;
1996 Get the speed the player is going
1998 bool is_climbing = lplayer->is_climbing;
2000 f32 player_speed = lplayer->getSpeed().getLength();
2003 Maximum position increment
2005 //f32 position_max_increment = 0.05*BS;
2006 f32 position_max_increment = 0.1*BS;
2008 // Maximum time increment (for collision detection etc)
2009 // time = distance / speed
2010 f32 dtime_max_increment = 1;
2011 if(player_speed > 0.001)
2012 dtime_max_increment = position_max_increment / player_speed;
2014 // Maximum time increment is 10ms or lower
2015 if(dtime_max_increment > 0.01)
2016 dtime_max_increment = 0.01;
2018 // Don't allow overly huge dtime
2022 f32 dtime_downcount = dtime;
2025 Stuff that has a maximum time increment
2034 if(dtime_downcount > dtime_max_increment)
2036 dtime_part = dtime_max_increment;
2037 dtime_downcount -= dtime_part;
2041 dtime_part = dtime_downcount;
2043 Setting this to 0 (no -=dtime_part) disables an infinite loop
2044 when dtime_part is so small that dtime_downcount -= dtime_part
2047 dtime_downcount = 0;
2055 v3f lplayerpos = lplayer->getPosition();
2058 if(free_move == false && is_climbing == false)
2061 v3f speed = lplayer->getSpeed();
2062 if(lplayer->swimming_up == false)
2063 speed.Y -= 9.81 * BS * dtime_part * 2;
2066 if(lplayer->in_water_stable || lplayer->in_water)
2068 f32 max_down = 2.0*BS;
2069 if(speed.Y < -max_down) speed.Y = -max_down;
2072 if(speed.getLength() > max)
2074 speed = speed / speed.getLength() * max;
2078 lplayer->setSpeed(speed);
2083 This also does collision detection.
2085 lplayer->move(dtime_part, *m_map, position_max_increment,
2086 &player_collisions);
2089 while(dtime_downcount > 0.001);
2091 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2093 for(core::list<CollisionInfo>::Iterator
2094 i = player_collisions.begin();
2095 i != player_collisions.end(); i++)
2097 CollisionInfo &info = *i;
2098 v3f speed_diff = info.new_speed - info.old_speed;;
2099 // Handle only fall damage
2100 // (because otherwise walking against something in fast_move kills you)
2101 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2103 // Get rid of other components
2106 f32 pre_factor = 1; // 1 hp per node/s
2107 f32 tolerance = BS*14; // 5 without damage
2108 f32 post_factor = 1; // 1 hp per node/s
2109 if(info.type == COLLISION_NODE)
2111 const ContentFeatures &f = m_gamedef->ndef()->
2112 get(m_map->getNodeNoEx(info.node_p));
2113 // Determine fall damage multiplier
2114 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2115 pre_factor = 1.0 + (float)addp/100.0;
2117 float speed = pre_factor * speed_diff.getLength();
2118 if(speed > tolerance)
2120 f32 damage_f = (speed - tolerance)/BS * post_factor;
2121 u16 damage = (u16)(damage_f+0.5);
2123 damageLocalPlayer(damage, true);
2128 A quick draft of lava damage
2130 if(m_lava_hurt_interval.step(dtime, 1.0))
2132 v3f pf = lplayer->getPosition();
2134 // Feet, middle and head
2135 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2136 MapNode n1 = m_map->getNodeNoEx(p1);
2137 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2138 MapNode n2 = m_map->getNodeNoEx(p2);
2139 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2140 MapNode n3 = m_map->getNodeNoEx(p2);
2142 u32 damage_per_second = 0;
2143 damage_per_second = MYMAX(damage_per_second,
2144 m_gamedef->ndef()->get(n1).damage_per_second);
2145 damage_per_second = MYMAX(damage_per_second,
2146 m_gamedef->ndef()->get(n2).damage_per_second);
2147 damage_per_second = MYMAX(damage_per_second,
2148 m_gamedef->ndef()->get(n3).damage_per_second);
2150 if(damage_per_second != 0)
2152 damageLocalPlayer(damage_per_second, true);
2157 Stuff that can be done in an arbitarily large dtime
2159 for(core::list<Player*>::Iterator i = m_players.begin();
2160 i != m_players.end(); i++)
2162 Player *player = *i;
2163 v3f playerpos = player->getPosition();
2166 Handle non-local players
2168 if(player->isLocal() == false)
2171 player->move(dtime, *m_map, 100*BS);
2175 // Update lighting on all players on client
2179 v3s16 p = player->getLightPosition();
2180 MapNode n = m_map->getNode(p);
2181 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2183 catch(InvalidPositionException &e){
2184 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2186 player->light = light;
2190 Step active objects and update lighting of them
2193 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2194 for(core::map<u16, ClientActiveObject*>::Iterator
2195 i = m_active_objects.getIterator();
2196 i.atEnd()==false; i++)
2198 ClientActiveObject* obj = i.getNode()->getValue();
2200 obj->step(dtime, this);
2208 v3s16 p = obj->getLightPosition();
2209 MapNode n = m_map->getNode(p);
2210 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2212 catch(InvalidPositionException &e){
2213 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2215 obj->updateLight(light);
2220 Step and handle simple objects
2222 for(core::list<ClientSimpleObject*>::Iterator
2223 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2225 ClientSimpleObject *simple = *i;
2226 core::list<ClientSimpleObject*>::Iterator cur = i;
2228 simple->step(dtime);
2229 if(simple->m_to_be_removed){
2231 m_simple_objects.erase(cur);
2236 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2238 m_simple_objects.push_back(simple);
2241 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2243 core::map<u16, ClientActiveObject*>::Node *n;
2244 n = m_active_objects.find(id);
2247 return n->getValue();
2250 bool isFreeClientActiveObjectId(u16 id,
2251 core::map<u16, ClientActiveObject*> &objects)
2256 for(core::map<u16, ClientActiveObject*>::Iterator
2257 i = objects.getIterator();
2258 i.atEnd()==false; i++)
2260 if(i.getNode()->getKey() == id)
2266 u16 getFreeClientActiveObjectId(
2267 core::map<u16, ClientActiveObject*> &objects)
2272 if(isFreeClientActiveObjectId(new_id, objects))
2282 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2285 if(object->getId() == 0)
2287 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2290 infostream<<"ClientEnvironment::addActiveObject(): "
2291 <<"no free ids available"<<std::endl;
2295 object->setId(new_id);
2297 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2299 infostream<<"ClientEnvironment::addActiveObject(): "
2300 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2304 infostream<<"ClientEnvironment::addActiveObject(): "
2305 <<"added (id="<<object->getId()<<")"<<std::endl;
2306 m_active_objects.insert(object->getId(), object);
2307 object->addToScene(m_smgr, m_texturesource, m_irr);
2308 { // Update lighting immediately
2312 v3s16 p = object->getLightPosition();
2313 MapNode n = m_map->getNode(p);
2314 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2316 catch(InvalidPositionException &e){
2317 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2319 object->updateLight(light);
2321 return object->getId();
2324 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2325 const std::string &init_data)
2327 ClientActiveObject* obj =
2328 ClientActiveObject::create(type, m_gamedef, this);
2331 infostream<<"ClientEnvironment::addActiveObject(): "
2332 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2341 obj->initialize(init_data);
2343 catch(SerializationError &e)
2345 errorstream<<"ClientEnvironment::addActiveObject():"
2346 <<" id="<<id<<" type="<<type
2347 <<": SerializationError in initialize(): "
2349 <<": init_data="<<serializeJsonString(init_data)
2353 addActiveObject(obj);
2356 void ClientEnvironment::removeActiveObject(u16 id)
2358 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2359 <<"id="<<id<<std::endl;
2360 ClientActiveObject* obj = getActiveObject(id);
2363 infostream<<"ClientEnvironment::removeActiveObject(): "
2364 <<"id="<<id<<" not found"<<std::endl;
2367 obj->removeFromScene(true);
2369 m_active_objects.remove(id);
2372 void ClientEnvironment::processActiveObjectMessage(u16 id,
2373 const std::string &data)
2375 ClientActiveObject* obj = getActiveObject(id);
2378 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2379 <<" got message for id="<<id<<", which doesn't exist."
2385 obj->processMessage(data);
2387 catch(SerializationError &e)
2389 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2390 <<" id="<<id<<" type="<<obj->getType()
2391 <<" SerializationError in processMessage(),"
2392 <<" message="<<serializeJsonString(data)
2398 Callbacks for activeobjects
2401 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2403 LocalPlayer *lplayer = getLocalPlayer();
2407 if(lplayer->hp > damage)
2408 lplayer->hp -= damage;
2413 ClientEnvEvent event;
2414 event.type = CEE_PLAYER_DAMAGE;
2415 event.player_damage.amount = damage;
2416 event.player_damage.send_to_server = handle_hp;
2417 m_client_event_queue.push_back(event);
2421 Client likes to call these
2424 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2425 core::array<DistanceSortedActiveObject> &dest)
2427 for(core::map<u16, ClientActiveObject*>::Iterator
2428 i = m_active_objects.getIterator();
2429 i.atEnd()==false; i++)
2431 ClientActiveObject* obj = i.getNode()->getValue();
2433 f32 d = (obj->getPosition() - origin).getLength();
2438 DistanceSortedActiveObject dso(obj, d);
2440 dest.push_back(dso);
2444 ClientEnvEvent ClientEnvironment::getClientEvent()
2446 if(m_client_event_queue.size() == 0)
2448 ClientEnvEvent event;
2449 event.type = CEE_NONE;
2452 return m_client_event_queue.pop_front();
2455 #endif // #ifndef SERVER