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 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
56 Environment::~Environment()
59 for(core::list<Player*>::Iterator i = m_players.begin();
60 i != m_players.end(); i++)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(core::list<Player*>::Iterator i = m_players.begin();
88 i != m_players.end(); i++)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(core::list<Player*>::Iterator i = m_players.begin();
105 i != m_players.end(); i++)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(core::list<Player*>::Iterator i = m_players.begin();
117 i != m_players.end(); i++)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 core::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(core::list<Player*>::Iterator
132 i = connected_players.begin();
133 i != connected_players.end(); i++)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 core::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(core::list<Player*>::Iterator
151 i = connected_players.begin();
152 i != connected_players.end(); i++)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 core::list<Player*> Environment::getPlayers()
170 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 core::list<Player*> newlist;
173 for(core::list<Player*>::Iterator
174 i = m_players.begin();
175 i != m_players.end(); i++)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 void Environment::printPlayers(std::ostream &o)
193 o<<"Players in environment:"<<std::endl;
194 for(core::list<Player*>::Iterator i = m_players.begin();
195 i != m_players.end(); i++)
198 o<<"Player peer_id="<<player->peer_id<<std::endl;
202 u32 Environment::getDayNightRatio()
204 return time_to_daynight_ratio(m_time_of_day);
207 void Environment::stepTimeOfDay(float dtime)
209 m_time_counter += dtime;
210 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
211 u32 units = (u32)(m_time_counter*speed);
212 m_time_counter -= (f32)units / speed;
216 if(m_time_of_day + units >= 24000)
218 m_time_of_day = (m_time_of_day + units) % 24000;
220 m_time_of_day_f = (float)m_time_of_day / 24000.0;
223 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
224 if(m_time_of_day_f > 1.0)
225 m_time_of_day_f -= 1.0;
226 if(m_time_of_day_f < 0.0)
227 m_time_of_day_f += 1.0;
235 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
239 // Initialize timer to random value to spread processing
240 float itv = abm->getTriggerInterval();
241 itv = MYMAX(0.001, itv); // No less than 1ms
242 int minval = MYMAX(-0.51*itv, -60); // Clamp to
243 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
244 timer = myrand_range(minval, maxval);
251 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
254 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
255 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
256 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
263 void ActiveBlockList::update(core::list<v3s16> &active_positions,
265 core::map<v3s16, bool> &blocks_removed,
266 core::map<v3s16, bool> &blocks_added)
271 core::map<v3s16, bool> newlist;
272 for(core::list<v3s16>::Iterator i = active_positions.begin();
273 i != active_positions.end(); i++)
275 fillRadiusBlock(*i, radius, newlist);
279 Find out which blocks on the old list are not on the new list
281 // Go through old list
282 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
283 i.atEnd()==false; i++)
285 v3s16 p = i.getNode()->getKey();
286 // If not on new list, it's been removed
287 if(newlist.find(p) == NULL)
288 blocks_removed.insert(p, true);
292 Find out which blocks on the new list are not on the old list
294 // Go through new list
295 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
296 i.atEnd()==false; i++)
298 v3s16 p = i.getNode()->getKey();
299 // If not on old list, it's been added
300 if(m_list.find(p) == NULL)
301 blocks_added.insert(p, true);
308 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
309 i.atEnd()==false; i++)
311 v3s16 p = i.getNode()->getKey();
312 m_list.insert(p, true);
320 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
321 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
326 m_random_spawn_timer(3),
327 m_send_recommended_timer(0),
329 m_game_time_fraction_counter(0)
333 ServerEnvironment::~ServerEnvironment()
335 // Clear active block list.
336 // This makes the next one delete all active objects.
337 m_active_blocks.clear();
339 // Convert all objects to static and delete the active objects
340 deactivateFarObjects(true);
345 // Delete ActiveBlockModifiers
346 for(core::list<ABMWithState>::Iterator
347 i = m_abms.begin(); i != m_abms.end(); i++){
352 void ServerEnvironment::serializePlayers(const std::string &savedir)
354 std::string players_path = savedir + "/players";
355 fs::CreateDir(players_path);
357 core::map<Player*, bool> saved_players;
359 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
360 for(u32 i=0; i<player_files.size(); i++)
362 if(player_files[i].dir)
365 // Full path to this file
366 std::string path = players_path + "/" + player_files[i].name;
368 //infostream<<"Checking player file "<<path<<std::endl;
370 // Load player to see what is its name
371 RemotePlayer testplayer(m_gamedef);
373 // Open file and deserialize
374 std::ifstream is(path.c_str(), std::ios_base::binary);
375 if(is.good() == false)
377 infostream<<"Failed to read "<<path<<std::endl;
380 testplayer.deSerialize(is);
383 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
385 // Search for the player
386 std::string playername = testplayer.getName();
387 Player *player = getPlayer(playername.c_str());
390 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
394 //infostream<<"Found matching player, overwriting."<<std::endl;
396 // OK, found. Save player there.
398 // Open file and serialize
399 std::ofstream os(path.c_str(), std::ios_base::binary);
400 if(os.good() == false)
402 infostream<<"Failed to overwrite "<<path<<std::endl;
405 player->serialize(os);
406 saved_players.insert(player, true);
410 for(core::list<Player*>::Iterator i = m_players.begin();
411 i != m_players.end(); i++)
414 if(saved_players.find(player) != NULL)
416 /*infostream<<"Player "<<player->getName()
417 <<" was already saved."<<std::endl;*/
420 std::string playername = player->getName();
421 // Don't save unnamed player
424 //infostream<<"Not saving unnamed player."<<std::endl;
430 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
431 playername = "player";
432 std::string path = players_path + "/" + playername;
434 for(u32 i=0; i<1000; i++)
436 if(fs::PathExists(path) == false)
441 path = players_path + "/" + playername + itos(i);
445 infostream<<"Didn't find free file for player"<<std::endl;
450 /*infostream<<"Saving player "<<player->getName()<<" to "
452 // Open file and serialize
453 std::ofstream os(path.c_str(), std::ios_base::binary);
454 if(os.good() == false)
456 infostream<<"Failed to overwrite "<<path<<std::endl;
459 player->serialize(os);
460 saved_players.insert(player, true);
464 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
467 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
469 std::string players_path = savedir + "/players";
471 core::map<Player*, bool> saved_players;
473 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
474 for(u32 i=0; i<player_files.size(); i++)
476 if(player_files[i].dir)
479 // Full path to this file
480 std::string path = players_path + "/" + player_files[i].name;
482 //infostream<<"Checking player file "<<path<<std::endl;
484 // Load player to see what is its name
485 RemotePlayer testplayer(m_gamedef);
487 // Open file and deserialize
488 std::ifstream is(path.c_str(), std::ios_base::binary);
489 if(is.good() == false)
491 infostream<<"Failed to read "<<path<<std::endl;
494 testplayer.deSerialize(is);
497 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
499 infostream<<"Not loading player with invalid name: "
500 <<testplayer.getName()<<std::endl;
503 /*infostream<<"Loaded test player with name "<<testplayer.getName()
506 // Search for the player
507 std::string playername = testplayer.getName();
508 Player *player = getPlayer(playername.c_str());
509 bool newplayer = false;
512 //infostream<<"Is a new player"<<std::endl;
513 player = new RemotePlayer(m_gamedef);
519 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
521 // Open file and deserialize
522 std::ifstream is(path.c_str(), std::ios_base::binary);
523 if(is.good() == false)
525 infostream<<"Failed to read "<<path<<std::endl;
528 player->deSerialize(is);
538 void ServerEnvironment::saveMeta(const std::string &savedir)
540 std::string path = savedir + "/env_meta.txt";
542 // Open file and serialize
543 std::ofstream os(path.c_str(), std::ios_base::binary);
544 if(os.good() == false)
546 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
548 throw SerializationError("Couldn't save env meta");
552 args.setU64("game_time", m_game_time);
553 args.setU64("time_of_day", getTimeOfDay());
558 void ServerEnvironment::loadMeta(const std::string &savedir)
560 std::string path = savedir + "/env_meta.txt";
562 // Open file and deserialize
563 std::ifstream is(path.c_str(), std::ios_base::binary);
564 if(is.good() == false)
566 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
568 throw SerializationError("Couldn't load env meta");
576 throw SerializationError
577 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
579 std::getline(is, line);
580 std::string trimmedline = trim(line);
581 if(trimmedline == "EnvArgsEnd")
583 args.parseConfigLine(line);
587 m_game_time = args.getU64("game_time");
588 }catch(SettingNotFoundException &e){
589 // Getting this is crucial, otherwise timestamps are useless
590 throw SerializationError("Couldn't load env meta game_time");
594 m_time_of_day = args.getU64("time_of_day");
595 }catch(SettingNotFoundException &e){
596 // This is not as important
597 m_time_of_day = 9000;
603 ActiveBlockModifier *abm;
605 std::set<content_t> required_neighbors;
611 ServerEnvironment *m_env;
612 std::map<content_t, std::list<ActiveABM> > m_aabms;
614 ABMHandler(core::list<ABMWithState> &abms,
615 float dtime_s, ServerEnvironment *env,
621 INodeDefManager *ndef = env->getGameDef()->ndef();
622 for(core::list<ABMWithState>::Iterator
623 i = abms.begin(); i != abms.end(); i++){
624 ActiveBlockModifier *abm = i->abm;
625 float trigger_interval = abm->getTriggerInterval();
626 if(trigger_interval < 0.001)
627 trigger_interval = 0.001;
628 float actual_interval = dtime_s;
631 if(i->timer < trigger_interval)
633 i->timer -= trigger_interval;
634 actual_interval = trigger_interval;
636 float intervals = actual_interval / trigger_interval;
639 float chance = abm->getTriggerChance();
644 aabm.chance = chance / intervals;
648 std::set<std::string> required_neighbors_s
649 = abm->getRequiredNeighbors();
650 for(std::set<std::string>::iterator
651 i = required_neighbors_s.begin();
652 i != required_neighbors_s.end(); i++)
654 ndef->getIds(*i, aabm.required_neighbors);
657 std::set<std::string> contents_s = abm->getTriggerContents();
658 for(std::set<std::string>::iterator
659 i = contents_s.begin(); i != contents_s.end(); i++)
661 std::set<content_t> ids;
662 ndef->getIds(*i, ids);
663 for(std::set<content_t>::const_iterator k = ids.begin();
667 std::map<content_t, std::list<ActiveABM> >::iterator j;
669 if(j == m_aabms.end()){
670 std::list<ActiveABM> aabmlist;
671 m_aabms[c] = aabmlist;
674 j->second.push_back(aabm);
679 void apply(MapBlock *block)
684 ServerMap *map = &m_env->getServerMap();
687 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
688 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
689 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
691 MapNode n = block->getNodeNoEx(p0);
692 content_t c = n.getContent();
693 v3s16 p = p0 + block->getPosRelative();
695 std::map<content_t, std::list<ActiveABM> >::iterator j;
697 if(j == m_aabms.end())
700 for(std::list<ActiveABM>::iterator
701 i = j->second.begin(); i != j->second.end(); i++)
703 if(myrand() % i->chance != 0)
707 if(!i->required_neighbors.empty())
710 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
711 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
712 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
716 MapNode n = map->getNodeNoEx(p1);
717 content_t c = n.getContent();
718 std::set<content_t>::const_iterator k;
719 k = i->required_neighbors.find(c);
720 if(k != i->required_neighbors.end()){
724 // No required neighbor found
729 // Find out how many objects the block contains
730 u32 active_object_count = block->m_static_objects.m_active.size();
731 // Find out how many objects this and all the neighbors contain
732 u32 active_object_count_wider = 0;
733 for(s16 x=-1; x<=1; x++)
734 for(s16 y=-1; y<=1; y++)
735 for(s16 z=-1; z<=1; z++)
737 MapBlock *block2 = map->getBlockNoCreateNoEx(
738 block->getPos() + v3s16(x,y,z));
741 active_object_count_wider +=
742 block2->m_static_objects.m_active.size()
743 + block2->m_static_objects.m_stored.size();
746 // Call all the trigger variations
747 i->abm->trigger(m_env, p, n);
748 i->abm->trigger(m_env, p, n,
749 active_object_count, active_object_count_wider);
755 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
757 // Get time difference
759 u32 stamp = block->getTimestamp();
760 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
761 dtime_s = m_game_time - block->getTimestamp();
762 dtime_s += additional_dtime;
764 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
765 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
767 // Set current time as timestamp
768 block->setTimestampNoChangedFlag(m_game_time);
770 /*infostream<<"ServerEnvironment::activateBlock(): block is "
771 <<dtime_s<<" seconds old."<<std::endl;*/
773 // Activate stored objects
774 activateObjects(block);
777 std::map<v3s16, NodeTimer> elapsed_timers =
778 block->m_node_timers.step((float)dtime_s);
779 if(!elapsed_timers.empty()){
781 for(std::map<v3s16, NodeTimer>::iterator
782 i = elapsed_timers.begin();
783 i != elapsed_timers.end(); i++){
784 n = block->getNodeNoEx(i->first);
785 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
786 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
790 /* Handle ActiveBlockModifiers */
791 ABMHandler abmhandler(m_abms, dtime_s, this, false);
792 abmhandler.apply(block);
795 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
797 m_abms.push_back(ABMWithState(abm));
800 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
802 std::set<u16> objects;
803 for(core::map<u16, ServerActiveObject*>::Iterator
804 i = m_active_objects.getIterator();
805 i.atEnd()==false; i++)
807 ServerActiveObject* obj = i.getNode()->getValue();
808 u16 id = i.getNode()->getKey();
809 v3f objectpos = obj->getBasePosition();
810 if(objectpos.getDistanceFrom(pos) > radius)
817 void ServerEnvironment::clearAllObjects()
819 infostream<<"ServerEnvironment::clearAllObjects(): "
820 <<"Removing all active objects"<<std::endl;
821 core::list<u16> objects_to_remove;
822 for(core::map<u16, ServerActiveObject*>::Iterator
823 i = m_active_objects.getIterator();
824 i.atEnd()==false; i++)
826 ServerActiveObject* obj = i.getNode()->getValue();
827 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
829 u16 id = i.getNode()->getKey();
830 v3f objectpos = obj->getBasePosition();
831 // Delete static object if block is loaded
832 if(obj->m_static_exists){
833 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
835 block->m_static_objects.remove(id);
836 block->raiseModified(MOD_STATE_WRITE_NEEDED,
838 obj->m_static_exists = false;
841 // If known by some client, don't delete immediately
842 if(obj->m_known_by_count > 0){
843 obj->m_pending_deactivation = true;
844 obj->m_removed = true;
848 // Tell the object about removal
849 obj->removingFromEnvironment();
850 // Deregister in scripting api
851 scriptapi_rm_object_reference(m_lua, obj);
853 // Delete active object
854 if(obj->environmentDeletes())
856 // Id to be removed from m_active_objects
857 objects_to_remove.push_back(id);
859 // Remove references from m_active_objects
860 for(core::list<u16>::Iterator i = objects_to_remove.begin();
861 i != objects_to_remove.end(); i++)
863 m_active_objects.remove(*i);
866 core::list<v3s16> loadable_blocks;
867 infostream<<"ServerEnvironment::clearAllObjects(): "
868 <<"Listing all loadable blocks"<<std::endl;
869 m_map->listAllLoadableBlocks(loadable_blocks);
870 infostream<<"ServerEnvironment::clearAllObjects(): "
871 <<"Done listing all loadable blocks: "
872 <<loadable_blocks.size()
873 <<", now clearing"<<std::endl;
874 u32 report_interval = loadable_blocks.size() / 10;
875 u32 num_blocks_checked = 0;
876 u32 num_blocks_cleared = 0;
877 u32 num_objs_cleared = 0;
878 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
879 i != loadable_blocks.end(); i++)
882 MapBlock *block = m_map->emergeBlock(p, false);
884 errorstream<<"ServerEnvironment::clearAllObjects(): "
885 <<"Failed to emerge block "<<PP(p)<<std::endl;
888 u32 num_stored = block->m_static_objects.m_stored.size();
889 u32 num_active = block->m_static_objects.m_active.size();
890 if(num_stored != 0 || num_active != 0){
891 block->m_static_objects.m_stored.clear();
892 block->m_static_objects.m_active.clear();
893 block->raiseModified(MOD_STATE_WRITE_NEEDED,
895 num_objs_cleared += num_stored + num_active;
896 num_blocks_cleared++;
898 num_blocks_checked++;
900 if(num_blocks_checked % report_interval == 0){
901 float percent = 100.0 * (float)num_blocks_checked /
902 loadable_blocks.size();
903 infostream<<"ServerEnvironment::clearAllObjects(): "
904 <<"Cleared "<<num_objs_cleared<<" objects"
905 <<" in "<<num_blocks_cleared<<" blocks ("
906 <<percent<<"%)"<<std::endl;
909 infostream<<"ServerEnvironment::clearAllObjects(): "
910 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
911 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
914 void ServerEnvironment::step(float dtime)
916 DSTACK(__FUNCTION_NAME);
918 //TimeTaker timer("ServerEnv step");
920 /* Step time of day */
921 stepTimeOfDay(dtime);
927 m_game_time_fraction_counter += dtime;
928 u32 inc_i = (u32)m_game_time_fraction_counter;
929 m_game_time += inc_i;
930 m_game_time_fraction_counter -= (float)inc_i;
937 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
938 for(core::list<Player*>::Iterator i = m_players.begin();
939 i != m_players.end(); i++)
943 // Ignore disconnected players
944 if(player->peer_id == 0)
947 v3f playerpos = player->getPosition();
950 player->move(dtime, *m_map, 100*BS);
955 Manage active block list
957 if(m_active_blocks_management_interval.step(dtime, 2.0))
959 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
961 Get player block positions
963 core::list<v3s16> players_blockpos;
964 for(core::list<Player*>::Iterator
965 i = m_players.begin();
966 i != m_players.end(); i++)
969 // Ignore disconnected players
970 if(player->peer_id == 0)
972 v3s16 blockpos = getNodeBlockPos(
973 floatToInt(player->getPosition(), BS));
974 players_blockpos.push_back(blockpos);
978 Update list of active blocks, collecting changes
980 const s16 active_block_range = g_settings->getS16("active_block_range");
981 core::map<v3s16, bool> blocks_removed;
982 core::map<v3s16, bool> blocks_added;
983 m_active_blocks.update(players_blockpos, active_block_range,
984 blocks_removed, blocks_added);
987 Handle removed blocks
990 // Convert active objects that are no more in active blocks to static
991 deactivateFarObjects(false);
993 for(core::map<v3s16, bool>::Iterator
994 i = blocks_removed.getIterator();
995 i.atEnd()==false; i++)
997 v3s16 p = i.getNode()->getKey();
999 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1000 <<") became inactive"<<std::endl;*/
1002 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1006 // Set current time as timestamp (and let it set ChangedFlag)
1007 block->setTimestamp(m_game_time);
1014 for(core::map<v3s16, bool>::Iterator
1015 i = blocks_added.getIterator();
1016 i.atEnd()==false; i++)
1018 v3s16 p = i.getNode()->getKey();
1020 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1021 <<") became active"<<std::endl;*/
1023 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1025 // Block needs to be fetched first
1026 m_emerger->queueBlockEmerge(p, false);
1027 m_active_blocks.m_list.remove(p);
1031 activateBlock(block);
1036 Mess around in active blocks
1038 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1040 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1044 for(core::map<v3s16, bool>::Iterator
1045 i = m_active_blocks.m_list.getIterator();
1046 i.atEnd()==false; i++)
1048 v3s16 p = i.getNode()->getKey();
1050 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1051 <<") being handled"<<std::endl;*/
1053 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1057 // Reset block usage timer
1058 block->resetUsageTimer();
1060 // Set current time as timestamp
1061 block->setTimestampNoChangedFlag(m_game_time);
1062 // If time has changed much from the one on disk,
1063 // set block to be saved when it is unloaded
1064 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1065 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1066 "Timestamp older than 60s (step)");
1069 std::map<v3s16, NodeTimer> elapsed_timers =
1070 block->m_node_timers.step((float)dtime);
1071 if(!elapsed_timers.empty()){
1073 for(std::map<v3s16, NodeTimer>::iterator
1074 i = elapsed_timers.begin();
1075 i != elapsed_timers.end(); i++){
1076 n = block->getNodeNoEx(i->first);
1077 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
1078 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1084 const float abm_interval = 1.0;
1085 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1087 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1088 TimeTaker timer("modify in active blocks");
1090 // Initialize handling of ActiveBlockModifiers
1091 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1093 for(core::map<v3s16, bool>::Iterator
1094 i = m_active_blocks.m_list.getIterator();
1095 i.atEnd()==false; i++)
1097 v3s16 p = i.getNode()->getKey();
1099 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1100 <<") being handled"<<std::endl;*/
1102 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1106 // Set current time as timestamp
1107 block->setTimestampNoChangedFlag(m_game_time);
1109 /* Handle ActiveBlockModifiers */
1110 abmhandler.apply(block);
1113 u32 time_ms = timer.stop(true);
1114 u32 max_time_ms = 200;
1115 if(time_ms > max_time_ms){
1116 infostream<<"WARNING: active block modifiers took "
1117 <<time_ms<<"ms (longer than "
1118 <<max_time_ms<<"ms)"<<std::endl;
1123 Step script environment (run global on_step())
1125 scriptapi_environment_step(m_lua, dtime);
1131 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1132 //TimeTaker timer("Step active objects");
1134 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1136 // This helps the objects to send data at the same time
1137 bool send_recommended = false;
1138 m_send_recommended_timer += dtime;
1139 if(m_send_recommended_timer > getSendRecommendedInterval())
1141 m_send_recommended_timer -= getSendRecommendedInterval();
1142 send_recommended = true;
1145 for(core::map<u16, ServerActiveObject*>::Iterator
1146 i = m_active_objects.getIterator();
1147 i.atEnd()==false; i++)
1149 ServerActiveObject* obj = i.getNode()->getValue();
1150 // Remove non-peaceful mobs on peaceful mode
1151 if(g_settings->getBool("only_peaceful_mobs")){
1152 if(!obj->isPeaceful())
1153 obj->m_removed = true;
1155 // Don't step if is to be removed or stored statically
1156 if(obj->m_removed || obj->m_pending_deactivation)
1159 obj->step(dtime, send_recommended);
1160 // Read messages from object
1161 while(obj->m_messages_out.size() > 0)
1163 m_active_object_messages.push_back(
1164 obj->m_messages_out.pop_front());
1170 Manage active objects
1172 if(m_object_management_interval.step(dtime, 0.5))
1174 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1176 Remove objects that satisfy (m_removed && m_known_by_count==0)
1178 removeRemovedObjects();
1182 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1184 core::map<u16, ServerActiveObject*>::Node *n;
1185 n = m_active_objects.find(id);
1188 return n->getValue();
1191 bool isFreeServerActiveObjectId(u16 id,
1192 core::map<u16, ServerActiveObject*> &objects)
1197 for(core::map<u16, ServerActiveObject*>::Iterator
1198 i = objects.getIterator();
1199 i.atEnd()==false; i++)
1201 if(i.getNode()->getKey() == id)
1207 u16 getFreeServerActiveObjectId(
1208 core::map<u16, ServerActiveObject*> &objects)
1213 if(isFreeServerActiveObjectId(new_id, objects))
1223 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1226 u16 id = addActiveObjectRaw(object, true);
1230 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1234 v3f objectpos = obj->getBasePosition();
1236 // The block in which the object resides in
1237 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1240 Update the static data
1243 // Create new static object
1244 std::string staticdata = obj->getStaticData();
1245 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1246 // Add to the block where the object is located in
1247 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1248 // Get or generate the block
1249 MapBlock *block = m_map->emergeBlock(blockpos);
1251 bool succeeded = false;
1255 block->m_static_objects.insert(0, s_obj);
1256 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1257 "addActiveObjectAsStatic");
1261 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1262 <<"Could not find or generate "
1263 <<"a block for storing static object"<<std::endl;
1267 if(obj->environmentDeletes())
1274 Finds out what new objects have been added to
1275 inside a radius around a position
1277 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1278 core::map<u16, bool> ¤t_objects,
1279 core::map<u16, bool> &added_objects)
1281 v3f pos_f = intToFloat(pos, BS);
1282 f32 radius_f = radius * BS;
1284 Go through the object list,
1285 - discard m_removed objects,
1286 - discard objects that are too far away,
1287 - discard objects that are found in current_objects.
1288 - add remaining objects to added_objects
1290 for(core::map<u16, ServerActiveObject*>::Iterator
1291 i = m_active_objects.getIterator();
1292 i.atEnd()==false; i++)
1294 u16 id = i.getNode()->getKey();
1296 ServerActiveObject *object = i.getNode()->getValue();
1299 // Discard if removed
1300 if(object->m_removed)
1302 if(object->unlimitedTransferDistance() == false){
1303 // Discard if too far
1304 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1305 if(distance_f > radius_f)
1308 // Discard if already on current_objects
1309 core::map<u16, bool>::Node *n;
1310 n = current_objects.find(id);
1313 // Add to added_objects
1314 added_objects.insert(id, false);
1319 Finds out what objects have been removed from
1320 inside a radius around a position
1322 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1323 core::map<u16, bool> ¤t_objects,
1324 core::map<u16, bool> &removed_objects)
1326 v3f pos_f = intToFloat(pos, BS);
1327 f32 radius_f = radius * BS;
1329 Go through current_objects; object is removed if:
1330 - object is not found in m_active_objects (this is actually an
1331 error condition; objects should be set m_removed=true and removed
1332 only after all clients have been informed about removal), or
1333 - object has m_removed=true, or
1334 - object is too far away
1336 for(core::map<u16, bool>::Iterator
1337 i = current_objects.getIterator();
1338 i.atEnd()==false; i++)
1340 u16 id = i.getNode()->getKey();
1341 ServerActiveObject *object = getActiveObject(id);
1344 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1345 <<" object in current_objects is NULL"<<std::endl;
1346 removed_objects.insert(id, false);
1350 if(object->m_removed)
1352 removed_objects.insert(id, false);
1356 // If transfer distance is unlimited, don't remove
1357 if(object->unlimitedTransferDistance())
1360 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1362 if(distance_f >= radius_f)
1364 removed_objects.insert(id, false);
1372 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1374 if(m_active_object_messages.size() == 0)
1375 return ActiveObjectMessage(0);
1377 return m_active_object_messages.pop_front();
1381 ************ Private methods *************
1384 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1388 if(object->getId() == 0){
1389 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1392 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1393 <<"no free ids available"<<std::endl;
1394 if(object->environmentDeletes())
1398 object->setId(new_id);
1401 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1402 <<"supplied with id "<<object->getId()<<std::endl;
1404 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1406 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1407 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1408 if(object->environmentDeletes())
1412 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1413 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1415 m_active_objects.insert(object->getId(), object);
1417 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1418 <<"Added id="<<object->getId()<<"; there are now "
1419 <<m_active_objects.size()<<" active objects."
1422 // Register reference in scripting api (must be done before post-init)
1423 scriptapi_add_object_reference(m_lua, object);
1424 // Post-initialize object
1425 object->addedToEnvironment();
1427 // Add static data to block
1428 if(object->isStaticAllowed())
1430 // Add static object to active static list of the block
1431 v3f objectpos = object->getBasePosition();
1432 std::string staticdata = object->getStaticData();
1433 StaticObject s_obj(object->getType(), objectpos, staticdata);
1434 // Add to the block where the object is located in
1435 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1436 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1439 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1440 object->m_static_exists = true;
1441 object->m_static_block = blockpos;
1444 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1445 "addActiveObjectRaw");
1448 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1449 <<"could not find block for storing id="<<object->getId()
1450 <<" statically"<<std::endl;
1454 return object->getId();
1458 Remove objects that satisfy (m_removed && m_known_by_count==0)
1460 void ServerEnvironment::removeRemovedObjects()
1462 core::list<u16> objects_to_remove;
1463 for(core::map<u16, ServerActiveObject*>::Iterator
1464 i = m_active_objects.getIterator();
1465 i.atEnd()==false; i++)
1467 u16 id = i.getNode()->getKey();
1468 ServerActiveObject* obj = i.getNode()->getValue();
1469 // This shouldn't happen but check it
1472 infostream<<"NULL object found in ServerEnvironment"
1473 <<" while finding removed objects. id="<<id<<std::endl;
1474 // Id to be removed from m_active_objects
1475 objects_to_remove.push_back(id);
1480 We will delete objects that are marked as removed or thatare
1481 waiting for deletion after deactivation
1483 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1487 Delete static data from block if is marked as removed
1489 if(obj->m_static_exists && obj->m_removed)
1491 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1494 block->m_static_objects.remove(id);
1495 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1496 "removeRemovedObjects");
1497 obj->m_static_exists = false;
1501 // If m_known_by_count > 0, don't actually remove.
1502 if(obj->m_known_by_count > 0)
1505 // Tell the object about removal
1506 obj->removingFromEnvironment();
1507 // Deregister in scripting api
1508 scriptapi_rm_object_reference(m_lua, obj);
1511 if(obj->environmentDeletes())
1513 // Id to be removed from m_active_objects
1514 objects_to_remove.push_back(id);
1516 // Remove references from m_active_objects
1517 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1518 i != objects_to_remove.end(); i++)
1520 m_active_objects.remove(*i);
1524 static void print_hexdump(std::ostream &o, const std::string &data)
1526 const int linelength = 16;
1527 for(int l=0; ; l++){
1528 int i0 = linelength * l;
1529 bool at_end = false;
1530 int thislinelength = linelength;
1531 if(i0 + thislinelength > (int)data.size()){
1532 thislinelength = data.size() - i0;
1535 for(int di=0; di<linelength; di++){
1538 if(di<thislinelength)
1539 snprintf(buf, 4, "%.2x ", data[i]);
1541 snprintf(buf, 4, " ");
1545 for(int di=0; di<thislinelength; di++){
1559 Convert stored objects from blocks near the players to active.
1561 void ServerEnvironment::activateObjects(MapBlock *block)
1565 // Ignore if no stored objects (to not set changed flag)
1566 if(block->m_static_objects.m_stored.size() == 0)
1568 verbosestream<<"ServerEnvironment::activateObjects(): "
1569 <<"activating objects of block "<<PP(block->getPos())
1570 <<" ("<<block->m_static_objects.m_stored.size()
1571 <<" objects)"<<std::endl;
1572 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1574 errorstream<<"suspiciously large amount of objects detected: "
1575 <<block->m_static_objects.m_stored.size()<<" in "
1576 <<PP(block->getPos())
1577 <<"; removing all of them."<<std::endl;
1578 // Clear stored list
1579 block->m_static_objects.m_stored.clear();
1580 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1581 "stored list cleared in activateObjects due to "
1582 "large amount of objects");
1585 // A list for objects that couldn't be converted to static for some
1586 // reason. They will be stored back.
1587 core::list<StaticObject> new_stored;
1588 // Loop through stored static objects
1589 for(core::list<StaticObject>::Iterator
1590 i = block->m_static_objects.m_stored.begin();
1591 i != block->m_static_objects.m_stored.end(); i++)
1593 /*infostream<<"Server: Creating an active object from "
1594 <<"static data"<<std::endl;*/
1595 StaticObject &s_obj = *i;
1596 // Create an active object from the data
1597 ServerActiveObject *obj = ServerActiveObject::create
1598 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1599 // If couldn't create object, store static data back.
1602 errorstream<<"ServerEnvironment::activateObjects(): "
1603 <<"failed to create active object from static object "
1604 <<"in block "<<PP(s_obj.pos/BS)
1605 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1606 print_hexdump(verbosestream, s_obj.data);
1608 new_stored.push_back(s_obj);
1611 verbosestream<<"ServerEnvironment::activateObjects(): "
1612 <<"activated static object pos="<<PP(s_obj.pos/BS)
1613 <<" type="<<(int)s_obj.type<<std::endl;
1614 // This will also add the object to the active static list
1615 addActiveObjectRaw(obj, false);
1617 // Clear stored list
1618 block->m_static_objects.m_stored.clear();
1619 // Add leftover failed stuff to stored list
1620 for(core::list<StaticObject>::Iterator
1621 i = new_stored.begin();
1622 i != new_stored.end(); i++)
1624 StaticObject &s_obj = *i;
1625 block->m_static_objects.m_stored.push_back(s_obj);
1628 Note: Block hasn't really been modified here.
1629 The objects have just been activated and moved from the stored
1630 static list to the active static list.
1631 As such, the block is essentially the same.
1632 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1633 Otherwise there would be a huge amount of unnecessary I/O.
1638 Convert objects that are not standing inside active blocks to static.
1640 If m_known_by_count != 0, active object is not deleted, but static
1641 data is still updated.
1643 If force_delete is set, active object is deleted nevertheless. It
1644 shall only be set so in the destructor of the environment.
1646 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1648 core::list<u16> objects_to_remove;
1649 for(core::map<u16, ServerActiveObject*>::Iterator
1650 i = m_active_objects.getIterator();
1651 i.atEnd()==false; i++)
1653 ServerActiveObject* obj = i.getNode()->getValue();
1656 // Do not deactivate if static data creation not allowed
1657 if(!force_delete && !obj->isStaticAllowed())
1660 // If pending deactivation, let removeRemovedObjects() do it
1661 if(!force_delete && obj->m_pending_deactivation)
1664 u16 id = i.getNode()->getKey();
1665 v3f objectpos = obj->getBasePosition();
1667 // The block in which the object resides in
1668 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1670 // If block is active, don't remove
1671 if(!force_delete && m_active_blocks.contains(blockpos_o))
1674 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1675 <<"deactivating object id="<<id<<" on inactive block "
1676 <<PP(blockpos_o)<<std::endl;
1678 // If known by some client, don't immediately delete.
1679 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1682 Update the static data
1685 if(obj->isStaticAllowed())
1687 // Create new static object
1688 std::string staticdata_new = obj->getStaticData();
1689 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1691 bool stays_in_same_block = false;
1692 bool data_changed = true;
1694 if(obj->m_static_exists){
1695 if(obj->m_static_block == blockpos_o)
1696 stays_in_same_block = true;
1698 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1700 core::map<u16, StaticObject>::Node *n =
1701 block->m_static_objects.m_active.find(id);
1703 StaticObject static_old = n->getValue();
1705 float save_movem = obj->getMinimumSavedMovement();
1707 if(static_old.data == staticdata_new &&
1708 (static_old.pos - objectpos).getLength() < save_movem)
1709 data_changed = false;
1711 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1712 <<"id="<<id<<" m_static_exists=true but "
1713 <<"static data doesn't actually exist in "
1714 <<PP(obj->m_static_block)<<std::endl;
1718 bool shall_be_written = (!stays_in_same_block || data_changed);
1720 // Delete old static object
1721 if(obj->m_static_exists)
1723 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1726 block->m_static_objects.remove(id);
1727 obj->m_static_exists = false;
1728 // Only mark block as modified if data changed considerably
1729 if(shall_be_written)
1730 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1731 "deactivateFarObjects: Static data "
1732 "changed considerably");
1736 // Add to the block where the object is located in
1737 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1738 // Get or generate the block
1739 MapBlock *block = m_map->emergeBlock(blockpos);
1743 if(block->m_static_objects.m_stored.size() >= 49){
1744 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1745 <<" statically but block "<<PP(blockpos)
1746 <<" already contains "
1747 <<block->m_static_objects.m_stored.size()
1748 <<" (over 49) objects."
1749 <<" Forcing delete."<<std::endl;
1750 force_delete = true;
1752 u16 new_id = pending_delete ? id : 0;
1753 // If static counterpart already exists, remove it first.
1754 // This shouldn't happen, but happens rarely for some
1755 // unknown reason. Unsuccessful attempts have been made to
1756 // find said reason.
1757 if(new_id && block->m_static_objects.m_active.find(new_id)){
1758 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1760 block->m_static_objects.remove(new_id);
1762 block->m_static_objects.insert(new_id, s_obj);
1764 // Only mark block as modified if data changed considerably
1765 if(shall_be_written)
1766 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1767 "deactivateFarObjects: Static data "
1768 "changed considerably");
1770 obj->m_static_exists = true;
1771 obj->m_static_block = block->getPos();
1776 errorstream<<"ServerEnv: Could not find or generate "
1777 <<"a block for storing id="<<obj->getId()
1778 <<" statically"<<std::endl;
1785 If known by some client, set pending deactivation.
1786 Otherwise delete it immediately.
1789 if(pending_delete && !force_delete)
1791 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1792 <<"object id="<<id<<" is known by clients"
1793 <<"; not deleting yet"<<std::endl;
1795 obj->m_pending_deactivation = true;
1799 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1800 <<"object id="<<id<<" is not known by clients"
1801 <<"; deleting"<<std::endl;
1803 // Tell the object about removal
1804 obj->removingFromEnvironment();
1805 // Deregister in scripting api
1806 scriptapi_rm_object_reference(m_lua, obj);
1808 // Delete active object
1809 if(obj->environmentDeletes())
1811 // Id to be removed from m_active_objects
1812 objects_to_remove.push_back(id);
1815 // Remove references from m_active_objects
1816 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1817 i != objects_to_remove.end(); i++)
1819 m_active_objects.remove(*i);
1826 #include "clientsimpleobject.h"
1832 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1833 ITextureSource *texturesource, IGameDef *gamedef,
1834 IrrlichtDevice *irr):
1837 m_texturesource(texturesource),
1843 ClientEnvironment::~ClientEnvironment()
1845 // delete active objects
1846 for(core::map<u16, ClientActiveObject*>::Iterator
1847 i = m_active_objects.getIterator();
1848 i.atEnd()==false; i++)
1850 delete i.getNode()->getValue();
1853 for(core::list<ClientSimpleObject*>::Iterator
1854 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1863 Map & ClientEnvironment::getMap()
1868 ClientMap & ClientEnvironment::getClientMap()
1873 void ClientEnvironment::addPlayer(Player *player)
1875 DSTACK(__FUNCTION_NAME);
1877 It is a failure if player is local and there already is a local
1880 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1882 Environment::addPlayer(player);
1885 LocalPlayer * ClientEnvironment::getLocalPlayer()
1887 for(core::list<Player*>::Iterator i = m_players.begin();
1888 i != m_players.end(); i++)
1890 Player *player = *i;
1891 if(player->isLocal())
1892 return (LocalPlayer*)player;
1897 void ClientEnvironment::step(float dtime)
1899 DSTACK(__FUNCTION_NAME);
1901 /* Step time of day */
1902 stepTimeOfDay(dtime);
1904 // Get some settings
1905 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1906 bool free_move = fly_allowed && g_settings->getBool("free_move");
1909 LocalPlayer *lplayer = getLocalPlayer();
1911 // collision info queue
1912 core::list<CollisionInfo> player_collisions;
1915 Get the speed the player is going
1917 bool is_climbing = lplayer->is_climbing;
1919 f32 player_speed = lplayer->getSpeed().getLength();
1922 Maximum position increment
1924 //f32 position_max_increment = 0.05*BS;
1925 f32 position_max_increment = 0.1*BS;
1927 // Maximum time increment (for collision detection etc)
1928 // time = distance / speed
1929 f32 dtime_max_increment = 1;
1930 if(player_speed > 0.001)
1931 dtime_max_increment = position_max_increment / player_speed;
1933 // Maximum time increment is 10ms or lower
1934 if(dtime_max_increment > 0.01)
1935 dtime_max_increment = 0.01;
1937 // Don't allow overly huge dtime
1941 f32 dtime_downcount = dtime;
1944 Stuff that has a maximum time increment
1953 if(dtime_downcount > dtime_max_increment)
1955 dtime_part = dtime_max_increment;
1956 dtime_downcount -= dtime_part;
1960 dtime_part = dtime_downcount;
1962 Setting this to 0 (no -=dtime_part) disables an infinite loop
1963 when dtime_part is so small that dtime_downcount -= dtime_part
1966 dtime_downcount = 0;
1974 v3f lplayerpos = lplayer->getPosition();
1977 if(free_move == false && is_climbing == false)
1980 v3f speed = lplayer->getSpeed();
1981 if(lplayer->swimming_up == false)
1982 speed.Y -= 9.81 * BS * dtime_part * 2;
1985 if(lplayer->in_water_stable || lplayer->in_water)
1987 f32 max_down = 2.0*BS;
1988 if(speed.Y < -max_down) speed.Y = -max_down;
1991 if(speed.getLength() > max)
1993 speed = speed / speed.getLength() * max;
1997 lplayer->setSpeed(speed);
2002 This also does collision detection.
2004 lplayer->move(dtime_part, *m_map, position_max_increment,
2005 &player_collisions);
2008 while(dtime_downcount > 0.001);
2010 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2012 for(core::list<CollisionInfo>::Iterator
2013 i = player_collisions.begin();
2014 i != player_collisions.end(); i++)
2016 CollisionInfo &info = *i;
2017 if(info.t == COLLISION_FALL)
2019 //f32 tolerance = BS*10; // 2 without damage
2020 //f32 tolerance = BS*12; // 3 without damage
2021 f32 tolerance = BS*14; // 5 without damage
2023 if(info.speed > tolerance)
2025 f32 damage_f = (info.speed - tolerance)/BS*factor;
2026 u16 damage = (u16)(damage_f+0.5);
2028 damageLocalPlayer(damage, true);
2034 A quick draft of lava damage
2036 if(m_lava_hurt_interval.step(dtime, 1.0))
2038 v3f pf = lplayer->getPosition();
2040 // Feet, middle and head
2041 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2042 MapNode n1 = m_map->getNodeNoEx(p1);
2043 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2044 MapNode n2 = m_map->getNodeNoEx(p2);
2045 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2046 MapNode n3 = m_map->getNodeNoEx(p2);
2048 u32 damage_per_second = 0;
2049 damage_per_second = MYMAX(damage_per_second,
2050 m_gamedef->ndef()->get(n1).damage_per_second);
2051 damage_per_second = MYMAX(damage_per_second,
2052 m_gamedef->ndef()->get(n2).damage_per_second);
2053 damage_per_second = MYMAX(damage_per_second,
2054 m_gamedef->ndef()->get(n3).damage_per_second);
2056 if(damage_per_second != 0)
2058 damageLocalPlayer(damage_per_second, true);
2063 Stuff that can be done in an arbitarily large dtime
2065 for(core::list<Player*>::Iterator i = m_players.begin();
2066 i != m_players.end(); i++)
2068 Player *player = *i;
2069 v3f playerpos = player->getPosition();
2072 Handle non-local players
2074 if(player->isLocal() == false)
2077 player->move(dtime, *m_map, 100*BS);
2081 // Update lighting on all players on client
2082 u8 light = LIGHT_MAX;
2085 v3s16 p = player->getLightPosition();
2086 MapNode n = m_map->getNode(p);
2087 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2089 catch(InvalidPositionException &e){
2090 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2092 player->light = light;
2096 Step active objects and update lighting of them
2099 for(core::map<u16, ClientActiveObject*>::Iterator
2100 i = m_active_objects.getIterator();
2101 i.atEnd()==false; i++)
2103 ClientActiveObject* obj = i.getNode()->getValue();
2105 obj->step(dtime, this);
2107 if(m_active_object_light_update_interval.step(dtime, 0.21))
2113 v3s16 p = obj->getLightPosition();
2114 MapNode n = m_map->getNode(p);
2115 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2117 catch(InvalidPositionException &e){
2118 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2120 obj->updateLight(light);
2125 Step and handle simple objects
2127 for(core::list<ClientSimpleObject*>::Iterator
2128 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2130 ClientSimpleObject *simple = *i;
2131 core::list<ClientSimpleObject*>::Iterator cur = i;
2133 simple->step(dtime);
2134 if(simple->m_to_be_removed){
2136 m_simple_objects.erase(cur);
2141 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2143 m_simple_objects.push_back(simple);
2146 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2148 core::map<u16, ClientActiveObject*>::Node *n;
2149 n = m_active_objects.find(id);
2152 return n->getValue();
2155 bool isFreeClientActiveObjectId(u16 id,
2156 core::map<u16, ClientActiveObject*> &objects)
2161 for(core::map<u16, ClientActiveObject*>::Iterator
2162 i = objects.getIterator();
2163 i.atEnd()==false; i++)
2165 if(i.getNode()->getKey() == id)
2171 u16 getFreeClientActiveObjectId(
2172 core::map<u16, ClientActiveObject*> &objects)
2177 if(isFreeClientActiveObjectId(new_id, objects))
2187 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2190 if(object->getId() == 0)
2192 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2195 infostream<<"ClientEnvironment::addActiveObject(): "
2196 <<"no free ids available"<<std::endl;
2200 object->setId(new_id);
2202 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2204 infostream<<"ClientEnvironment::addActiveObject(): "
2205 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2209 infostream<<"ClientEnvironment::addActiveObject(): "
2210 <<"added (id="<<object->getId()<<")"<<std::endl;
2211 m_active_objects.insert(object->getId(), object);
2212 object->addToScene(m_smgr, m_texturesource, m_irr);
2213 { // Update lighting immediately
2217 v3s16 p = object->getLightPosition();
2218 MapNode n = m_map->getNode(p);
2219 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2221 catch(InvalidPositionException &e){
2222 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2224 object->updateLight(light);
2226 return object->getId();
2229 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2230 const std::string &init_data)
2232 ClientActiveObject* obj =
2233 ClientActiveObject::create(type, m_gamedef, this);
2236 infostream<<"ClientEnvironment::addActiveObject(): "
2237 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2246 obj->initialize(init_data);
2248 catch(SerializationError &e)
2250 errorstream<<"ClientEnvironment::addActiveObject():"
2251 <<" id="<<id<<" type="<<type
2252 <<": SerializationError in initialize(),"
2253 <<" init_data="<<serializeJsonString(init_data)
2257 addActiveObject(obj);
2260 void ClientEnvironment::removeActiveObject(u16 id)
2262 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2263 <<"id="<<id<<std::endl;
2264 ClientActiveObject* obj = getActiveObject(id);
2267 infostream<<"ClientEnvironment::removeActiveObject(): "
2268 <<"id="<<id<<" not found"<<std::endl;
2271 obj->removeFromScene();
2273 m_active_objects.remove(id);
2276 void ClientEnvironment::processActiveObjectMessage(u16 id,
2277 const std::string &data)
2279 ClientActiveObject* obj = getActiveObject(id);
2282 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2283 <<" got message for id="<<id<<", which doesn't exist."
2289 obj->processMessage(data);
2291 catch(SerializationError &e)
2293 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2294 <<" id="<<id<<" type="<<obj->getType()
2295 <<" SerializationError in processMessage(),"
2296 <<" message="<<serializeJsonString(data)
2302 Callbacks for activeobjects
2305 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2307 LocalPlayer *lplayer = getLocalPlayer();
2311 if(lplayer->hp > damage)
2312 lplayer->hp -= damage;
2317 ClientEnvEvent event;
2318 event.type = CEE_PLAYER_DAMAGE;
2319 event.player_damage.amount = damage;
2320 event.player_damage.send_to_server = handle_hp;
2321 m_client_event_queue.push_back(event);
2325 Client likes to call these
2328 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2329 core::array<DistanceSortedActiveObject> &dest)
2331 for(core::map<u16, ClientActiveObject*>::Iterator
2332 i = m_active_objects.getIterator();
2333 i.atEnd()==false; i++)
2335 ClientActiveObject* obj = i.getNode()->getValue();
2337 f32 d = (obj->getPosition() - origin).getLength();
2342 DistanceSortedActiveObject dso(obj, d);
2344 dest.push_back(dso);
2348 ClientEnvEvent ClientEnvironment::getClientEvent()
2350 if(m_client_event_queue.size() == 0)
2352 ClientEnvEvent event;
2353 event.type = CEE_NONE;
2356 return m_client_event_queue.pop_front();
2359 #endif // #ifndef SERVER