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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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 bool changed = block->m_node_metadata->step((float)dtime_s);
781 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
782 event.p = block->getPos();
783 m_map->dispatchEvent(&event);
785 block->raiseModified(MOD_STATE_WRITE_NEEDED,
786 "node metadata modified in activateBlock");
789 /* Handle ActiveBlockModifiers */
790 ABMHandler abmhandler(m_abms, dtime_s, this, false);
791 abmhandler.apply(block);
794 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
796 m_abms.push_back(ABMWithState(abm));
799 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
801 std::set<u16> objects;
802 for(core::map<u16, ServerActiveObject*>::Iterator
803 i = m_active_objects.getIterator();
804 i.atEnd()==false; i++)
806 ServerActiveObject* obj = i.getNode()->getValue();
807 u16 id = i.getNode()->getKey();
808 v3f objectpos = obj->getBasePosition();
809 if(objectpos.getDistanceFrom(pos) > radius)
816 void ServerEnvironment::clearAllObjects()
818 infostream<<"ServerEnvironment::clearAllObjects(): "
819 <<"Removing all active objects"<<std::endl;
820 core::list<u16> objects_to_remove;
821 for(core::map<u16, ServerActiveObject*>::Iterator
822 i = m_active_objects.getIterator();
823 i.atEnd()==false; i++)
825 ServerActiveObject* obj = i.getNode()->getValue();
826 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
828 u16 id = i.getNode()->getKey();
829 v3f objectpos = obj->getBasePosition();
830 // Delete static object if block is loaded
831 if(obj->m_static_exists){
832 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
834 block->m_static_objects.remove(id);
835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
837 obj->m_static_exists = false;
840 // If known by some client, don't delete immediately
841 if(obj->m_known_by_count > 0){
842 obj->m_pending_deactivation = true;
843 obj->m_removed = true;
847 // Tell the object about removal
848 obj->removingFromEnvironment();
849 // Deregister in scripting api
850 scriptapi_rm_object_reference(m_lua, obj);
852 // Delete active object
853 if(obj->environmentDeletes())
855 // Id to be removed from m_active_objects
856 objects_to_remove.push_back(id);
858 // Remove references from m_active_objects
859 for(core::list<u16>::Iterator i = objects_to_remove.begin();
860 i != objects_to_remove.end(); i++)
862 m_active_objects.remove(*i);
865 core::list<v3s16> loadable_blocks;
866 infostream<<"ServerEnvironment::clearAllObjects(): "
867 <<"Listing all loadable blocks"<<std::endl;
868 m_map->listAllLoadableBlocks(loadable_blocks);
869 infostream<<"ServerEnvironment::clearAllObjects(): "
870 <<"Done listing all loadable blocks: "
871 <<loadable_blocks.size()
872 <<", now clearing"<<std::endl;
873 u32 report_interval = loadable_blocks.size() / 10;
874 u32 num_blocks_checked = 0;
875 u32 num_blocks_cleared = 0;
876 u32 num_objs_cleared = 0;
877 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
878 i != loadable_blocks.end(); i++)
881 MapBlock *block = m_map->emergeBlock(p, false);
883 errorstream<<"ServerEnvironment::clearAllObjects(): "
884 <<"Failed to emerge block "<<PP(p)<<std::endl;
887 u32 num_stored = block->m_static_objects.m_stored.size();
888 u32 num_active = block->m_static_objects.m_active.size();
889 if(num_stored != 0 || num_active != 0){
890 block->m_static_objects.m_stored.clear();
891 block->m_static_objects.m_active.clear();
892 block->raiseModified(MOD_STATE_WRITE_NEEDED,
894 num_objs_cleared += num_stored + num_active;
895 num_blocks_cleared++;
897 num_blocks_checked++;
899 if(num_blocks_checked % report_interval == 0){
900 float percent = 100.0 * (float)num_blocks_checked /
901 loadable_blocks.size();
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Cleared "<<num_objs_cleared<<" objects"
904 <<" in "<<num_blocks_cleared<<" blocks ("
905 <<percent<<"%)"<<std::endl;
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
910 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
913 void ServerEnvironment::step(float dtime)
915 DSTACK(__FUNCTION_NAME);
917 //TimeTaker timer("ServerEnv step");
919 /* Step time of day */
920 stepTimeOfDay(dtime);
926 m_game_time_fraction_counter += dtime;
927 u32 inc_i = (u32)m_game_time_fraction_counter;
928 m_game_time += inc_i;
929 m_game_time_fraction_counter -= (float)inc_i;
936 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
937 for(core::list<Player*>::Iterator i = m_players.begin();
938 i != m_players.end(); i++)
942 // Ignore disconnected players
943 if(player->peer_id == 0)
946 v3f playerpos = player->getPosition();
949 player->move(dtime, *m_map, 100*BS);
954 Manage active block list
956 if(m_active_blocks_management_interval.step(dtime, 2.0))
958 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
960 Get player block positions
962 core::list<v3s16> players_blockpos;
963 for(core::list<Player*>::Iterator
964 i = m_players.begin();
965 i != m_players.end(); i++)
968 // Ignore disconnected players
969 if(player->peer_id == 0)
971 v3s16 blockpos = getNodeBlockPos(
972 floatToInt(player->getPosition(), BS));
973 players_blockpos.push_back(blockpos);
977 Update list of active blocks, collecting changes
979 const s16 active_block_range = g_settings->getS16("active_block_range");
980 core::map<v3s16, bool> blocks_removed;
981 core::map<v3s16, bool> blocks_added;
982 m_active_blocks.update(players_blockpos, active_block_range,
983 blocks_removed, blocks_added);
986 Handle removed blocks
989 // Convert active objects that are no more in active blocks to static
990 deactivateFarObjects(false);
992 for(core::map<v3s16, bool>::Iterator
993 i = blocks_removed.getIterator();
994 i.atEnd()==false; i++)
996 v3s16 p = i.getNode()->getKey();
998 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
999 <<") became inactive"<<std::endl;*/
1001 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1005 // Set current time as timestamp (and let it set ChangedFlag)
1006 block->setTimestamp(m_game_time);
1013 for(core::map<v3s16, bool>::Iterator
1014 i = blocks_added.getIterator();
1015 i.atEnd()==false; i++)
1017 v3s16 p = i.getNode()->getKey();
1019 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1020 <<") became active"<<std::endl;*/
1022 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1024 // Block needs to be fetched first
1025 m_emerger->queueBlockEmerge(p, false);
1026 m_active_blocks.m_list.remove(p);
1030 activateBlock(block);
1035 Mess around in active blocks
1037 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1039 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1043 for(core::map<v3s16, bool>::Iterator
1044 i = m_active_blocks.m_list.getIterator();
1045 i.atEnd()==false; i++)
1047 v3s16 p = i.getNode()->getKey();
1049 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1050 <<") being handled"<<std::endl;*/
1052 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1056 // Reset block usage timer
1057 block->resetUsageTimer();
1059 // Set current time as timestamp
1060 block->setTimestampNoChangedFlag(m_game_time);
1061 // If time has changed much from the one on disk,
1062 // set block to be saved when it is unloaded
1063 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1064 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1065 "Timestamp older than 60s (step)");
1067 // Run node metadata
1068 bool changed = block->m_node_metadata->step(dtime);
1072 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1074 m_map->dispatchEvent(&event);
1076 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1077 "node metadata modified in step");
1082 const float abm_interval = 1.0;
1083 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1085 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1086 TimeTaker timer("modify in active blocks");
1088 // Initialize handling of ActiveBlockModifiers
1089 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1091 for(core::map<v3s16, bool>::Iterator
1092 i = m_active_blocks.m_list.getIterator();
1093 i.atEnd()==false; i++)
1095 v3s16 p = i.getNode()->getKey();
1097 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1098 <<") being handled"<<std::endl;*/
1100 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1104 // Set current time as timestamp
1105 block->setTimestampNoChangedFlag(m_game_time);
1107 /* Handle ActiveBlockModifiers */
1108 abmhandler.apply(block);
1111 u32 time_ms = timer.stop(true);
1112 u32 max_time_ms = 200;
1113 if(time_ms > max_time_ms){
1114 infostream<<"WARNING: active block modifiers took "
1115 <<time_ms<<"ms (longer than "
1116 <<max_time_ms<<"ms)"<<std::endl;
1121 Step script environment (run global on_step())
1123 scriptapi_environment_step(m_lua, dtime);
1129 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1130 //TimeTaker timer("Step active objects");
1132 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1134 // This helps the objects to send data at the same time
1135 bool send_recommended = false;
1136 m_send_recommended_timer += dtime;
1137 if(m_send_recommended_timer > getSendRecommendedInterval())
1139 m_send_recommended_timer -= getSendRecommendedInterval();
1140 send_recommended = true;
1143 for(core::map<u16, ServerActiveObject*>::Iterator
1144 i = m_active_objects.getIterator();
1145 i.atEnd()==false; i++)
1147 ServerActiveObject* obj = i.getNode()->getValue();
1148 // Remove non-peaceful mobs on peaceful mode
1149 if(g_settings->getBool("only_peaceful_mobs")){
1150 if(!obj->isPeaceful())
1151 obj->m_removed = true;
1153 // Don't step if is to be removed or stored statically
1154 if(obj->m_removed || obj->m_pending_deactivation)
1157 obj->step(dtime, send_recommended);
1158 // Read messages from object
1159 while(obj->m_messages_out.size() > 0)
1161 m_active_object_messages.push_back(
1162 obj->m_messages_out.pop_front());
1168 Manage active objects
1170 if(m_object_management_interval.step(dtime, 0.5))
1172 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1174 Remove objects that satisfy (m_removed && m_known_by_count==0)
1176 removeRemovedObjects();
1180 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1182 core::map<u16, ServerActiveObject*>::Node *n;
1183 n = m_active_objects.find(id);
1186 return n->getValue();
1189 bool isFreeServerActiveObjectId(u16 id,
1190 core::map<u16, ServerActiveObject*> &objects)
1195 for(core::map<u16, ServerActiveObject*>::Iterator
1196 i = objects.getIterator();
1197 i.atEnd()==false; i++)
1199 if(i.getNode()->getKey() == id)
1205 u16 getFreeServerActiveObjectId(
1206 core::map<u16, ServerActiveObject*> &objects)
1211 if(isFreeServerActiveObjectId(new_id, objects))
1221 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1224 u16 id = addActiveObjectRaw(object, true);
1228 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1232 v3f objectpos = obj->getBasePosition();
1234 // The block in which the object resides in
1235 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1238 Update the static data
1241 // Create new static object
1242 std::string staticdata = obj->getStaticData();
1243 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1244 // Add to the block where the object is located in
1245 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1246 // Get or generate the block
1247 MapBlock *block = m_map->emergeBlock(blockpos);
1249 bool succeeded = false;
1253 block->m_static_objects.insert(0, s_obj);
1254 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1255 "addActiveObjectAsStatic");
1259 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1260 <<"Could not find or generate "
1261 <<"a block for storing static object"<<std::endl;
1265 if(obj->environmentDeletes())
1272 Finds out what new objects have been added to
1273 inside a radius around a position
1275 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1276 core::map<u16, bool> ¤t_objects,
1277 core::map<u16, bool> &added_objects)
1279 v3f pos_f = intToFloat(pos, BS);
1280 f32 radius_f = radius * BS;
1282 Go through the object list,
1283 - discard m_removed objects,
1284 - discard objects that are too far away,
1285 - discard objects that are found in current_objects.
1286 - add remaining objects to added_objects
1288 for(core::map<u16, ServerActiveObject*>::Iterator
1289 i = m_active_objects.getIterator();
1290 i.atEnd()==false; i++)
1292 u16 id = i.getNode()->getKey();
1294 ServerActiveObject *object = i.getNode()->getValue();
1297 // Discard if removed
1298 if(object->m_removed)
1300 if(object->unlimitedTransferDistance() == false){
1301 // Discard if too far
1302 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1303 if(distance_f > radius_f)
1306 // Discard if already on current_objects
1307 core::map<u16, bool>::Node *n;
1308 n = current_objects.find(id);
1311 // Add to added_objects
1312 added_objects.insert(id, false);
1317 Finds out what objects have been removed from
1318 inside a radius around a position
1320 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1321 core::map<u16, bool> ¤t_objects,
1322 core::map<u16, bool> &removed_objects)
1324 v3f pos_f = intToFloat(pos, BS);
1325 f32 radius_f = radius * BS;
1327 Go through current_objects; object is removed if:
1328 - object is not found in m_active_objects (this is actually an
1329 error condition; objects should be set m_removed=true and removed
1330 only after all clients have been informed about removal), or
1331 - object has m_removed=true, or
1332 - object is too far away
1334 for(core::map<u16, bool>::Iterator
1335 i = current_objects.getIterator();
1336 i.atEnd()==false; i++)
1338 u16 id = i.getNode()->getKey();
1339 ServerActiveObject *object = getActiveObject(id);
1342 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1343 <<" object in current_objects is NULL"<<std::endl;
1344 removed_objects.insert(id, false);
1348 if(object->m_removed)
1350 removed_objects.insert(id, false);
1354 // If transfer distance is unlimited, don't remove
1355 if(object->unlimitedTransferDistance())
1358 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1360 if(distance_f >= radius_f)
1362 removed_objects.insert(id, false);
1370 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1372 if(m_active_object_messages.size() == 0)
1373 return ActiveObjectMessage(0);
1375 return m_active_object_messages.pop_front();
1379 ************ Private methods *************
1382 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1386 if(object->getId() == 0){
1387 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1390 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1391 <<"no free ids available"<<std::endl;
1392 if(object->environmentDeletes())
1396 object->setId(new_id);
1399 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1400 <<"supplied with id "<<object->getId()<<std::endl;
1402 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1404 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1405 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1406 if(object->environmentDeletes())
1410 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1411 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1413 m_active_objects.insert(object->getId(), object);
1415 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1416 <<"Added id="<<object->getId()<<"; there are now "
1417 <<m_active_objects.size()<<" active objects."
1420 // Register reference in scripting api (must be done before post-init)
1421 scriptapi_add_object_reference(m_lua, object);
1422 // Post-initialize object
1423 object->addedToEnvironment();
1425 // Add static data to block
1426 if(object->isStaticAllowed())
1428 // Add static object to active static list of the block
1429 v3f objectpos = object->getBasePosition();
1430 std::string staticdata = object->getStaticData();
1431 StaticObject s_obj(object->getType(), objectpos, staticdata);
1432 // Add to the block where the object is located in
1433 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1434 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1437 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1438 object->m_static_exists = true;
1439 object->m_static_block = blockpos;
1442 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1443 "addActiveObjectRaw");
1446 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1447 <<"could not find block for storing id="<<object->getId()
1448 <<" statically"<<std::endl;
1452 return object->getId();
1456 Remove objects that satisfy (m_removed && m_known_by_count==0)
1458 void ServerEnvironment::removeRemovedObjects()
1460 core::list<u16> objects_to_remove;
1461 for(core::map<u16, ServerActiveObject*>::Iterator
1462 i = m_active_objects.getIterator();
1463 i.atEnd()==false; i++)
1465 u16 id = i.getNode()->getKey();
1466 ServerActiveObject* obj = i.getNode()->getValue();
1467 // This shouldn't happen but check it
1470 infostream<<"NULL object found in ServerEnvironment"
1471 <<" while finding removed objects. id="<<id<<std::endl;
1472 // Id to be removed from m_active_objects
1473 objects_to_remove.push_back(id);
1478 We will delete objects that are marked as removed or thatare
1479 waiting for deletion after deactivation
1481 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1485 Delete static data from block if is marked as removed
1487 if(obj->m_static_exists && obj->m_removed)
1489 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1492 block->m_static_objects.remove(id);
1493 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1494 "removeRemovedObjects");
1495 obj->m_static_exists = false;
1499 // If m_known_by_count > 0, don't actually remove.
1500 if(obj->m_known_by_count > 0)
1503 // Tell the object about removal
1504 obj->removingFromEnvironment();
1505 // Deregister in scripting api
1506 scriptapi_rm_object_reference(m_lua, obj);
1509 if(obj->environmentDeletes())
1511 // Id to be removed from m_active_objects
1512 objects_to_remove.push_back(id);
1514 // Remove references from m_active_objects
1515 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1516 i != objects_to_remove.end(); i++)
1518 m_active_objects.remove(*i);
1522 static void print_hexdump(std::ostream &o, const std::string &data)
1524 const int linelength = 16;
1525 for(int l=0; ; l++){
1526 int i0 = linelength * l;
1527 bool at_end = false;
1528 int thislinelength = linelength;
1529 if(i0 + thislinelength > (int)data.size()){
1530 thislinelength = data.size() - i0;
1533 for(int di=0; di<linelength; di++){
1536 if(di<thislinelength)
1537 snprintf(buf, 4, "%.2x ", data[i]);
1539 snprintf(buf, 4, " ");
1543 for(int di=0; di<thislinelength; di++){
1557 Convert stored objects from blocks near the players to active.
1559 void ServerEnvironment::activateObjects(MapBlock *block)
1563 // Ignore if no stored objects (to not set changed flag)
1564 if(block->m_static_objects.m_stored.size() == 0)
1566 verbosestream<<"ServerEnvironment::activateObjects(): "
1567 <<"activating objects of block "<<PP(block->getPos())
1568 <<" ("<<block->m_static_objects.m_stored.size()
1569 <<" objects)"<<std::endl;
1570 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1572 errorstream<<"suspiciously large amount of objects detected: "
1573 <<block->m_static_objects.m_stored.size()<<" in "
1574 <<PP(block->getPos())
1575 <<"; removing all of them."<<std::endl;
1576 // Clear stored list
1577 block->m_static_objects.m_stored.clear();
1578 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1579 "stored list cleared in activateObjects due to "
1580 "large amount of objects");
1583 // A list for objects that couldn't be converted to static for some
1584 // reason. They will be stored back.
1585 core::list<StaticObject> new_stored;
1586 // Loop through stored static objects
1587 for(core::list<StaticObject>::Iterator
1588 i = block->m_static_objects.m_stored.begin();
1589 i != block->m_static_objects.m_stored.end(); i++)
1591 /*infostream<<"Server: Creating an active object from "
1592 <<"static data"<<std::endl;*/
1593 StaticObject &s_obj = *i;
1594 // Create an active object from the data
1595 ServerActiveObject *obj = ServerActiveObject::create
1596 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1597 // If couldn't create object, store static data back.
1600 errorstream<<"ServerEnvironment::activateObjects(): "
1601 <<"failed to create active object from static object "
1602 <<"in block "<<PP(s_obj.pos/BS)
1603 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1604 print_hexdump(verbosestream, s_obj.data);
1606 new_stored.push_back(s_obj);
1609 verbosestream<<"ServerEnvironment::activateObjects(): "
1610 <<"activated static object pos="<<PP(s_obj.pos/BS)
1611 <<" type="<<(int)s_obj.type<<std::endl;
1612 // This will also add the object to the active static list
1613 addActiveObjectRaw(obj, false);
1615 // Clear stored list
1616 block->m_static_objects.m_stored.clear();
1617 // Add leftover failed stuff to stored list
1618 for(core::list<StaticObject>::Iterator
1619 i = new_stored.begin();
1620 i != new_stored.end(); i++)
1622 StaticObject &s_obj = *i;
1623 block->m_static_objects.m_stored.push_back(s_obj);
1626 Note: Block hasn't really been modified here.
1627 The objects have just been activated and moved from the stored
1628 static list to the active static list.
1629 As such, the block is essentially the same.
1630 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1631 Otherwise there would be a huge amount of unnecessary I/O.
1636 Convert objects that are not standing inside active blocks to static.
1638 If m_known_by_count != 0, active object is not deleted, but static
1639 data is still updated.
1641 If force_delete is set, active object is deleted nevertheless. It
1642 shall only be set so in the destructor of the environment.
1644 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1646 core::list<u16> objects_to_remove;
1647 for(core::map<u16, ServerActiveObject*>::Iterator
1648 i = m_active_objects.getIterator();
1649 i.atEnd()==false; i++)
1651 ServerActiveObject* obj = i.getNode()->getValue();
1654 // Do not deactivate if static data creation not allowed
1655 if(!force_delete && !obj->isStaticAllowed())
1658 // If pending deactivation, let removeRemovedObjects() do it
1659 if(!force_delete && obj->m_pending_deactivation)
1662 u16 id = i.getNode()->getKey();
1663 v3f objectpos = obj->getBasePosition();
1665 // The block in which the object resides in
1666 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1668 // If block is active, don't remove
1669 if(!force_delete && m_active_blocks.contains(blockpos_o))
1672 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1673 <<"deactivating object id="<<id<<" on inactive block "
1674 <<PP(blockpos_o)<<std::endl;
1676 // If known by some client, don't immediately delete.
1677 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1680 Update the static data
1683 if(obj->isStaticAllowed())
1685 // Create new static object
1686 std::string staticdata_new = obj->getStaticData();
1687 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1689 bool stays_in_same_block = false;
1690 bool data_changed = true;
1692 if(obj->m_static_exists){
1693 if(obj->m_static_block == blockpos_o)
1694 stays_in_same_block = true;
1696 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1698 core::map<u16, StaticObject>::Node *n =
1699 block->m_static_objects.m_active.find(id);
1701 StaticObject static_old = n->getValue();
1703 float save_movem = obj->getMinimumSavedMovement();
1705 if(static_old.data == staticdata_new &&
1706 (static_old.pos - objectpos).getLength() < save_movem)
1707 data_changed = false;
1709 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1710 <<"id="<<id<<" m_static_exists=true but "
1711 <<"static data doesn't actually exist in "
1712 <<PP(obj->m_static_block)<<std::endl;
1716 bool shall_be_written = (!stays_in_same_block || data_changed);
1718 // Delete old static object
1719 if(obj->m_static_exists)
1721 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1724 block->m_static_objects.remove(id);
1725 obj->m_static_exists = false;
1726 // Only mark block as modified if data changed considerably
1727 if(shall_be_written)
1728 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1729 "deactivateFarObjects: Static data "
1730 "changed considerably");
1734 // Add to the block where the object is located in
1735 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1736 // Get or generate the block
1737 MapBlock *block = m_map->emergeBlock(blockpos);
1741 if(block->m_static_objects.m_stored.size() >= 49){
1742 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1743 <<" statically but block "<<PP(blockpos)
1744 <<" already contains "
1745 <<block->m_static_objects.m_stored.size()
1746 <<" (over 49) objects."
1747 <<" Forcing delete."<<std::endl;
1748 force_delete = true;
1750 u16 new_id = pending_delete ? id : 0;
1751 // If static counterpart already exists, remove it first.
1752 // This shouldn't happen, but happens rarely for some
1753 // unknown reason. Unsuccessful attempts have been made to
1754 // find said reason.
1755 if(new_id && block->m_static_objects.m_active.find(new_id)){
1756 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1758 block->m_static_objects.remove(new_id);
1760 block->m_static_objects.insert(new_id, s_obj);
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");
1768 obj->m_static_exists = true;
1769 obj->m_static_block = block->getPos();
1774 errorstream<<"ServerEnv: Could not find or generate "
1775 <<"a block for storing id="<<obj->getId()
1776 <<" statically"<<std::endl;
1783 If known by some client, set pending deactivation.
1784 Otherwise delete it immediately.
1787 if(pending_delete && !force_delete)
1789 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1790 <<"object id="<<id<<" is known by clients"
1791 <<"; not deleting yet"<<std::endl;
1793 obj->m_pending_deactivation = true;
1797 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1798 <<"object id="<<id<<" is not known by clients"
1799 <<"; deleting"<<std::endl;
1801 // Tell the object about removal
1802 obj->removingFromEnvironment();
1803 // Deregister in scripting api
1804 scriptapi_rm_object_reference(m_lua, obj);
1806 // Delete active object
1807 if(obj->environmentDeletes())
1809 // Id to be removed from m_active_objects
1810 objects_to_remove.push_back(id);
1813 // Remove references from m_active_objects
1814 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1815 i != objects_to_remove.end(); i++)
1817 m_active_objects.remove(*i);
1824 #include "clientsimpleobject.h"
1830 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1831 ITextureSource *texturesource, IGameDef *gamedef,
1832 IrrlichtDevice *irr):
1835 m_texturesource(texturesource),
1841 ClientEnvironment::~ClientEnvironment()
1843 // delete active objects
1844 for(core::map<u16, ClientActiveObject*>::Iterator
1845 i = m_active_objects.getIterator();
1846 i.atEnd()==false; i++)
1848 delete i.getNode()->getValue();
1851 for(core::list<ClientSimpleObject*>::Iterator
1852 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1861 Map & ClientEnvironment::getMap()
1866 ClientMap & ClientEnvironment::getClientMap()
1871 void ClientEnvironment::addPlayer(Player *player)
1873 DSTACK(__FUNCTION_NAME);
1875 It is a failure if player is local and there already is a local
1878 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1880 Environment::addPlayer(player);
1883 LocalPlayer * ClientEnvironment::getLocalPlayer()
1885 for(core::list<Player*>::Iterator i = m_players.begin();
1886 i != m_players.end(); i++)
1888 Player *player = *i;
1889 if(player->isLocal())
1890 return (LocalPlayer*)player;
1895 void ClientEnvironment::step(float dtime)
1897 DSTACK(__FUNCTION_NAME);
1899 /* Step time of day */
1900 stepTimeOfDay(dtime);
1902 // Get some settings
1903 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1904 bool free_move = fly_allowed && g_settings->getBool("free_move");
1907 LocalPlayer *lplayer = getLocalPlayer();
1909 // collision info queue
1910 core::list<CollisionInfo> player_collisions;
1913 Get the speed the player is going
1915 bool is_climbing = lplayer->is_climbing;
1917 f32 player_speed = lplayer->getSpeed().getLength();
1920 Maximum position increment
1922 //f32 position_max_increment = 0.05*BS;
1923 f32 position_max_increment = 0.1*BS;
1925 // Maximum time increment (for collision detection etc)
1926 // time = distance / speed
1927 f32 dtime_max_increment = 1;
1928 if(player_speed > 0.001)
1929 dtime_max_increment = position_max_increment / player_speed;
1931 // Maximum time increment is 10ms or lower
1932 if(dtime_max_increment > 0.01)
1933 dtime_max_increment = 0.01;
1935 // Don't allow overly huge dtime
1939 f32 dtime_downcount = dtime;
1942 Stuff that has a maximum time increment
1951 if(dtime_downcount > dtime_max_increment)
1953 dtime_part = dtime_max_increment;
1954 dtime_downcount -= dtime_part;
1958 dtime_part = dtime_downcount;
1960 Setting this to 0 (no -=dtime_part) disables an infinite loop
1961 when dtime_part is so small that dtime_downcount -= dtime_part
1964 dtime_downcount = 0;
1972 v3f lplayerpos = lplayer->getPosition();
1975 if(free_move == false && is_climbing == false)
1978 v3f speed = lplayer->getSpeed();
1979 if(lplayer->swimming_up == false)
1980 speed.Y -= 9.81 * BS * dtime_part * 2;
1983 if(lplayer->in_water_stable || lplayer->in_water)
1985 f32 max_down = 2.0*BS;
1986 if(speed.Y < -max_down) speed.Y = -max_down;
1989 if(speed.getLength() > max)
1991 speed = speed / speed.getLength() * max;
1995 lplayer->setSpeed(speed);
2000 This also does collision detection.
2002 lplayer->move(dtime_part, *m_map, position_max_increment,
2003 &player_collisions);
2006 while(dtime_downcount > 0.001);
2008 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2010 for(core::list<CollisionInfo>::Iterator
2011 i = player_collisions.begin();
2012 i != player_collisions.end(); i++)
2014 CollisionInfo &info = *i;
2015 if(info.t == COLLISION_FALL)
2017 //f32 tolerance = BS*10; // 2 without damage
2018 //f32 tolerance = BS*12; // 3 without damage
2019 f32 tolerance = BS*14; // 5 without damage
2021 if(info.speed > tolerance)
2023 f32 damage_f = (info.speed - tolerance)/BS*factor;
2024 u16 damage = (u16)(damage_f+0.5);
2026 damageLocalPlayer(damage, true);
2032 A quick draft of lava damage
2034 if(m_lava_hurt_interval.step(dtime, 1.0))
2036 v3f pf = lplayer->getPosition();
2038 // Feet, middle and head
2039 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2040 MapNode n1 = m_map->getNodeNoEx(p1);
2041 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2042 MapNode n2 = m_map->getNodeNoEx(p2);
2043 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2044 MapNode n3 = m_map->getNodeNoEx(p2);
2046 u32 damage_per_second = 0;
2047 damage_per_second = MYMAX(damage_per_second,
2048 m_gamedef->ndef()->get(n1).damage_per_second);
2049 damage_per_second = MYMAX(damage_per_second,
2050 m_gamedef->ndef()->get(n2).damage_per_second);
2051 damage_per_second = MYMAX(damage_per_second,
2052 m_gamedef->ndef()->get(n3).damage_per_second);
2054 if(damage_per_second != 0)
2056 damageLocalPlayer(damage_per_second, true);
2061 Stuff that can be done in an arbitarily large dtime
2063 for(core::list<Player*>::Iterator i = m_players.begin();
2064 i != m_players.end(); i++)
2066 Player *player = *i;
2067 v3f playerpos = player->getPosition();
2070 Handle non-local players
2072 if(player->isLocal() == false)
2075 player->move(dtime, *m_map, 100*BS);
2079 // Update lighting on all players on client
2080 u8 light = LIGHT_MAX;
2083 v3s16 p = player->getLightPosition();
2084 MapNode n = m_map->getNode(p);
2085 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2087 catch(InvalidPositionException &e){
2088 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2090 player->light = light;
2094 Step active objects and update lighting of them
2097 for(core::map<u16, ClientActiveObject*>::Iterator
2098 i = m_active_objects.getIterator();
2099 i.atEnd()==false; i++)
2101 ClientActiveObject* obj = i.getNode()->getValue();
2103 obj->step(dtime, this);
2105 if(m_active_object_light_update_interval.step(dtime, 0.21))
2111 v3s16 p = obj->getLightPosition();
2112 MapNode n = m_map->getNode(p);
2113 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2115 catch(InvalidPositionException &e){
2116 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2118 obj->updateLight(light);
2123 Step and handle simple objects
2125 for(core::list<ClientSimpleObject*>::Iterator
2126 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2128 ClientSimpleObject *simple = *i;
2129 core::list<ClientSimpleObject*>::Iterator cur = i;
2131 simple->step(dtime);
2132 if(simple->m_to_be_removed){
2134 m_simple_objects.erase(cur);
2139 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2141 m_simple_objects.push_back(simple);
2144 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2146 core::map<u16, ClientActiveObject*>::Node *n;
2147 n = m_active_objects.find(id);
2150 return n->getValue();
2153 bool isFreeClientActiveObjectId(u16 id,
2154 core::map<u16, ClientActiveObject*> &objects)
2159 for(core::map<u16, ClientActiveObject*>::Iterator
2160 i = objects.getIterator();
2161 i.atEnd()==false; i++)
2163 if(i.getNode()->getKey() == id)
2169 u16 getFreeClientActiveObjectId(
2170 core::map<u16, ClientActiveObject*> &objects)
2175 if(isFreeClientActiveObjectId(new_id, objects))
2185 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2188 if(object->getId() == 0)
2190 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2193 infostream<<"ClientEnvironment::addActiveObject(): "
2194 <<"no free ids available"<<std::endl;
2198 object->setId(new_id);
2200 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2202 infostream<<"ClientEnvironment::addActiveObject(): "
2203 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2207 infostream<<"ClientEnvironment::addActiveObject(): "
2208 <<"added (id="<<object->getId()<<")"<<std::endl;
2209 m_active_objects.insert(object->getId(), object);
2210 object->addToScene(m_smgr, m_texturesource, m_irr);
2211 { // Update lighting immediately
2215 v3s16 p = object->getLightPosition();
2216 MapNode n = m_map->getNode(p);
2217 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2219 catch(InvalidPositionException &e){
2220 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2222 object->updateLight(light);
2224 return object->getId();
2227 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2228 const std::string &init_data)
2230 ClientActiveObject* obj =
2231 ClientActiveObject::create(type, m_gamedef, this);
2234 infostream<<"ClientEnvironment::addActiveObject(): "
2235 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2244 obj->initialize(init_data);
2246 catch(SerializationError &e)
2248 errorstream<<"ClientEnvironment::addActiveObject():"
2249 <<" id="<<id<<" type="<<type
2250 <<": SerializationError in initialize(),"
2251 <<" init_data="<<serializeJsonString(init_data)
2255 addActiveObject(obj);
2258 void ClientEnvironment::removeActiveObject(u16 id)
2260 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2261 <<"id="<<id<<std::endl;
2262 ClientActiveObject* obj = getActiveObject(id);
2265 infostream<<"ClientEnvironment::removeActiveObject(): "
2266 <<"id="<<id<<" not found"<<std::endl;
2269 obj->removeFromScene();
2271 m_active_objects.remove(id);
2274 void ClientEnvironment::processActiveObjectMessage(u16 id,
2275 const std::string &data)
2277 ClientActiveObject* obj = getActiveObject(id);
2280 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2281 <<" got message for id="<<id<<", which doesn't exist."
2287 obj->processMessage(data);
2289 catch(SerializationError &e)
2291 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2292 <<" id="<<id<<" type="<<obj->getType()
2293 <<" SerializationError in processMessage(),"
2294 <<" message="<<serializeJsonString(data)
2300 Callbacks for activeobjects
2303 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2305 LocalPlayer *lplayer = getLocalPlayer();
2309 if(lplayer->hp > damage)
2310 lplayer->hp -= damage;
2315 ClientEnvEvent event;
2316 event.type = CEE_PLAYER_DAMAGE;
2317 event.player_damage.amount = damage;
2318 event.player_damage.send_to_server = handle_hp;
2319 m_client_event_queue.push_back(event);
2323 Client likes to call these
2326 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2327 core::array<DistanceSortedActiveObject> &dest)
2329 for(core::map<u16, ClientActiveObject*>::Iterator
2330 i = m_active_objects.getIterator();
2331 i.atEnd()==false; i++)
2333 ClientActiveObject* obj = i.getNode()->getValue();
2335 f32 d = (obj->getPosition() - origin).getLength();
2340 DistanceSortedActiveObject dso(obj, d);
2342 dest.push_back(dso);
2346 ClientEnvEvent ClientEnvironment::getClientEvent()
2348 if(m_client_event_queue.size() == 0)
2350 ClientEnvEvent event;
2351 event.type = CEE_NONE;
2354 return m_client_event_queue.pop_front();
2357 #endif // #ifndef SERVER