3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
44 #include "daynightratio.h"
46 #include "util/serialize.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
58 Environment::~Environment()
61 for(core::list<Player*>::Iterator i = m_players.begin();
62 i != m_players.end(); i++)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(core::list<Player*>::Iterator i = m_players.begin();
90 i != m_players.end(); i++)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(core::list<Player*>::Iterator i = m_players.begin();
107 i != m_players.end(); i++)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(core::list<Player*>::Iterator i = m_players.begin();
119 i != m_players.end(); i++)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 core::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 core::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(core::list<Player*>::Iterator
153 i = connected_players.begin();
154 i != connected_players.end(); i++)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 core::list<Player*> Environment::getPlayers()
172 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 core::list<Player*> newlist;
175 for(core::list<Player*>::Iterator
176 i = m_players.begin();
177 i != m_players.end(); i++)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 void Environment::printPlayers(std::ostream &o)
195 o<<"Players in environment:"<<std::endl;
196 for(core::list<Player*>::Iterator i = m_players.begin();
197 i != m_players.end(); i++)
200 o<<"Player peer_id="<<player->peer_id<<std::endl;
204 u32 Environment::getDayNightRatio()
206 return time_to_daynight_ratio(m_time_of_day);
209 void Environment::stepTimeOfDay(float dtime)
211 m_time_counter += dtime;
212 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
213 u32 units = (u32)(m_time_counter*speed);
214 m_time_counter -= (f32)units / speed;
218 if(m_time_of_day + units >= 24000)
220 m_time_of_day = (m_time_of_day + units) % 24000;
222 m_time_of_day_f = (float)m_time_of_day / 24000.0;
225 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
226 if(m_time_of_day_f > 1.0)
227 m_time_of_day_f -= 1.0;
228 if(m_time_of_day_f < 0.0)
229 m_time_of_day_f += 1.0;
237 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
241 // Initialize timer to random value to spread processing
242 float itv = abm->getTriggerInterval();
243 itv = MYMAX(0.001, itv); // No less than 1ms
244 int minval = MYMAX(-0.51*itv, -60); // Clamp to
245 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
246 timer = myrand_range(minval, maxval);
253 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
256 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
257 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
258 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
265 void ActiveBlockList::update(core::list<v3s16> &active_positions,
267 core::map<v3s16, bool> &blocks_removed,
268 core::map<v3s16, bool> &blocks_added)
273 core::map<v3s16, bool> newlist;
274 for(core::list<v3s16>::Iterator i = active_positions.begin();
275 i != active_positions.end(); i++)
277 fillRadiusBlock(*i, radius, newlist);
281 Find out which blocks on the old list are not on the new list
283 // Go through old list
284 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
285 i.atEnd()==false; i++)
287 v3s16 p = i.getNode()->getKey();
288 // If not on new list, it's been removed
289 if(newlist.find(p) == NULL)
290 blocks_removed.insert(p, true);
294 Find out which blocks on the new list are not on the old list
296 // Go through new list
297 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
298 i.atEnd()==false; i++)
300 v3s16 p = i.getNode()->getKey();
301 // If not on old list, it's been added
302 if(m_list.find(p) == NULL)
303 blocks_added.insert(p, true);
310 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
311 i.atEnd()==false; i++)
313 v3s16 p = i.getNode()->getKey();
314 m_list.insert(p, true);
322 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
323 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
328 m_random_spawn_timer(3),
329 m_send_recommended_timer(0),
330 m_active_block_interval_overload_skip(0),
332 m_game_time_fraction_counter(0),
333 m_recommended_send_interval(0.1)
337 ServerEnvironment::~ServerEnvironment()
339 // Clear active block list.
340 // This makes the next one delete all active objects.
341 m_active_blocks.clear();
343 // Convert all objects to static and delete the active objects
344 deactivateFarObjects(true);
349 // Delete ActiveBlockModifiers
350 for(core::list<ABMWithState>::Iterator
351 i = m_abms.begin(); i != m_abms.end(); i++){
356 Map & ServerEnvironment::getMap()
361 ServerMap & ServerEnvironment::getServerMap()
367 void ServerEnvironment::serializePlayers(const std::string &savedir)
369 std::string players_path = savedir + "/players";
370 fs::CreateDir(players_path);
372 core::map<Player*, bool> saved_players;
374 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
375 for(u32 i=0; i<player_files.size(); i++)
377 if(player_files[i].dir)
380 // Full path to this file
381 std::string path = players_path + "/" + player_files[i].name;
383 //infostream<<"Checking player file "<<path<<std::endl;
385 // Load player to see what is its name
386 RemotePlayer testplayer(m_gamedef);
388 // Open file and deserialize
389 std::ifstream is(path.c_str(), std::ios_base::binary);
390 if(is.good() == false)
392 infostream<<"Failed to read "<<path<<std::endl;
395 testplayer.deSerialize(is);
398 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
400 // Search for the player
401 std::string playername = testplayer.getName();
402 Player *player = getPlayer(playername.c_str());
405 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
409 //infostream<<"Found matching player, overwriting."<<std::endl;
411 // OK, found. Save player there.
413 // Open file and serialize
414 std::ofstream os(path.c_str(), std::ios_base::binary);
415 if(os.good() == false)
417 infostream<<"Failed to overwrite "<<path<<std::endl;
420 player->serialize(os);
421 saved_players.insert(player, true);
425 for(core::list<Player*>::Iterator i = m_players.begin();
426 i != m_players.end(); i++)
429 if(saved_players.find(player) != NULL)
431 /*infostream<<"Player "<<player->getName()
432 <<" was already saved."<<std::endl;*/
435 std::string playername = player->getName();
436 // Don't save unnamed player
439 //infostream<<"Not saving unnamed player."<<std::endl;
445 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
446 playername = "player";
447 std::string path = players_path + "/" + playername;
449 for(u32 i=0; i<1000; i++)
451 if(fs::PathExists(path) == false)
456 path = players_path + "/" + playername + itos(i);
460 infostream<<"Didn't find free file for player"<<std::endl;
465 /*infostream<<"Saving player "<<player->getName()<<" to "
467 // Open file and serialize
468 std::ofstream os(path.c_str(), std::ios_base::binary);
469 if(os.good() == false)
471 infostream<<"Failed to overwrite "<<path<<std::endl;
474 player->serialize(os);
475 saved_players.insert(player, true);
479 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
482 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
484 std::string players_path = savedir + "/players";
486 core::map<Player*, bool> saved_players;
488 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
489 for(u32 i=0; i<player_files.size(); i++)
491 if(player_files[i].dir)
494 // Full path to this file
495 std::string path = players_path + "/" + player_files[i].name;
497 //infostream<<"Checking player file "<<path<<std::endl;
499 // Load player to see what is its name
500 RemotePlayer testplayer(m_gamedef);
502 // Open file and deserialize
503 std::ifstream is(path.c_str(), std::ios_base::binary);
504 if(is.good() == false)
506 infostream<<"Failed to read "<<path<<std::endl;
509 testplayer.deSerialize(is);
512 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
514 infostream<<"Not loading player with invalid name: "
515 <<testplayer.getName()<<std::endl;
518 /*infostream<<"Loaded test player with name "<<testplayer.getName()
521 // Search for the player
522 std::string playername = testplayer.getName();
523 Player *player = getPlayer(playername.c_str());
524 bool newplayer = false;
527 //infostream<<"Is a new player"<<std::endl;
528 player = new RemotePlayer(m_gamedef);
534 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
536 // Open file and deserialize
537 std::ifstream is(path.c_str(), std::ios_base::binary);
538 if(is.good() == false)
540 infostream<<"Failed to read "<<path<<std::endl;
543 player->deSerialize(is);
553 void ServerEnvironment::saveMeta(const std::string &savedir)
555 std::string path = savedir + "/env_meta.txt";
557 // Open file and serialize
558 std::ofstream os(path.c_str(), std::ios_base::binary);
559 if(os.good() == false)
561 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
563 throw SerializationError("Couldn't save env meta");
567 args.setU64("game_time", m_game_time);
568 args.setU64("time_of_day", getTimeOfDay());
573 void ServerEnvironment::loadMeta(const std::string &savedir)
575 std::string path = savedir + "/env_meta.txt";
577 // Open file and deserialize
578 std::ifstream is(path.c_str(), std::ios_base::binary);
579 if(is.good() == false)
581 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
583 throw SerializationError("Couldn't load env meta");
591 throw SerializationError
592 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
594 std::getline(is, line);
595 std::string trimmedline = trim(line);
596 if(trimmedline == "EnvArgsEnd")
598 args.parseConfigLine(line);
602 m_game_time = args.getU64("game_time");
603 }catch(SettingNotFoundException &e){
604 // Getting this is crucial, otherwise timestamps are useless
605 throw SerializationError("Couldn't load env meta game_time");
609 m_time_of_day = args.getU64("time_of_day");
610 }catch(SettingNotFoundException &e){
611 // This is not as important
612 m_time_of_day = 9000;
618 ActiveBlockModifier *abm;
620 std::set<content_t> required_neighbors;
626 ServerEnvironment *m_env;
627 std::map<content_t, std::list<ActiveABM> > m_aabms;
629 ABMHandler(core::list<ABMWithState> &abms,
630 float dtime_s, ServerEnvironment *env,
636 INodeDefManager *ndef = env->getGameDef()->ndef();
637 for(core::list<ABMWithState>::Iterator
638 i = abms.begin(); i != abms.end(); i++){
639 ActiveBlockModifier *abm = i->abm;
640 float trigger_interval = abm->getTriggerInterval();
641 if(trigger_interval < 0.001)
642 trigger_interval = 0.001;
643 float actual_interval = dtime_s;
646 if(i->timer < trigger_interval)
648 i->timer -= trigger_interval;
649 actual_interval = trigger_interval;
651 float intervals = actual_interval / trigger_interval;
654 float chance = abm->getTriggerChance();
659 aabm.chance = chance / intervals;
663 std::set<std::string> required_neighbors_s
664 = abm->getRequiredNeighbors();
665 for(std::set<std::string>::iterator
666 i = required_neighbors_s.begin();
667 i != required_neighbors_s.end(); i++)
669 ndef->getIds(*i, aabm.required_neighbors);
672 std::set<std::string> contents_s = abm->getTriggerContents();
673 for(std::set<std::string>::iterator
674 i = contents_s.begin(); i != contents_s.end(); i++)
676 std::set<content_t> ids;
677 ndef->getIds(*i, ids);
678 for(std::set<content_t>::const_iterator k = ids.begin();
682 std::map<content_t, std::list<ActiveABM> >::iterator j;
684 if(j == m_aabms.end()){
685 std::list<ActiveABM> aabmlist;
686 m_aabms[c] = aabmlist;
689 j->second.push_back(aabm);
694 void apply(MapBlock *block)
699 ServerMap *map = &m_env->getServerMap();
702 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
703 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
704 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
706 MapNode n = block->getNodeNoEx(p0);
707 content_t c = n.getContent();
708 v3s16 p = p0 + block->getPosRelative();
710 std::map<content_t, std::list<ActiveABM> >::iterator j;
712 if(j == m_aabms.end())
715 for(std::list<ActiveABM>::iterator
716 i = j->second.begin(); i != j->second.end(); i++)
718 if(myrand() % i->chance != 0)
722 if(!i->required_neighbors.empty())
725 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
726 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
727 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
731 MapNode n = map->getNodeNoEx(p1);
732 content_t c = n.getContent();
733 std::set<content_t>::const_iterator k;
734 k = i->required_neighbors.find(c);
735 if(k != i->required_neighbors.end()){
739 // No required neighbor found
744 // Find out how many objects the block contains
745 u32 active_object_count = block->m_static_objects.m_active.size();
746 // Find out how many objects this and all the neighbors contain
747 u32 active_object_count_wider = 0;
748 u32 wider_unknown_count = 0;
749 for(s16 x=-1; x<=1; x++)
750 for(s16 y=-1; y<=1; y++)
751 for(s16 z=-1; z<=1; z++)
753 MapBlock *block2 = map->getBlockNoCreateNoEx(
754 block->getPos() + v3s16(x,y,z));
756 wider_unknown_count = 0;
759 active_object_count_wider +=
760 block2->m_static_objects.m_active.size()
761 + block2->m_static_objects.m_stored.size();
764 u32 wider_known_count = 3*3*3 - wider_unknown_count;
765 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
767 // Call all the trigger variations
768 i->abm->trigger(m_env, p, n);
769 i->abm->trigger(m_env, p, n,
770 active_object_count, active_object_count_wider);
776 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
778 // Get time difference
780 u32 stamp = block->getTimestamp();
781 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
782 dtime_s = m_game_time - block->getTimestamp();
783 dtime_s += additional_dtime;
785 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
786 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
788 // Set current time as timestamp
789 block->setTimestampNoChangedFlag(m_game_time);
791 /*infostream<<"ServerEnvironment::activateBlock(): block is "
792 <<dtime_s<<" seconds old."<<std::endl;*/
794 // Activate stored objects
795 activateObjects(block, dtime_s);
798 std::map<v3s16, NodeTimer> elapsed_timers =
799 block->m_node_timers.step((float)dtime_s);
800 if(!elapsed_timers.empty()){
802 for(std::map<v3s16, NodeTimer>::iterator
803 i = elapsed_timers.begin();
804 i != elapsed_timers.end(); i++){
805 n = block->getNodeNoEx(i->first);
806 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
807 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
811 /* Handle ActiveBlockModifiers */
812 ABMHandler abmhandler(m_abms, dtime_s, this, false);
813 abmhandler.apply(block);
816 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
818 m_abms.push_back(ABMWithState(abm));
821 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
823 std::set<u16> objects;
824 for(core::map<u16, ServerActiveObject*>::Iterator
825 i = m_active_objects.getIterator();
826 i.atEnd()==false; i++)
828 ServerActiveObject* obj = i.getNode()->getValue();
829 u16 id = i.getNode()->getKey();
830 v3f objectpos = obj->getBasePosition();
831 if(objectpos.getDistanceFrom(pos) > radius)
838 void ServerEnvironment::clearAllObjects()
840 infostream<<"ServerEnvironment::clearAllObjects(): "
841 <<"Removing all active objects"<<std::endl;
842 core::list<u16> objects_to_remove;
843 for(core::map<u16, ServerActiveObject*>::Iterator
844 i = m_active_objects.getIterator();
845 i.atEnd()==false; i++)
847 ServerActiveObject* obj = i.getNode()->getValue();
848 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
850 u16 id = i.getNode()->getKey();
851 v3f objectpos = obj->getBasePosition();
852 // Delete static object if block is loaded
853 if(obj->m_static_exists){
854 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
856 block->m_static_objects.remove(id);
857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
859 obj->m_static_exists = false;
862 // If known by some client, don't delete immediately
863 if(obj->m_known_by_count > 0){
864 obj->m_pending_deactivation = true;
865 obj->m_removed = true;
869 // Tell the object about removal
870 obj->removingFromEnvironment();
871 // Deregister in scripting api
872 scriptapi_rm_object_reference(m_lua, obj);
874 // Delete active object
875 if(obj->environmentDeletes())
877 // Id to be removed from m_active_objects
878 objects_to_remove.push_back(id);
880 // Remove references from m_active_objects
881 for(core::list<u16>::Iterator i = objects_to_remove.begin();
882 i != objects_to_remove.end(); i++)
884 m_active_objects.remove(*i);
887 core::list<v3s16> loadable_blocks;
888 infostream<<"ServerEnvironment::clearAllObjects(): "
889 <<"Listing all loadable blocks"<<std::endl;
890 m_map->listAllLoadableBlocks(loadable_blocks);
891 infostream<<"ServerEnvironment::clearAllObjects(): "
892 <<"Done listing all loadable blocks: "
893 <<loadable_blocks.size()
894 <<", now clearing"<<std::endl;
895 u32 report_interval = loadable_blocks.size() / 10;
896 u32 num_blocks_checked = 0;
897 u32 num_blocks_cleared = 0;
898 u32 num_objs_cleared = 0;
899 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
900 i != loadable_blocks.end(); i++)
903 MapBlock *block = m_map->emergeBlock(p, false);
905 errorstream<<"ServerEnvironment::clearAllObjects(): "
906 <<"Failed to emerge block "<<PP(p)<<std::endl;
909 u32 num_stored = block->m_static_objects.m_stored.size();
910 u32 num_active = block->m_static_objects.m_active.size();
911 if(num_stored != 0 || num_active != 0){
912 block->m_static_objects.m_stored.clear();
913 block->m_static_objects.m_active.clear();
914 block->raiseModified(MOD_STATE_WRITE_NEEDED,
916 num_objs_cleared += num_stored + num_active;
917 num_blocks_cleared++;
919 num_blocks_checked++;
921 if(num_blocks_checked % report_interval == 0){
922 float percent = 100.0 * (float)num_blocks_checked /
923 loadable_blocks.size();
924 infostream<<"ServerEnvironment::clearAllObjects(): "
925 <<"Cleared "<<num_objs_cleared<<" objects"
926 <<" in "<<num_blocks_cleared<<" blocks ("
927 <<percent<<"%)"<<std::endl;
930 infostream<<"ServerEnvironment::clearAllObjects(): "
931 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
932 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
935 void ServerEnvironment::step(float dtime)
937 DSTACK(__FUNCTION_NAME);
939 //TimeTaker timer("ServerEnv step");
941 /* Step time of day */
942 stepTimeOfDay(dtime);
945 // NOTE: This is kind of funny on a singleplayer game, but doesn't
946 // really matter that much.
947 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
953 m_game_time_fraction_counter += dtime;
954 u32 inc_i = (u32)m_game_time_fraction_counter;
955 m_game_time += inc_i;
956 m_game_time_fraction_counter -= (float)inc_i;
963 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
964 for(core::list<Player*>::Iterator i = m_players.begin();
965 i != m_players.end(); i++)
969 // Ignore disconnected players
970 if(player->peer_id == 0)
973 v3f playerpos = player->getPosition();
976 player->move(dtime, *m_map, 100*BS);
981 Manage active block list
983 if(m_active_blocks_management_interval.step(dtime, 2.0))
985 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
987 Get player block positions
989 core::list<v3s16> players_blockpos;
990 for(core::list<Player*>::Iterator
991 i = m_players.begin();
992 i != m_players.end(); i++)
995 // Ignore disconnected players
996 if(player->peer_id == 0)
998 v3s16 blockpos = getNodeBlockPos(
999 floatToInt(player->getPosition(), BS));
1000 players_blockpos.push_back(blockpos);
1004 Update list of active blocks, collecting changes
1006 const s16 active_block_range = g_settings->getS16("active_block_range");
1007 core::map<v3s16, bool> blocks_removed;
1008 core::map<v3s16, bool> blocks_added;
1009 m_active_blocks.update(players_blockpos, active_block_range,
1010 blocks_removed, blocks_added);
1013 Handle removed blocks
1016 // Convert active objects that are no more in active blocks to static
1017 deactivateFarObjects(false);
1019 for(core::map<v3s16, bool>::Iterator
1020 i = blocks_removed.getIterator();
1021 i.atEnd()==false; i++)
1023 v3s16 p = i.getNode()->getKey();
1025 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1026 <<") became inactive"<<std::endl;*/
1028 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1032 // Set current time as timestamp (and let it set ChangedFlag)
1033 block->setTimestamp(m_game_time);
1040 for(core::map<v3s16, bool>::Iterator
1041 i = blocks_added.getIterator();
1042 i.atEnd()==false; i++)
1044 v3s16 p = i.getNode()->getKey();
1046 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1047 <<") became active"<<std::endl;*/
1049 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1051 // Block needs to be fetched first
1052 m_emerger->queueBlockEmerge(p, false);
1053 m_active_blocks.m_list.remove(p);
1057 activateBlock(block);
1062 Mess around in active blocks
1064 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1066 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1070 for(core::map<v3s16, bool>::Iterator
1071 i = m_active_blocks.m_list.getIterator();
1072 i.atEnd()==false; i++)
1074 v3s16 p = i.getNode()->getKey();
1076 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1077 <<") being handled"<<std::endl;*/
1079 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1083 // Reset block usage timer
1084 block->resetUsageTimer();
1086 // Set current time as timestamp
1087 block->setTimestampNoChangedFlag(m_game_time);
1088 // If time has changed much from the one on disk,
1089 // set block to be saved when it is unloaded
1090 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1091 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1092 "Timestamp older than 60s (step)");
1095 std::map<v3s16, NodeTimer> elapsed_timers =
1096 block->m_node_timers.step((float)dtime);
1097 if(!elapsed_timers.empty()){
1099 for(std::map<v3s16, NodeTimer>::iterator
1100 i = elapsed_timers.begin();
1101 i != elapsed_timers.end(); i++){
1102 n = block->getNodeNoEx(i->first);
1103 p = i->first + block->getPosRelative();
1104 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1105 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1111 const float abm_interval = 1.0;
1112 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1114 if(m_active_block_interval_overload_skip > 0){
1115 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1116 m_active_block_interval_overload_skip--;
1119 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1120 TimeTaker timer("modify in active blocks");
1122 // Initialize handling of ActiveBlockModifiers
1123 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1125 for(core::map<v3s16, bool>::Iterator
1126 i = m_active_blocks.m_list.getIterator();
1127 i.atEnd()==false; i++)
1129 v3s16 p = i.getNode()->getKey();
1131 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1132 <<") being handled"<<std::endl;*/
1134 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1138 // Set current time as timestamp
1139 block->setTimestampNoChangedFlag(m_game_time);
1141 /* Handle ActiveBlockModifiers */
1142 abmhandler.apply(block);
1145 u32 time_ms = timer.stop(true);
1146 u32 max_time_ms = 200;
1147 if(time_ms > max_time_ms){
1148 infostream<<"WARNING: active block modifiers took "
1149 <<time_ms<<"ms (longer than "
1150 <<max_time_ms<<"ms)"<<std::endl;
1151 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1156 Step script environment (run global on_step())
1158 scriptapi_environment_step(m_lua, dtime);
1164 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1165 //TimeTaker timer("Step active objects");
1167 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1169 // This helps the objects to send data at the same time
1170 bool send_recommended = false;
1171 m_send_recommended_timer += dtime;
1172 if(m_send_recommended_timer > getSendRecommendedInterval())
1174 m_send_recommended_timer -= getSendRecommendedInterval();
1175 send_recommended = true;
1178 for(core::map<u16, ServerActiveObject*>::Iterator
1179 i = m_active_objects.getIterator();
1180 i.atEnd()==false; i++)
1182 ServerActiveObject* obj = i.getNode()->getValue();
1183 // Remove non-peaceful mobs on peaceful mode
1184 if(g_settings->getBool("only_peaceful_mobs")){
1185 if(!obj->isPeaceful())
1186 obj->m_removed = true;
1188 // Don't step if is to be removed or stored statically
1189 if(obj->m_removed || obj->m_pending_deactivation)
1192 obj->step(dtime, send_recommended);
1193 // Read messages from object
1194 while(obj->m_messages_out.size() > 0)
1196 m_active_object_messages.push_back(
1197 obj->m_messages_out.pop_front());
1203 Manage active objects
1205 if(m_object_management_interval.step(dtime, 0.5))
1207 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1209 Remove objects that satisfy (m_removed && m_known_by_count==0)
1211 removeRemovedObjects();
1215 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1217 core::map<u16, ServerActiveObject*>::Node *n;
1218 n = m_active_objects.find(id);
1221 return n->getValue();
1224 bool isFreeServerActiveObjectId(u16 id,
1225 core::map<u16, ServerActiveObject*> &objects)
1230 for(core::map<u16, ServerActiveObject*>::Iterator
1231 i = objects.getIterator();
1232 i.atEnd()==false; i++)
1234 if(i.getNode()->getKey() == id)
1240 u16 getFreeServerActiveObjectId(
1241 core::map<u16, ServerActiveObject*> &objects)
1246 if(isFreeServerActiveObjectId(new_id, objects))
1256 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1259 u16 id = addActiveObjectRaw(object, true, 0);
1263 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1267 v3f objectpos = obj->getBasePosition();
1269 // The block in which the object resides in
1270 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1273 Update the static data
1276 // Create new static object
1277 std::string staticdata = obj->getStaticData();
1278 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1279 // Add to the block where the object is located in
1280 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1281 // Get or generate the block
1282 MapBlock *block = m_map->emergeBlock(blockpos);
1284 bool succeeded = false;
1288 block->m_static_objects.insert(0, s_obj);
1289 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1290 "addActiveObjectAsStatic");
1294 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1295 <<"Could not find or generate "
1296 <<"a block for storing static object"<<std::endl;
1300 if(obj->environmentDeletes())
1307 Finds out what new objects have been added to
1308 inside a radius around a position
1310 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1311 core::map<u16, bool> ¤t_objects,
1312 core::map<u16, bool> &added_objects)
1314 v3f pos_f = intToFloat(pos, BS);
1315 f32 radius_f = radius * BS;
1317 Go through the object list,
1318 - discard m_removed objects,
1319 - discard objects that are too far away,
1320 - discard objects that are found in current_objects.
1321 - add remaining objects to added_objects
1323 for(core::map<u16, ServerActiveObject*>::Iterator
1324 i = m_active_objects.getIterator();
1325 i.atEnd()==false; i++)
1327 u16 id = i.getNode()->getKey();
1329 ServerActiveObject *object = i.getNode()->getValue();
1332 // Discard if removed
1333 if(object->m_removed)
1335 if(object->unlimitedTransferDistance() == false){
1336 // Discard if too far
1337 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1338 if(distance_f > radius_f)
1341 // Discard if already on current_objects
1342 core::map<u16, bool>::Node *n;
1343 n = current_objects.find(id);
1346 // Add to added_objects
1347 added_objects.insert(id, false);
1352 Finds out what objects have been removed from
1353 inside a radius around a position
1355 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1356 core::map<u16, bool> ¤t_objects,
1357 core::map<u16, bool> &removed_objects)
1359 v3f pos_f = intToFloat(pos, BS);
1360 f32 radius_f = radius * BS;
1362 Go through current_objects; object is removed if:
1363 - object is not found in m_active_objects (this is actually an
1364 error condition; objects should be set m_removed=true and removed
1365 only after all clients have been informed about removal), or
1366 - object has m_removed=true, or
1367 - object is too far away
1369 for(core::map<u16, bool>::Iterator
1370 i = current_objects.getIterator();
1371 i.atEnd()==false; i++)
1373 u16 id = i.getNode()->getKey();
1374 ServerActiveObject *object = getActiveObject(id);
1377 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1378 <<" object in current_objects is NULL"<<std::endl;
1379 removed_objects.insert(id, false);
1383 if(object->m_removed)
1385 removed_objects.insert(id, false);
1389 // If transfer distance is unlimited, don't remove
1390 if(object->unlimitedTransferDistance())
1393 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1395 if(distance_f >= radius_f)
1397 removed_objects.insert(id, false);
1405 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1407 if(m_active_object_messages.size() == 0)
1408 return ActiveObjectMessage(0);
1410 return m_active_object_messages.pop_front();
1414 ************ Private methods *************
1417 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1418 bool set_changed, u32 dtime_s)
1421 if(object->getId() == 0){
1422 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1425 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1426 <<"no free ids available"<<std::endl;
1427 if(object->environmentDeletes())
1431 object->setId(new_id);
1434 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1435 <<"supplied with id "<<object->getId()<<std::endl;
1437 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1439 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1440 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1441 if(object->environmentDeletes())
1445 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1446 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1448 m_active_objects.insert(object->getId(), object);
1450 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1451 <<"Added id="<<object->getId()<<"; there are now "
1452 <<m_active_objects.size()<<" active objects."
1455 // Register reference in scripting api (must be done before post-init)
1456 scriptapi_add_object_reference(m_lua, object);
1457 // Post-initialize object
1458 object->addedToEnvironment(dtime_s);
1460 // Add static data to block
1461 if(object->isStaticAllowed())
1463 // Add static object to active static list of the block
1464 v3f objectpos = object->getBasePosition();
1465 std::string staticdata = object->getStaticData();
1466 StaticObject s_obj(object->getType(), objectpos, staticdata);
1467 // Add to the block where the object is located in
1468 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1469 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1472 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1473 object->m_static_exists = true;
1474 object->m_static_block = blockpos;
1477 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1478 "addActiveObjectRaw");
1481 v3s16 p = floatToInt(objectpos, BS);
1482 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"could not find block for storing id="<<object->getId()
1484 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1488 return object->getId();
1492 Remove objects that satisfy (m_removed && m_known_by_count==0)
1494 void ServerEnvironment::removeRemovedObjects()
1496 core::list<u16> objects_to_remove;
1497 for(core::map<u16, ServerActiveObject*>::Iterator
1498 i = m_active_objects.getIterator();
1499 i.atEnd()==false; i++)
1501 u16 id = i.getNode()->getKey();
1502 ServerActiveObject* obj = i.getNode()->getValue();
1503 // This shouldn't happen but check it
1506 infostream<<"NULL object found in ServerEnvironment"
1507 <<" while finding removed objects. id="<<id<<std::endl;
1508 // Id to be removed from m_active_objects
1509 objects_to_remove.push_back(id);
1514 We will delete objects that are marked as removed or thatare
1515 waiting for deletion after deactivation
1517 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1521 Delete static data from block if is marked as removed
1523 if(obj->m_static_exists && obj->m_removed)
1525 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1528 block->m_static_objects.remove(id);
1529 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1530 "removeRemovedObjects");
1531 obj->m_static_exists = false;
1535 // If m_known_by_count > 0, don't actually remove.
1536 if(obj->m_known_by_count > 0)
1539 // Tell the object about removal
1540 obj->removingFromEnvironment();
1541 // Deregister in scripting api
1542 scriptapi_rm_object_reference(m_lua, obj);
1545 if(obj->environmentDeletes())
1547 // Id to be removed from m_active_objects
1548 objects_to_remove.push_back(id);
1550 // Remove references from m_active_objects
1551 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1552 i != objects_to_remove.end(); i++)
1554 m_active_objects.remove(*i);
1558 static void print_hexdump(std::ostream &o, const std::string &data)
1560 const int linelength = 16;
1561 for(int l=0; ; l++){
1562 int i0 = linelength * l;
1563 bool at_end = false;
1564 int thislinelength = linelength;
1565 if(i0 + thislinelength > (int)data.size()){
1566 thislinelength = data.size() - i0;
1569 for(int di=0; di<linelength; di++){
1572 if(di<thislinelength)
1573 snprintf(buf, 4, "%.2x ", data[i]);
1575 snprintf(buf, 4, " ");
1579 for(int di=0; di<thislinelength; di++){
1593 Convert stored objects from blocks near the players to active.
1595 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1599 // Ignore if no stored objects (to not set changed flag)
1600 if(block->m_static_objects.m_stored.size() == 0)
1602 verbosestream<<"ServerEnvironment::activateObjects(): "
1603 <<"activating objects of block "<<PP(block->getPos())
1604 <<" ("<<block->m_static_objects.m_stored.size()
1605 <<" objects)"<<std::endl;
1606 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1608 errorstream<<"suspiciously large amount of objects detected: "
1609 <<block->m_static_objects.m_stored.size()<<" in "
1610 <<PP(block->getPos())
1611 <<"; removing all of them."<<std::endl;
1612 // Clear stored list
1613 block->m_static_objects.m_stored.clear();
1614 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1615 "stored list cleared in activateObjects due to "
1616 "large amount of objects");
1619 // A list for objects that couldn't be converted to active for some
1620 // reason. They will be stored back.
1621 core::list<StaticObject> new_stored;
1622 // Loop through stored static objects
1623 for(core::list<StaticObject>::Iterator
1624 i = block->m_static_objects.m_stored.begin();
1625 i != block->m_static_objects.m_stored.end(); i++)
1627 /*infostream<<"Server: Creating an active object from "
1628 <<"static data"<<std::endl;*/
1629 StaticObject &s_obj = *i;
1630 // Create an active object from the data
1631 ServerActiveObject *obj = ServerActiveObject::create
1632 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1633 // If couldn't create object, store static data back.
1636 errorstream<<"ServerEnvironment::activateObjects(): "
1637 <<"failed to create active object from static object "
1638 <<"in block "<<PP(s_obj.pos/BS)
1639 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1640 print_hexdump(verbosestream, s_obj.data);
1642 new_stored.push_back(s_obj);
1645 verbosestream<<"ServerEnvironment::activateObjects(): "
1646 <<"activated static object pos="<<PP(s_obj.pos/BS)
1647 <<" type="<<(int)s_obj.type<<std::endl;
1648 // This will also add the object to the active static list
1649 addActiveObjectRaw(obj, false, dtime_s);
1651 // Clear stored list
1652 block->m_static_objects.m_stored.clear();
1653 // Add leftover failed stuff to stored list
1654 for(core::list<StaticObject>::Iterator
1655 i = new_stored.begin();
1656 i != new_stored.end(); i++)
1658 StaticObject &s_obj = *i;
1659 block->m_static_objects.m_stored.push_back(s_obj);
1662 Note: Block hasn't really been modified here.
1663 The objects have just been activated and moved from the stored
1664 static list to the active static list.
1665 As such, the block is essentially the same.
1666 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1667 Otherwise there would be a huge amount of unnecessary I/O.
1672 Convert objects that are not standing inside active blocks to static.
1674 If m_known_by_count != 0, active object is not deleted, but static
1675 data is still updated.
1677 If force_delete is set, active object is deleted nevertheless. It
1678 shall only be set so in the destructor of the environment.
1680 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1682 core::list<u16> objects_to_remove;
1683 for(core::map<u16, ServerActiveObject*>::Iterator
1684 i = m_active_objects.getIterator();
1685 i.atEnd()==false; i++)
1687 ServerActiveObject* obj = i.getNode()->getValue();
1690 // Do not deactivate if static data creation not allowed
1691 if(!force_delete && !obj->isStaticAllowed())
1694 // If pending deactivation, let removeRemovedObjects() do it
1695 if(!force_delete && obj->m_pending_deactivation)
1698 u16 id = i.getNode()->getKey();
1699 v3f objectpos = obj->getBasePosition();
1701 // The block in which the object resides in
1702 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1704 // If block is active, don't remove
1705 if(!force_delete && m_active_blocks.contains(blockpos_o))
1708 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1709 <<"deactivating object id="<<id<<" on inactive block "
1710 <<PP(blockpos_o)<<std::endl;
1712 // If known by some client, don't immediately delete.
1713 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1716 Update the static data
1719 if(obj->isStaticAllowed())
1721 // Create new static object
1722 std::string staticdata_new = obj->getStaticData();
1723 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1725 bool stays_in_same_block = false;
1726 bool data_changed = true;
1728 if(obj->m_static_exists){
1729 if(obj->m_static_block == blockpos_o)
1730 stays_in_same_block = true;
1732 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1734 core::map<u16, StaticObject>::Node *n =
1735 block->m_static_objects.m_active.find(id);
1737 StaticObject static_old = n->getValue();
1739 float save_movem = obj->getMinimumSavedMovement();
1741 if(static_old.data == staticdata_new &&
1742 (static_old.pos - objectpos).getLength() < save_movem)
1743 data_changed = false;
1745 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1746 <<"id="<<id<<" m_static_exists=true but "
1747 <<"static data doesn't actually exist in "
1748 <<PP(obj->m_static_block)<<std::endl;
1752 bool shall_be_written = (!stays_in_same_block || data_changed);
1754 // Delete old static object
1755 if(obj->m_static_exists)
1757 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1760 block->m_static_objects.remove(id);
1761 obj->m_static_exists = false;
1762 // Only mark block as modified if data changed considerably
1763 if(shall_be_written)
1764 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1765 "deactivateFarObjects: Static data "
1766 "changed considerably");
1770 // Add to the block where the object is located in
1771 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1772 // Get or generate the block
1773 MapBlock *block = NULL;
1775 block = m_map->emergeBlock(blockpos);
1776 } catch(InvalidPositionException &e){
1777 // Handled via NULL pointer
1782 if(block->m_static_objects.m_stored.size() >= 49){
1783 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1784 <<" statically but block "<<PP(blockpos)
1785 <<" already contains "
1786 <<block->m_static_objects.m_stored.size()
1787 <<" (over 49) objects."
1788 <<" Forcing delete."<<std::endl;
1789 force_delete = true;
1791 u16 new_id = pending_delete ? id : 0;
1792 // If static counterpart already exists, remove it first.
1793 // This shouldn't happen, but happens rarely for some
1794 // unknown reason. Unsuccessful attempts have been made to
1795 // find said reason.
1796 if(new_id && block->m_static_objects.m_active.find(new_id)){
1797 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1799 block->m_static_objects.remove(new_id);
1801 block->m_static_objects.insert(new_id, s_obj);
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");
1809 obj->m_static_exists = true;
1810 obj->m_static_block = block->getPos();
1815 v3s16 p = floatToInt(objectpos, BS);
1816 errorstream<<"ServerEnv: Could not find or generate "
1817 <<"a block for storing id="<<obj->getId()
1818 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1825 If known by some client, set pending deactivation.
1826 Otherwise delete it immediately.
1829 if(pending_delete && !force_delete)
1831 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1832 <<"object id="<<id<<" is known by clients"
1833 <<"; not deleting yet"<<std::endl;
1835 obj->m_pending_deactivation = true;
1839 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1840 <<"object id="<<id<<" is not known by clients"
1841 <<"; deleting"<<std::endl;
1843 // Tell the object about removal
1844 obj->removingFromEnvironment();
1845 // Deregister in scripting api
1846 scriptapi_rm_object_reference(m_lua, obj);
1848 // Delete active object
1849 if(obj->environmentDeletes())
1851 // Id to be removed from m_active_objects
1852 objects_to_remove.push_back(id);
1855 // Remove references from m_active_objects
1856 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1857 i != objects_to_remove.end(); i++)
1859 m_active_objects.remove(*i);
1866 #include "clientsimpleobject.h"
1872 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1873 ITextureSource *texturesource, IGameDef *gamedef,
1874 IrrlichtDevice *irr):
1877 m_texturesource(texturesource),
1883 ClientEnvironment::~ClientEnvironment()
1885 // delete active objects
1886 for(core::map<u16, ClientActiveObject*>::Iterator
1887 i = m_active_objects.getIterator();
1888 i.atEnd()==false; i++)
1890 delete i.getNode()->getValue();
1893 for(core::list<ClientSimpleObject*>::Iterator
1894 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1903 Map & ClientEnvironment::getMap()
1908 ClientMap & ClientEnvironment::getClientMap()
1913 void ClientEnvironment::addPlayer(Player *player)
1915 DSTACK(__FUNCTION_NAME);
1917 It is a failure if player is local and there already is a local
1920 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1922 Environment::addPlayer(player);
1925 LocalPlayer * ClientEnvironment::getLocalPlayer()
1927 for(core::list<Player*>::Iterator i = m_players.begin();
1928 i != m_players.end(); i++)
1930 Player *player = *i;
1931 if(player->isLocal())
1932 return (LocalPlayer*)player;
1937 void ClientEnvironment::step(float dtime)
1939 DSTACK(__FUNCTION_NAME);
1941 /* Step time of day */
1942 stepTimeOfDay(dtime);
1944 // Get some settings
1945 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1946 bool free_move = fly_allowed && g_settings->getBool("free_move");
1949 LocalPlayer *lplayer = getLocalPlayer();
1951 // collision info queue
1952 core::list<CollisionInfo> player_collisions;
1955 Get the speed the player is going
1957 bool is_climbing = lplayer->is_climbing;
1959 f32 player_speed = lplayer->getSpeed().getLength();
1962 Maximum position increment
1964 //f32 position_max_increment = 0.05*BS;
1965 f32 position_max_increment = 0.1*BS;
1967 // Maximum time increment (for collision detection etc)
1968 // time = distance / speed
1969 f32 dtime_max_increment = 1;
1970 if(player_speed > 0.001)
1971 dtime_max_increment = position_max_increment / player_speed;
1973 // Maximum time increment is 10ms or lower
1974 if(dtime_max_increment > 0.01)
1975 dtime_max_increment = 0.01;
1977 // Don't allow overly huge dtime
1981 f32 dtime_downcount = dtime;
1984 Stuff that has a maximum time increment
1993 if(dtime_downcount > dtime_max_increment)
1995 dtime_part = dtime_max_increment;
1996 dtime_downcount -= dtime_part;
2000 dtime_part = dtime_downcount;
2002 Setting this to 0 (no -=dtime_part) disables an infinite loop
2003 when dtime_part is so small that dtime_downcount -= dtime_part
2006 dtime_downcount = 0;
2014 v3f lplayerpos = lplayer->getPosition();
2017 if(free_move == false && is_climbing == false)
2020 v3f speed = lplayer->getSpeed();
2021 if(lplayer->swimming_up == false)
2022 speed.Y -= 9.81 * BS * dtime_part * 2;
2025 if(lplayer->in_water_stable || lplayer->in_water)
2027 f32 max_down = 2.0*BS;
2028 if(speed.Y < -max_down) speed.Y = -max_down;
2031 if(speed.getLength() > max)
2033 speed = speed / speed.getLength() * max;
2037 lplayer->setSpeed(speed);
2042 This also does collision detection.
2044 lplayer->move(dtime_part, *m_map, position_max_increment,
2045 &player_collisions);
2048 while(dtime_downcount > 0.001);
2050 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2052 for(core::list<CollisionInfo>::Iterator
2053 i = player_collisions.begin();
2054 i != player_collisions.end(); i++)
2056 CollisionInfo &info = *i;
2057 v3f speed_diff = info.new_speed - info.old_speed;;
2058 // Handle only fall damage
2059 // (because otherwise walking against something in fast_move kills you)
2060 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2062 // Get rid of other components
2065 f32 pre_factor = 1; // 1 hp per node/s
2066 f32 tolerance = BS*14; // 5 without damage
2067 f32 post_factor = 1; // 1 hp per node/s
2068 if(info.type == COLLISION_NODE)
2070 const ContentFeatures &f = m_gamedef->ndef()->
2071 get(m_map->getNodeNoEx(info.node_p));
2072 // Determine fall damage multiplier
2073 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2074 pre_factor = 1.0 + (float)addp/100.0;
2076 float speed = pre_factor * speed_diff.getLength();
2077 if(speed > tolerance)
2079 f32 damage_f = (speed - tolerance)/BS * post_factor;
2080 u16 damage = (u16)(damage_f+0.5);
2082 damageLocalPlayer(damage, true);
2087 A quick draft of lava damage
2089 if(m_lava_hurt_interval.step(dtime, 1.0))
2091 v3f pf = lplayer->getPosition();
2093 // Feet, middle and head
2094 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2095 MapNode n1 = m_map->getNodeNoEx(p1);
2096 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2097 MapNode n2 = m_map->getNodeNoEx(p2);
2098 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2099 MapNode n3 = m_map->getNodeNoEx(p2);
2101 u32 damage_per_second = 0;
2102 damage_per_second = MYMAX(damage_per_second,
2103 m_gamedef->ndef()->get(n1).damage_per_second);
2104 damage_per_second = MYMAX(damage_per_second,
2105 m_gamedef->ndef()->get(n2).damage_per_second);
2106 damage_per_second = MYMAX(damage_per_second,
2107 m_gamedef->ndef()->get(n3).damage_per_second);
2109 if(damage_per_second != 0)
2111 damageLocalPlayer(damage_per_second, true);
2116 Stuff that can be done in an arbitarily large dtime
2118 for(core::list<Player*>::Iterator i = m_players.begin();
2119 i != m_players.end(); i++)
2121 Player *player = *i;
2122 v3f playerpos = player->getPosition();
2125 Handle non-local players
2127 if(player->isLocal() == false)
2130 player->move(dtime, *m_map, 100*BS);
2134 // Update lighting on all players on client
2135 u8 light = LIGHT_MAX;
2138 v3s16 p = player->getLightPosition();
2139 MapNode n = m_map->getNode(p);
2140 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2142 catch(InvalidPositionException &e){
2143 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2145 player->light = light;
2149 Step active objects and update lighting of them
2152 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2153 for(core::map<u16, ClientActiveObject*>::Iterator
2154 i = m_active_objects.getIterator();
2155 i.atEnd()==false; i++)
2157 ClientActiveObject* obj = i.getNode()->getValue();
2159 obj->step(dtime, this);
2167 v3s16 p = obj->getLightPosition();
2168 MapNode n = m_map->getNode(p);
2169 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2171 catch(InvalidPositionException &e){
2172 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2174 obj->updateLight(light);
2179 Step and handle simple objects
2181 for(core::list<ClientSimpleObject*>::Iterator
2182 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2184 ClientSimpleObject *simple = *i;
2185 core::list<ClientSimpleObject*>::Iterator cur = i;
2187 simple->step(dtime);
2188 if(simple->m_to_be_removed){
2190 m_simple_objects.erase(cur);
2195 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2197 m_simple_objects.push_back(simple);
2200 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2202 core::map<u16, ClientActiveObject*>::Node *n;
2203 n = m_active_objects.find(id);
2206 return n->getValue();
2209 bool isFreeClientActiveObjectId(u16 id,
2210 core::map<u16, ClientActiveObject*> &objects)
2215 for(core::map<u16, ClientActiveObject*>::Iterator
2216 i = objects.getIterator();
2217 i.atEnd()==false; i++)
2219 if(i.getNode()->getKey() == id)
2225 u16 getFreeClientActiveObjectId(
2226 core::map<u16, ClientActiveObject*> &objects)
2231 if(isFreeClientActiveObjectId(new_id, objects))
2241 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2244 if(object->getId() == 0)
2246 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2249 infostream<<"ClientEnvironment::addActiveObject(): "
2250 <<"no free ids available"<<std::endl;
2254 object->setId(new_id);
2256 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2258 infostream<<"ClientEnvironment::addActiveObject(): "
2259 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2263 infostream<<"ClientEnvironment::addActiveObject(): "
2264 <<"added (id="<<object->getId()<<")"<<std::endl;
2265 m_active_objects.insert(object->getId(), object);
2266 object->addToScene(m_smgr, m_texturesource, m_irr);
2267 { // Update lighting immediately
2271 v3s16 p = object->getLightPosition();
2272 MapNode n = m_map->getNode(p);
2273 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2275 catch(InvalidPositionException &e){
2276 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2278 object->updateLight(light);
2280 return object->getId();
2283 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2284 const std::string &init_data)
2286 ClientActiveObject* obj =
2287 ClientActiveObject::create(type, m_gamedef, this);
2290 infostream<<"ClientEnvironment::addActiveObject(): "
2291 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2300 obj->initialize(init_data);
2302 catch(SerializationError &e)
2304 errorstream<<"ClientEnvironment::addActiveObject():"
2305 <<" id="<<id<<" type="<<type
2306 <<": SerializationError in initialize(): "
2308 <<": init_data="<<serializeJsonString(init_data)
2312 addActiveObject(obj);
2315 void ClientEnvironment::removeActiveObject(u16 id)
2317 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2318 <<"id="<<id<<std::endl;
2319 ClientActiveObject* obj = getActiveObject(id);
2322 infostream<<"ClientEnvironment::removeActiveObject(): "
2323 <<"id="<<id<<" not found"<<std::endl;
2326 obj->removeFromScene(true);
2328 m_active_objects.remove(id);
2331 void ClientEnvironment::processActiveObjectMessage(u16 id,
2332 const std::string &data)
2334 ClientActiveObject* obj = getActiveObject(id);
2337 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2338 <<" got message for id="<<id<<", which doesn't exist."
2344 obj->processMessage(data);
2346 catch(SerializationError &e)
2348 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2349 <<" id="<<id<<" type="<<obj->getType()
2350 <<" SerializationError in processMessage(),"
2351 <<" message="<<serializeJsonString(data)
2357 Callbacks for activeobjects
2360 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2362 LocalPlayer *lplayer = getLocalPlayer();
2366 if(lplayer->hp > damage)
2367 lplayer->hp -= damage;
2372 ClientEnvEvent event;
2373 event.type = CEE_PLAYER_DAMAGE;
2374 event.player_damage.amount = damage;
2375 event.player_damage.send_to_server = handle_hp;
2376 m_client_event_queue.push_back(event);
2380 Client likes to call these
2383 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2384 core::array<DistanceSortedActiveObject> &dest)
2386 for(core::map<u16, ClientActiveObject*>::Iterator
2387 i = m_active_objects.getIterator();
2388 i.atEnd()==false; i++)
2390 ClientActiveObject* obj = i.getNode()->getValue();
2392 f32 d = (obj->getPosition() - origin).getLength();
2397 DistanceSortedActiveObject dso(obj, d);
2399 dest.push_back(dso);
2403 ClientEnvEvent ClientEnvironment::getClientEvent()
2405 if(m_client_event_queue.size() == 0)
2407 ClientEnvEvent event;
2408 event.type = CEE_NONE;
2411 return m_client_event_queue.pop_front();
2414 #endif // #ifndef SERVER