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
40 #include "serverremoteplayer.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 Environment::Environment():
49 Environment::~Environment()
52 for(core::list<Player*>::Iterator i = m_players.begin();
53 i != m_players.end(); i++)
59 void Environment::addPlayer(Player *player)
61 DSTACK(__FUNCTION_NAME);
63 Check that peer_ids are unique.
64 Also check that names are unique.
65 Exception: there can be multiple players with peer_id=0
67 // If peer id is non-zero, it has to be unique.
68 if(player->peer_id != 0)
69 assert(getPlayer(player->peer_id) == NULL);
70 // Name has to be unique.
71 assert(getPlayer(player->getName()) == NULL);
73 m_players.push_back(player);
76 void Environment::removePlayer(u16 peer_id)
78 DSTACK(__FUNCTION_NAME);
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id != peer_id)
89 // See if there is an another one
90 // (shouldn't be, but just to be sure)
95 Player * Environment::getPlayer(u16 peer_id)
97 for(core::list<Player*>::Iterator i = m_players.begin();
98 i != m_players.end(); i++)
101 if(player->peer_id == peer_id)
107 Player * Environment::getPlayer(const char *name)
109 for(core::list<Player*>::Iterator i = m_players.begin();
110 i != m_players.end(); i++)
113 if(strcmp(player->getName(), name) == 0)
119 Player * Environment::getRandomConnectedPlayer()
121 core::list<Player*> connected_players = getPlayers(true);
122 u32 chosen_one = myrand() % connected_players.size();
124 for(core::list<Player*>::Iterator
125 i = connected_players.begin();
126 i != connected_players.end(); i++)
138 Player * Environment::getNearestConnectedPlayer(v3f pos)
140 core::list<Player*> connected_players = getPlayers(true);
142 Player *nearest_player = NULL;
143 for(core::list<Player*>::Iterator
144 i = connected_players.begin();
145 i != connected_players.end(); i++)
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL)
152 nearest_player = player;
155 return nearest_player;
158 core::list<Player*> Environment::getPlayers()
163 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
165 core::list<Player*> newlist;
166 for(core::list<Player*>::Iterator
167 i = m_players.begin();
168 i != m_players.end(); i++)
172 if(ignore_disconnected)
174 // Ignore disconnected players
175 if(player->peer_id == 0)
179 newlist.push_back(player);
184 void Environment::printPlayers(std::ostream &o)
186 o<<"Players in environment:"<<std::endl;
187 for(core::list<Player*>::Iterator i = m_players.begin();
188 i != m_players.end(); i++)
191 o<<"Player peer_id="<<player->peer_id<<std::endl;
195 /*void Environment::setDayNightRatio(u32 r)
197 getDayNightRatio() = r;
200 u32 Environment::getDayNightRatio()
202 //return getDayNightRatio();
203 return time_to_daynight_ratio(m_time_of_day);
210 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
214 // Initialize timer to random value to spread processing
215 float itv = abm->getTriggerInterval();
216 itv = MYMAX(0.001, itv); // No less than 1ms
217 int minval = MYMAX(-0.51*itv, -60); // Clamp to
218 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
219 timer = myrand_range(minval, maxval);
226 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
229 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
230 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
231 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
238 void ActiveBlockList::update(core::list<v3s16> &active_positions,
240 core::map<v3s16, bool> &blocks_removed,
241 core::map<v3s16, bool> &blocks_added)
246 core::map<v3s16, bool> newlist;
247 for(core::list<v3s16>::Iterator i = active_positions.begin();
248 i != active_positions.end(); i++)
250 fillRadiusBlock(*i, radius, newlist);
254 Find out which blocks on the old list are not on the new list
256 // Go through old list
257 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
258 i.atEnd()==false; i++)
260 v3s16 p = i.getNode()->getKey();
261 // If not on new list, it's been removed
262 if(newlist.find(p) == NULL)
263 blocks_removed.insert(p, true);
267 Find out which blocks on the new list are not on the old list
269 // Go through new list
270 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
271 i.atEnd()==false; i++)
273 v3s16 p = i.getNode()->getKey();
274 // If not on old list, it's been added
275 if(m_list.find(p) == NULL)
276 blocks_added.insert(p, true);
283 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
284 i.atEnd()==false; i++)
286 v3s16 p = i.getNode()->getKey();
287 m_list.insert(p, true);
295 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
296 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
301 m_random_spawn_timer(3),
302 m_send_recommended_timer(0),
304 m_game_time_fraction_counter(0)
308 ServerEnvironment::~ServerEnvironment()
310 // Clear active block list.
311 // This makes the next one delete all active objects.
312 m_active_blocks.clear();
314 // Convert all objects to static and delete the active objects
315 deactivateFarObjects(true);
320 // Delete ActiveBlockModifiers
321 for(core::list<ABMWithState>::Iterator
322 i = m_abms.begin(); i != m_abms.end(); i++){
327 void ServerEnvironment::serializePlayers(const std::string &savedir)
329 std::string players_path = savedir + "/players";
330 fs::CreateDir(players_path);
332 core::map<Player*, bool> saved_players;
334 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
335 for(u32 i=0; i<player_files.size(); i++)
337 if(player_files[i].dir)
340 // Full path to this file
341 std::string path = players_path + "/" + player_files[i].name;
343 //infostream<<"Checking player file "<<path<<std::endl;
345 // Load player to see what is its name
346 ServerRemotePlayer testplayer(this);
348 // Open file and deserialize
349 std::ifstream is(path.c_str(), std::ios_base::binary);
350 if(is.good() == false)
352 infostream<<"Failed to read "<<path<<std::endl;
355 testplayer.deSerialize(is);
358 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
360 // Search for the player
361 std::string playername = testplayer.getName();
362 Player *player = getPlayer(playername.c_str());
365 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
369 //infostream<<"Found matching player, overwriting."<<std::endl;
371 // OK, found. Save player there.
373 // Open file and serialize
374 std::ofstream os(path.c_str(), std::ios_base::binary);
375 if(os.good() == false)
377 infostream<<"Failed to overwrite "<<path<<std::endl;
380 player->serialize(os);
381 saved_players.insert(player, true);
385 for(core::list<Player*>::Iterator i = m_players.begin();
386 i != m_players.end(); i++)
389 if(saved_players.find(player) != NULL)
391 /*infostream<<"Player "<<player->getName()
392 <<" was already saved."<<std::endl;*/
395 std::string playername = player->getName();
396 // Don't save unnamed player
399 //infostream<<"Not saving unnamed player."<<std::endl;
405 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
406 playername = "player";
407 std::string path = players_path + "/" + playername;
409 for(u32 i=0; i<1000; i++)
411 if(fs::PathExists(path) == false)
416 path = players_path + "/" + playername + itos(i);
420 infostream<<"Didn't find free file for player"<<std::endl;
425 /*infostream<<"Saving player "<<player->getName()<<" to "
427 // Open file and serialize
428 std::ofstream os(path.c_str(), std::ios_base::binary);
429 if(os.good() == false)
431 infostream<<"Failed to overwrite "<<path<<std::endl;
434 player->serialize(os);
435 saved_players.insert(player, true);
439 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
442 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
444 std::string players_path = savedir + "/players";
446 core::map<Player*, bool> saved_players;
448 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
449 for(u32 i=0; i<player_files.size(); i++)
451 if(player_files[i].dir)
454 // Full path to this file
455 std::string path = players_path + "/" + player_files[i].name;
457 infostream<<"Checking player file "<<path<<std::endl;
459 // Load player to see what is its name
460 ServerRemotePlayer testplayer(this);
462 // Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
464 if(is.good() == false)
466 infostream<<"Failed to read "<<path<<std::endl;
469 testplayer.deSerialize(is);
472 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
474 infostream<<"Not loading player with invalid name: "
475 <<testplayer.getName()<<std::endl;
478 infostream<<"Loaded test player with name "<<testplayer.getName()
481 // Search for the player
482 std::string playername = testplayer.getName();
483 Player *player = getPlayer(playername.c_str());
484 bool newplayer = false;
487 infostream<<"Is a new player"<<std::endl;
488 player = new ServerRemotePlayer(this);
492 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
496 infostream<<"Reading player "<<testplayer.getName()<<" from "
498 // Open file and deserialize
499 std::ifstream is(path.c_str(), std::ios_base::binary);
500 if(is.good() == false)
502 infostream<<"Failed to read "<<path<<std::endl;
505 srp->deSerialize(is);
506 srp->m_last_good_position = srp->getBasePosition();
507 srp->m_last_good_position_age = 0;
517 void ServerEnvironment::saveMeta(const std::string &savedir)
519 std::string path = savedir + "/env_meta.txt";
521 // Open file and serialize
522 std::ofstream os(path.c_str(), std::ios_base::binary);
523 if(os.good() == false)
525 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
527 throw SerializationError("Couldn't save env meta");
531 args.setU64("game_time", m_game_time);
532 args.setU64("time_of_day", getTimeOfDay());
537 void ServerEnvironment::loadMeta(const std::string &savedir)
539 std::string path = savedir + "/env_meta.txt";
541 // Open file and deserialize
542 std::ifstream is(path.c_str(), std::ios_base::binary);
543 if(is.good() == false)
545 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
547 throw SerializationError("Couldn't load env meta");
555 throw SerializationError
556 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
558 std::getline(is, line);
559 std::string trimmedline = trim(line);
560 if(trimmedline == "EnvArgsEnd")
562 args.parseConfigLine(line);
566 m_game_time = args.getU64("game_time");
567 }catch(SettingNotFoundException &e){
568 // Getting this is crucial, otherwise timestamps are useless
569 throw SerializationError("Couldn't load env meta game_time");
573 m_time_of_day = args.getU64("time_of_day");
574 }catch(SettingNotFoundException &e){
575 // This is not as important
576 m_time_of_day = 9000;
582 ActiveBlockModifier *abm;
584 std::set<content_t> required_neighbors;
590 ServerEnvironment *m_env;
591 std::map<content_t, std::list<ActiveABM> > m_aabms;
593 ABMHandler(core::list<ABMWithState> &abms,
594 float dtime_s, ServerEnvironment *env,
600 INodeDefManager *ndef = env->getGameDef()->ndef();
601 for(core::list<ABMWithState>::Iterator
602 i = abms.begin(); i != abms.end(); i++){
603 ActiveBlockModifier *abm = i->abm;
604 float trigger_interval = abm->getTriggerInterval();
605 if(trigger_interval < 0.001)
606 trigger_interval = 0.001;
607 float actual_interval = dtime_s;
610 if(i->timer < trigger_interval)
612 i->timer -= trigger_interval;
613 actual_interval = trigger_interval;
617 float intervals = actual_interval / trigger_interval;
618 float chance = abm->getTriggerChance();
621 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
625 std::set<std::string> required_neighbors_s
626 = abm->getRequiredNeighbors();
627 for(std::set<std::string>::iterator
628 i = required_neighbors_s.begin();
629 i != required_neighbors_s.end(); i++){
630 content_t c = ndef->getId(*i);
631 if(c == CONTENT_IGNORE)
633 aabm.required_neighbors.insert(c);
636 std::set<std::string> contents_s = abm->getTriggerContents();
637 for(std::set<std::string>::iterator
638 i = contents_s.begin(); i != contents_s.end(); i++){
639 content_t c = ndef->getId(*i);
640 if(c == CONTENT_IGNORE)
642 std::map<content_t, std::list<ActiveABM> >::iterator j;
644 if(j == m_aabms.end()){
645 std::list<ActiveABM> aabmlist;
646 m_aabms[c] = aabmlist;
649 j->second.push_back(aabm);
653 void apply(MapBlock *block)
658 ServerMap *map = &m_env->getServerMap();
661 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
662 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
663 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
665 MapNode n = block->getNodeNoEx(p0);
666 content_t c = n.getContent();
667 v3s16 p = p0 + block->getPosRelative();
669 std::map<content_t, std::list<ActiveABM> >::iterator j;
671 if(j == m_aabms.end())
674 for(std::list<ActiveABM>::iterator
675 i = j->second.begin(); i != j->second.end(); i++)
677 if(myrand() % i->chance != 0)
681 if(!i->required_neighbors.empty())
684 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
685 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
686 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
690 MapNode n = map->getNodeNoEx(p1);
691 content_t c = n.getContent();
692 std::set<content_t>::const_iterator k;
693 k = i->required_neighbors.find(c);
694 if(k != i->required_neighbors.end()){
698 // No required neighbor found
703 // Find out how many objects the block contains
704 u32 active_object_count = block->m_static_objects.m_active.size();
705 // Find out how many objects this and all the neighbors contain
706 u32 active_object_count_wider = 0;
707 for(s16 x=-1; x<=1; x++)
708 for(s16 y=-1; y<=1; y++)
709 for(s16 z=-1; z<=1; z++)
711 MapBlock *block2 = map->getBlockNoCreateNoEx(
712 block->getPos() + v3s16(x,y,z));
715 active_object_count_wider +=
716 block2->m_static_objects.m_active.size()
717 + block2->m_static_objects.m_stored.size();
720 // Call all the trigger variations
721 i->abm->trigger(m_env, p, n);
722 i->abm->trigger(m_env, p, n,
723 active_object_count, active_object_count_wider);
729 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
731 // Get time difference
733 u32 stamp = block->getTimestamp();
734 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
735 dtime_s = m_game_time - block->getTimestamp();
736 dtime_s += additional_dtime;
738 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
739 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
741 // Set current time as timestamp
742 block->setTimestampNoChangedFlag(m_game_time);
744 /*infostream<<"ServerEnvironment::activateBlock(): block is "
745 <<dtime_s<<" seconds old."<<std::endl;*/
747 // Activate stored objects
748 activateObjects(block);
751 bool changed = block->m_node_metadata->step((float)dtime_s);
755 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
756 event.p = block->getPos();
757 m_map->dispatchEvent(&event);
759 block->raiseModified(MOD_STATE_WRITE_NEEDED,
760 "node metadata modified in activateBlock");
763 /* Handle ActiveBlockModifiers */
764 ABMHandler abmhandler(m_abms, dtime_s, this, false);
765 abmhandler.apply(block);
768 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
770 m_abms.push_back(ABMWithState(abm));
773 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
775 std::set<u16> objects;
776 for(core::map<u16, ServerActiveObject*>::Iterator
777 i = m_active_objects.getIterator();
778 i.atEnd()==false; i++)
780 ServerActiveObject* obj = i.getNode()->getValue();
781 u16 id = i.getNode()->getKey();
782 v3f objectpos = obj->getBasePosition();
783 if(objectpos.getDistanceFrom(pos) > radius)
790 void ServerEnvironment::clearAllObjects()
792 infostream<<"ServerEnvironment::clearAllObjects(): "
793 <<"Removing all active objects"<<std::endl;
794 core::list<u16> objects_to_remove;
795 for(core::map<u16, ServerActiveObject*>::Iterator
796 i = m_active_objects.getIterator();
797 i.atEnd()==false; i++)
799 ServerActiveObject* obj = i.getNode()->getValue();
800 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
802 u16 id = i.getNode()->getKey();
803 v3f objectpos = obj->getBasePosition();
804 // Delete static object if block is loaded
805 if(obj->m_static_exists){
806 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
808 block->m_static_objects.remove(id);
809 block->raiseModified(MOD_STATE_WRITE_NEEDED,
811 obj->m_static_exists = false;
814 // If known by some client, don't delete immediately
815 if(obj->m_known_by_count > 0){
816 obj->m_pending_deactivation = true;
817 obj->m_removed = true;
821 // Tell the object about removal
822 obj->removingFromEnvironment();
823 // Deregister in scripting api
824 scriptapi_rm_object_reference(m_lua, obj);
826 // Delete active object
827 if(obj->environmentDeletes())
829 // Id to be removed from m_active_objects
830 objects_to_remove.push_back(id);
832 // Remove references from m_active_objects
833 for(core::list<u16>::Iterator i = objects_to_remove.begin();
834 i != objects_to_remove.end(); i++)
836 m_active_objects.remove(*i);
839 core::list<v3s16> loadable_blocks;
840 infostream<<"ServerEnvironment::clearAllObjects(): "
841 <<"Listing all loadable blocks"<<std::endl;
842 m_map->listAllLoadableBlocks(loadable_blocks);
843 infostream<<"ServerEnvironment::clearAllObjects(): "
844 <<"Done listing all loadable blocks: "
845 <<loadable_blocks.size()
846 <<", now clearing"<<std::endl;
847 u32 report_interval = loadable_blocks.size() / 10;
848 u32 num_blocks_checked = 0;
849 u32 num_blocks_cleared = 0;
850 u32 num_objs_cleared = 0;
851 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
852 i != loadable_blocks.end(); i++)
855 MapBlock *block = m_map->emergeBlock(p, false);
857 errorstream<<"ServerEnvironment::clearAllObjects(): "
858 <<"Failed to emerge block "<<PP(p)<<std::endl;
861 u32 num_stored = block->m_static_objects.m_stored.size();
862 u32 num_active = block->m_static_objects.m_active.size();
863 if(num_stored != 0 || num_active != 0){
864 block->m_static_objects.m_stored.clear();
865 block->m_static_objects.m_active.clear();
866 block->raiseModified(MOD_STATE_WRITE_NEEDED,
868 num_objs_cleared += num_stored + num_active;
869 num_blocks_cleared++;
871 num_blocks_checked++;
873 if(num_blocks_checked % report_interval == 0){
874 float percent = 100.0 * (float)num_blocks_checked /
875 loadable_blocks.size();
876 infostream<<"ServerEnvironment::clearAllObjects(): "
877 <<"Cleared "<<num_objs_cleared<<" objects"
878 <<" in "<<num_blocks_cleared<<" blocks ("
879 <<percent<<"%)"<<std::endl;
882 infostream<<"ServerEnvironment::clearAllObjects(): "
883 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
884 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
887 void ServerEnvironment::step(float dtime)
889 DSTACK(__FUNCTION_NAME);
891 //TimeTaker timer("ServerEnv step");
894 bool footprints = g_settings->getBool("footprints");
900 m_game_time_fraction_counter += dtime;
901 u32 inc_i = (u32)m_game_time_fraction_counter;
902 m_game_time += inc_i;
903 m_game_time_fraction_counter -= (float)inc_i;
910 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
911 for(core::list<Player*>::Iterator i = m_players.begin();
912 i != m_players.end(); i++)
916 // Ignore disconnected players
917 if(player->peer_id == 0)
920 v3f playerpos = player->getPosition();
923 player->move(dtime, *m_map, 100*BS);
926 Add footsteps to grass
930 // Get node that is at BS/4 under player
931 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
933 MapNode n = m_map->getNode(bottompos);
934 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
936 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
937 m_map->setNode(bottompos, n);
940 catch(InvalidPositionException &e)
948 Manage active block list
950 if(m_active_blocks_management_interval.step(dtime, 2.0))
952 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
954 Get player block positions
956 core::list<v3s16> players_blockpos;
957 for(core::list<Player*>::Iterator
958 i = m_players.begin();
959 i != m_players.end(); i++)
962 // Ignore disconnected players
963 if(player->peer_id == 0)
965 v3s16 blockpos = getNodeBlockPos(
966 floatToInt(player->getPosition(), BS));
967 players_blockpos.push_back(blockpos);
971 Update list of active blocks, collecting changes
973 const s16 active_block_range = g_settings->getS16("active_block_range");
974 core::map<v3s16, bool> blocks_removed;
975 core::map<v3s16, bool> blocks_added;
976 m_active_blocks.update(players_blockpos, active_block_range,
977 blocks_removed, blocks_added);
980 Handle removed blocks
983 // Convert active objects that are no more in active blocks to static
984 deactivateFarObjects(false);
986 for(core::map<v3s16, bool>::Iterator
987 i = blocks_removed.getIterator();
988 i.atEnd()==false; i++)
990 v3s16 p = i.getNode()->getKey();
992 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
993 <<") became inactive"<<std::endl;*/
995 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
999 // Set current time as timestamp (and let it set ChangedFlag)
1000 block->setTimestamp(m_game_time);
1007 for(core::map<v3s16, bool>::Iterator
1008 i = blocks_added.getIterator();
1009 i.atEnd()==false; i++)
1011 v3s16 p = i.getNode()->getKey();
1013 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1014 <<") became active"<<std::endl;*/
1016 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1018 // Block needs to be fetched first
1019 m_emerger->queueBlockEmerge(p, false);
1020 m_active_blocks.m_list.remove(p);
1024 activateBlock(block);
1029 Mess around in active blocks
1031 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1033 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1037 for(core::map<v3s16, bool>::Iterator
1038 i = m_active_blocks.m_list.getIterator();
1039 i.atEnd()==false; i++)
1041 v3s16 p = i.getNode()->getKey();
1043 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1044 <<") being handled"<<std::endl;*/
1046 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1050 // Reset block usage timer
1051 block->resetUsageTimer();
1053 // Set current time as timestamp
1054 block->setTimestampNoChangedFlag(m_game_time);
1055 // If time has changed much from the one on disk,
1056 // set block to be saved when it is unloaded
1057 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1058 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1059 "Timestamp older than 60s (step)");
1061 // Run node metadata
1062 bool changed = block->m_node_metadata->step(dtime);
1066 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1068 m_map->dispatchEvent(&event);
1070 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1071 "node metadata modified in step");
1076 const float abm_interval = 1.0;
1077 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1079 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1080 TimeTaker timer("modify in active blocks");
1082 // Initialize handling of ActiveBlockModifiers
1083 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1085 for(core::map<v3s16, bool>::Iterator
1086 i = m_active_blocks.m_list.getIterator();
1087 i.atEnd()==false; i++)
1089 v3s16 p = i.getNode()->getKey();
1091 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1092 <<") being handled"<<std::endl;*/
1094 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1098 // Set current time as timestamp
1099 block->setTimestampNoChangedFlag(m_game_time);
1101 /* Handle ActiveBlockModifiers */
1102 abmhandler.apply(block);
1105 u32 time_ms = timer.stop(true);
1106 u32 max_time_ms = 200;
1107 if(time_ms > max_time_ms){
1108 infostream<<"WARNING: active block modifiers took "
1109 <<time_ms<<"ms (longer than "
1110 <<max_time_ms<<"ms)"<<std::endl;
1115 Step script environment (run global on_step())
1117 scriptapi_environment_step(m_lua, dtime);
1123 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1124 //TimeTaker timer("Step active objects");
1126 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1128 // This helps the objects to send data at the same time
1129 bool send_recommended = false;
1130 m_send_recommended_timer += dtime;
1131 if(m_send_recommended_timer > getSendRecommendedInterval())
1133 m_send_recommended_timer -= getSendRecommendedInterval();
1134 send_recommended = true;
1137 for(core::map<u16, ServerActiveObject*>::Iterator
1138 i = m_active_objects.getIterator();
1139 i.atEnd()==false; i++)
1141 ServerActiveObject* obj = i.getNode()->getValue();
1142 // Remove non-peaceful mobs on peaceful mode
1143 if(g_settings->getBool("only_peaceful_mobs")){
1144 if(!obj->isPeaceful())
1145 obj->m_removed = true;
1147 // Don't step if is to be removed or stored statically
1148 if(obj->m_removed || obj->m_pending_deactivation)
1151 obj->step(dtime, send_recommended);
1152 // Read messages from object
1153 while(obj->m_messages_out.size() > 0)
1155 m_active_object_messages.push_back(
1156 obj->m_messages_out.pop_front());
1162 Manage active objects
1164 if(m_object_management_interval.step(dtime, 0.5))
1166 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1168 Remove objects that satisfy (m_removed && m_known_by_count==0)
1170 removeRemovedObjects();
1174 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1176 core::map<u16, ServerActiveObject*>::Node *n;
1177 n = m_active_objects.find(id);
1180 return n->getValue();
1183 bool isFreeServerActiveObjectId(u16 id,
1184 core::map<u16, ServerActiveObject*> &objects)
1189 for(core::map<u16, ServerActiveObject*>::Iterator
1190 i = objects.getIterator();
1191 i.atEnd()==false; i++)
1193 if(i.getNode()->getKey() == id)
1199 u16 getFreeServerActiveObjectId(
1200 core::map<u16, ServerActiveObject*> &objects)
1205 if(isFreeServerActiveObjectId(new_id, objects))
1215 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1218 u16 id = addActiveObjectRaw(object, true);
1222 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1226 v3f objectpos = obj->getBasePosition();
1228 // The block in which the object resides in
1229 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1232 Update the static data
1235 // Create new static object
1236 std::string staticdata = obj->getStaticData();
1237 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1238 // Add to the block where the object is located in
1239 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1240 // Get or generate the block
1241 MapBlock *block = m_map->emergeBlock(blockpos);
1243 bool succeeded = false;
1247 block->m_static_objects.insert(0, s_obj);
1248 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1249 "addActiveObjectAsStatic");
1253 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1254 <<"Could not find or generate "
1255 <<"a block for storing static object"<<std::endl;
1259 if(obj->environmentDeletes())
1266 Finds out what new objects have been added to
1267 inside a radius around a position
1269 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1270 core::map<u16, bool> ¤t_objects,
1271 core::map<u16, bool> &added_objects)
1273 v3f pos_f = intToFloat(pos, BS);
1274 f32 radius_f = radius * BS;
1276 Go through the object list,
1277 - discard m_removed objects,
1278 - discard objects that are too far away,
1279 - discard objects that are found in current_objects.
1280 - add remaining objects to added_objects
1282 for(core::map<u16, ServerActiveObject*>::Iterator
1283 i = m_active_objects.getIterator();
1284 i.atEnd()==false; i++)
1286 u16 id = i.getNode()->getKey();
1288 ServerActiveObject *object = i.getNode()->getValue();
1291 // Discard if removed
1292 if(object->m_removed)
1294 if(object->unlimitedTransferDistance() == false){
1295 // Discard if too far
1296 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1297 if(distance_f > radius_f)
1300 // Discard if already on current_objects
1301 core::map<u16, bool>::Node *n;
1302 n = current_objects.find(id);
1305 // Add to added_objects
1306 added_objects.insert(id, false);
1311 Finds out what objects have been removed from
1312 inside a radius around a position
1314 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1315 core::map<u16, bool> ¤t_objects,
1316 core::map<u16, bool> &removed_objects)
1318 v3f pos_f = intToFloat(pos, BS);
1319 f32 radius_f = radius * BS;
1321 Go through current_objects; object is removed if:
1322 - object is not found in m_active_objects (this is actually an
1323 error condition; objects should be set m_removed=true and removed
1324 only after all clients have been informed about removal), or
1325 - object has m_removed=true, or
1326 - object is too far away
1328 for(core::map<u16, bool>::Iterator
1329 i = current_objects.getIterator();
1330 i.atEnd()==false; i++)
1332 u16 id = i.getNode()->getKey();
1333 ServerActiveObject *object = getActiveObject(id);
1336 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1337 <<" object in current_objects is NULL"<<std::endl;
1338 removed_objects.insert(id, false);
1342 if(object->m_removed)
1344 removed_objects.insert(id, false);
1348 // If transfer distance is unlimited, don't remove
1349 if(object->unlimitedTransferDistance())
1352 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1354 if(distance_f >= radius_f)
1356 removed_objects.insert(id, false);
1364 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1366 if(m_active_object_messages.size() == 0)
1367 return ActiveObjectMessage(0);
1369 return m_active_object_messages.pop_front();
1373 ************ Private methods *************
1376 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1380 if(object->getId() == 0){
1381 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1384 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1385 <<"no free ids available"<<std::endl;
1386 if(object->environmentDeletes())
1390 object->setId(new_id);
1393 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1394 <<"supplied with id "<<object->getId()<<std::endl;
1396 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1398 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1399 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1400 if(object->environmentDeletes())
1404 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1405 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1407 m_active_objects.insert(object->getId(), object);
1409 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1410 <<"Added id="<<object->getId()<<"; there are now "
1411 <<m_active_objects.size()<<" active objects."
1414 // Register reference in scripting api (must be done before post-init)
1415 scriptapi_add_object_reference(m_lua, object);
1416 // Post-initialize object
1417 object->addedToEnvironment();
1419 // Add static data to block
1420 if(object->isStaticAllowed())
1422 // Add static object to active static list of the block
1423 v3f objectpos = object->getBasePosition();
1424 std::string staticdata = object->getStaticData();
1425 StaticObject s_obj(object->getType(), objectpos, staticdata);
1426 // Add to the block where the object is located in
1427 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1428 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1431 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1432 object->m_static_exists = true;
1433 object->m_static_block = blockpos;
1436 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1437 "addActiveObjectRaw");
1440 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1441 <<"could not find block for storing id="<<object->getId()
1442 <<" statically"<<std::endl;
1446 return object->getId();
1450 Remove objects that satisfy (m_removed && m_known_by_count==0)
1452 void ServerEnvironment::removeRemovedObjects()
1454 core::list<u16> objects_to_remove;
1455 for(core::map<u16, ServerActiveObject*>::Iterator
1456 i = m_active_objects.getIterator();
1457 i.atEnd()==false; i++)
1459 u16 id = i.getNode()->getKey();
1460 ServerActiveObject* obj = i.getNode()->getValue();
1461 // This shouldn't happen but check it
1464 infostream<<"NULL object found in ServerEnvironment"
1465 <<" while finding removed objects. id="<<id<<std::endl;
1466 // Id to be removed from m_active_objects
1467 objects_to_remove.push_back(id);
1472 We will delete objects that are marked as removed or thatare
1473 waiting for deletion after deactivation
1475 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1479 Delete static data from block if is marked as removed
1481 if(obj->m_static_exists && obj->m_removed)
1483 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1486 block->m_static_objects.remove(id);
1487 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1488 "removeRemovedObjects");
1489 obj->m_static_exists = false;
1493 // If m_known_by_count > 0, don't actually remove.
1494 if(obj->m_known_by_count > 0)
1497 // Tell the object about removal
1498 obj->removingFromEnvironment();
1499 // Deregister in scripting api
1500 scriptapi_rm_object_reference(m_lua, obj);
1503 if(obj->environmentDeletes())
1505 // Id to be removed from m_active_objects
1506 objects_to_remove.push_back(id);
1508 // Remove references from m_active_objects
1509 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1510 i != objects_to_remove.end(); i++)
1512 m_active_objects.remove(*i);
1516 static void print_hexdump(std::ostream &o, const std::string &data)
1518 const int linelength = 16;
1519 for(int l=0; ; l++){
1520 int i0 = linelength * l;
1521 bool at_end = false;
1522 int thislinelength = linelength;
1523 if(i0 + thislinelength > (int)data.size()){
1524 thislinelength = data.size() - i0;
1527 for(int di=0; di<linelength; di++){
1530 if(di<thislinelength)
1531 snprintf(buf, 4, "%.2x ", data[i]);
1533 snprintf(buf, 4, " ");
1537 for(int di=0; di<thislinelength; di++){
1551 Convert stored objects from blocks near the players to active.
1553 void ServerEnvironment::activateObjects(MapBlock *block)
1557 // Ignore if no stored objects (to not set changed flag)
1558 if(block->m_static_objects.m_stored.size() == 0)
1560 verbosestream<<"ServerEnvironment::activateObjects(): "
1561 <<"activating objects of block "<<PP(block->getPos())
1562 <<" ("<<block->m_static_objects.m_stored.size()
1563 <<" objects)"<<std::endl;
1564 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1566 errorstream<<"suspiciously large amount of objects detected: "
1567 <<block->m_static_objects.m_stored.size()<<" in "
1568 <<PP(block->getPos())
1569 <<"; removing all of them."<<std::endl;
1570 // Clear stored list
1571 block->m_static_objects.m_stored.clear();
1572 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1573 "stored list cleared in activateObjects due to "
1574 "large amount of objects");
1577 // A list for objects that couldn't be converted to static for some
1578 // reason. They will be stored back.
1579 core::list<StaticObject> new_stored;
1580 // Loop through stored static objects
1581 for(core::list<StaticObject>::Iterator
1582 i = block->m_static_objects.m_stored.begin();
1583 i != block->m_static_objects.m_stored.end(); i++)
1585 /*infostream<<"Server: Creating an active object from "
1586 <<"static data"<<std::endl;*/
1587 StaticObject &s_obj = *i;
1588 // Create an active object from the data
1589 ServerActiveObject *obj = ServerActiveObject::create
1590 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1591 // If couldn't create object, store static data back.
1594 errorstream<<"ServerEnvironment::activateObjects(): "
1595 <<"failed to create active object from static object "
1596 <<"in block "<<PP(s_obj.pos/BS)
1597 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1598 print_hexdump(verbosestream, s_obj.data);
1600 new_stored.push_back(s_obj);
1603 verbosestream<<"ServerEnvironment::activateObjects(): "
1604 <<"activated static object pos="<<PP(s_obj.pos/BS)
1605 <<" type="<<(int)s_obj.type<<std::endl;
1606 // This will also add the object to the active static list
1607 addActiveObjectRaw(obj, false);
1609 // Clear stored list
1610 block->m_static_objects.m_stored.clear();
1611 // Add leftover failed stuff to stored list
1612 for(core::list<StaticObject>::Iterator
1613 i = new_stored.begin();
1614 i != new_stored.end(); i++)
1616 StaticObject &s_obj = *i;
1617 block->m_static_objects.m_stored.push_back(s_obj);
1620 Note: Block hasn't really been modified here.
1621 The objects have just been activated and moved from the stored
1622 static list to the active static list.
1623 As such, the block is essentially the same.
1624 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1625 Otherwise there would be a huge amount of unnecessary I/O.
1630 Convert objects that are not standing inside active blocks to static.
1632 If m_known_by_count != 0, active object is not deleted, but static
1633 data is still updated.
1635 If force_delete is set, active object is deleted nevertheless. It
1636 shall only be set so in the destructor of the environment.
1638 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1640 core::list<u16> objects_to_remove;
1641 for(core::map<u16, ServerActiveObject*>::Iterator
1642 i = m_active_objects.getIterator();
1643 i.atEnd()==false; i++)
1645 ServerActiveObject* obj = i.getNode()->getValue();
1648 // Do not deactivate if static data creation not allowed
1649 if(!force_delete && !obj->isStaticAllowed())
1652 // If pending deactivation, let removeRemovedObjects() do it
1653 if(!force_delete && obj->m_pending_deactivation)
1656 u16 id = i.getNode()->getKey();
1657 v3f objectpos = obj->getBasePosition();
1659 // The block in which the object resides in
1660 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1662 // If block is active, don't remove
1663 if(!force_delete && m_active_blocks.contains(blockpos_o))
1666 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1667 <<"deactivating object id="<<id<<" on inactive block "
1668 <<PP(blockpos_o)<<std::endl;
1670 // If known by some client, don't immediately delete.
1671 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1674 Update the static data
1677 if(obj->isStaticAllowed())
1679 // Create new static object
1680 std::string staticdata_new = obj->getStaticData();
1681 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1683 bool stays_in_same_block = false;
1684 bool data_changed = true;
1686 if(obj->m_static_exists){
1687 if(obj->m_static_block == blockpos_o)
1688 stays_in_same_block = true;
1690 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1692 core::map<u16, StaticObject>::Node *n =
1693 block->m_static_objects.m_active.find(id);
1695 StaticObject static_old = n->getValue();
1697 float save_movem = obj->getMinimumSavedMovement();
1699 if(static_old.data == staticdata_new &&
1700 (static_old.pos - objectpos).getLength() < save_movem)
1701 data_changed = false;
1703 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1704 <<"id="<<id<<" m_static_exists=true but "
1705 <<"static data doesn't actually exist in "
1706 <<PP(obj->m_static_block)<<std::endl;
1710 bool shall_be_written = (!stays_in_same_block || data_changed);
1712 // Delete old static object
1713 if(obj->m_static_exists)
1715 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1718 block->m_static_objects.remove(id);
1719 obj->m_static_exists = false;
1720 // Only mark block as modified if data changed considerably
1721 if(shall_be_written)
1722 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1723 "deactivateFarObjects: Static data "
1724 "changed considerably");
1728 // Add to the block where the object is located in
1729 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1730 // Get or generate the block
1731 MapBlock *block = m_map->emergeBlock(blockpos);
1735 if(block->m_static_objects.m_stored.size() >= 49){
1736 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1737 <<" statically but block "<<PP(blockpos)
1738 <<" already contains "
1739 <<block->m_static_objects.m_stored.size()
1740 <<" (over 49) objects."
1741 <<" Forcing delete."<<std::endl;
1742 force_delete = true;
1744 u16 new_id = pending_delete ? id : 0;
1745 block->m_static_objects.insert(new_id, s_obj);
1747 // Only mark block as modified if data changed considerably
1748 if(shall_be_written)
1749 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1750 "deactivateFarObjects: Static data "
1751 "changed considerably");
1753 obj->m_static_exists = true;
1754 obj->m_static_block = block->getPos();
1759 errorstream<<"ServerEnv: Could not find or generate "
1760 <<"a block for storing id="<<obj->getId()
1761 <<" statically"<<std::endl;
1768 If known by some client, set pending deactivation.
1769 Otherwise delete it immediately.
1772 if(pending_delete && !force_delete)
1774 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1775 <<"object id="<<id<<" is known by clients"
1776 <<"; not deleting yet"<<std::endl;
1778 obj->m_pending_deactivation = true;
1782 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1783 <<"object id="<<id<<" is not known by clients"
1784 <<"; deleting"<<std::endl;
1786 // Tell the object about removal
1787 obj->removingFromEnvironment();
1788 // Deregister in scripting api
1789 scriptapi_rm_object_reference(m_lua, obj);
1791 // Delete active object
1792 if(obj->environmentDeletes())
1794 // Id to be removed from m_active_objects
1795 objects_to_remove.push_back(id);
1798 // Remove references from m_active_objects
1799 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1800 i != objects_to_remove.end(); i++)
1802 m_active_objects.remove(*i);
1813 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1814 ITextureSource *texturesource, IGameDef *gamedef,
1815 IrrlichtDevice *irr):
1818 m_texturesource(texturesource),
1824 ClientEnvironment::~ClientEnvironment()
1826 // delete active objects
1827 for(core::map<u16, ClientActiveObject*>::Iterator
1828 i = m_active_objects.getIterator();
1829 i.atEnd()==false; i++)
1831 delete i.getNode()->getValue();
1838 void ClientEnvironment::addPlayer(Player *player)
1840 DSTACK(__FUNCTION_NAME);
1842 It is a failure if player is local and there already is a local
1845 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1847 Environment::addPlayer(player);
1850 LocalPlayer * ClientEnvironment::getLocalPlayer()
1852 for(core::list<Player*>::Iterator i = m_players.begin();
1853 i != m_players.end(); i++)
1855 Player *player = *i;
1856 if(player->isLocal())
1857 return (LocalPlayer*)player;
1862 void ClientEnvironment::step(float dtime)
1864 DSTACK(__FUNCTION_NAME);
1866 // Get some settings
1867 bool free_move = g_settings->getBool("free_move");
1868 bool footprints = g_settings->getBool("footprints");
1871 LocalPlayer *lplayer = getLocalPlayer();
1873 // collision info queue
1874 core::list<CollisionInfo> player_collisions;
1877 Get the speed the player is going
1879 bool is_climbing = lplayer->is_climbing;
1881 f32 player_speed = lplayer->getSpeed().getLength();
1884 Maximum position increment
1886 //f32 position_max_increment = 0.05*BS;
1887 f32 position_max_increment = 0.1*BS;
1889 // Maximum time increment (for collision detection etc)
1890 // time = distance / speed
1891 f32 dtime_max_increment = 1;
1892 if(player_speed > 0.001)
1893 dtime_max_increment = position_max_increment / player_speed;
1895 // Maximum time increment is 10ms or lower
1896 if(dtime_max_increment > 0.01)
1897 dtime_max_increment = 0.01;
1899 // Don't allow overly huge dtime
1903 f32 dtime_downcount = dtime;
1906 Stuff that has a maximum time increment
1915 if(dtime_downcount > dtime_max_increment)
1917 dtime_part = dtime_max_increment;
1918 dtime_downcount -= dtime_part;
1922 dtime_part = dtime_downcount;
1924 Setting this to 0 (no -=dtime_part) disables an infinite loop
1925 when dtime_part is so small that dtime_downcount -= dtime_part
1928 dtime_downcount = 0;
1936 v3f lplayerpos = lplayer->getPosition();
1939 if(free_move == false && is_climbing == false)
1942 v3f speed = lplayer->getSpeed();
1943 if(lplayer->swimming_up == false)
1944 speed.Y -= 9.81 * BS * dtime_part * 2;
1947 if(lplayer->in_water_stable || lplayer->in_water)
1949 f32 max_down = 2.0*BS;
1950 if(speed.Y < -max_down) speed.Y = -max_down;
1953 if(speed.getLength() > max)
1955 speed = speed / speed.getLength() * max;
1959 lplayer->setSpeed(speed);
1964 This also does collision detection.
1966 lplayer->move(dtime_part, *m_map, position_max_increment,
1967 &player_collisions);
1970 while(dtime_downcount > 0.001);
1972 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1974 for(core::list<CollisionInfo>::Iterator
1975 i = player_collisions.begin();
1976 i != player_collisions.end(); i++)
1978 CollisionInfo &info = *i;
1979 if(info.t == COLLISION_FALL)
1981 //f32 tolerance = BS*10; // 2 without damage
1982 f32 tolerance = BS*12; // 3 without damage
1984 if(info.speed > tolerance)
1986 f32 damage_f = (info.speed - tolerance)/BS*factor;
1987 u16 damage = (u16)(damage_f+0.5);
1988 damageLocalPlayer(damage, true);
1994 A quick draft of lava damage
1996 if(m_lava_hurt_interval.step(dtime, 1.0))
1998 v3f pf = lplayer->getPosition();
2000 // Feet, middle and head
2001 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2002 MapNode n1 = m_map->getNodeNoEx(p1);
2003 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2004 MapNode n2 = m_map->getNodeNoEx(p2);
2005 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2006 MapNode n3 = m_map->getNodeNoEx(p2);
2008 u32 damage_per_second = 0;
2009 damage_per_second = MYMAX(damage_per_second,
2010 m_gamedef->ndef()->get(n1).damage_per_second);
2011 damage_per_second = MYMAX(damage_per_second,
2012 m_gamedef->ndef()->get(n2).damage_per_second);
2013 damage_per_second = MYMAX(damage_per_second,
2014 m_gamedef->ndef()->get(n3).damage_per_second);
2016 if(damage_per_second != 0)
2018 damageLocalPlayer(damage_per_second, true);
2023 Stuff that can be done in an arbitarily large dtime
2025 for(core::list<Player*>::Iterator i = m_players.begin();
2026 i != m_players.end(); i++)
2028 Player *player = *i;
2029 v3f playerpos = player->getPosition();
2032 Handle non-local players
2034 if(player->isLocal() == false)
2037 player->move(dtime, *m_map, 100*BS);
2041 // Update lighting on all players on client
2042 u8 light = LIGHT_MAX;
2045 v3s16 p = player->getLightPosition();
2046 MapNode n = m_map->getNode(p);
2047 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2049 catch(InvalidPositionException &e){
2050 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2052 player->updateLight(light);
2055 Add footsteps to grass
2059 // Get node that is at BS/4 under player
2060 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2062 MapNode n = m_map->getNode(bottompos);
2063 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2065 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2066 m_map->setNode(bottompos, n);
2067 // Update mesh on client
2068 if(m_map->mapType() == MAPTYPE_CLIENT)
2070 v3s16 p_blocks = getNodeBlockPos(bottompos);
2071 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2072 //b->updateMesh(getDayNightRatio());
2073 b->setMeshExpired(true);
2077 catch(InvalidPositionException &e)
2084 Step active objects and update lighting of them
2087 for(core::map<u16, ClientActiveObject*>::Iterator
2088 i = m_active_objects.getIterator();
2089 i.atEnd()==false; i++)
2091 ClientActiveObject* obj = i.getNode()->getValue();
2093 obj->step(dtime, this);
2095 if(m_active_object_light_update_interval.step(dtime, 0.21))
2101 v3s16 p = obj->getLightPosition();
2102 MapNode n = m_map->getNode(p);
2103 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2105 catch(InvalidPositionException &e){
2106 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2108 obj->updateLight(light);
2113 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2115 m_map->updateMeshes(blockpos, getDayNightRatio());
2118 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2120 m_map->expireMeshes(only_daynight_diffed);
2123 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2125 core::map<u16, ClientActiveObject*>::Node *n;
2126 n = m_active_objects.find(id);
2129 return n->getValue();
2132 bool isFreeClientActiveObjectId(u16 id,
2133 core::map<u16, ClientActiveObject*> &objects)
2138 for(core::map<u16, ClientActiveObject*>::Iterator
2139 i = objects.getIterator();
2140 i.atEnd()==false; i++)
2142 if(i.getNode()->getKey() == id)
2148 u16 getFreeClientActiveObjectId(
2149 core::map<u16, ClientActiveObject*> &objects)
2154 if(isFreeClientActiveObjectId(new_id, objects))
2164 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2167 if(object->getId() == 0)
2169 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2172 infostream<<"ClientEnvironment::addActiveObject(): "
2173 <<"no free ids available"<<std::endl;
2177 object->setId(new_id);
2179 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2181 infostream<<"ClientEnvironment::addActiveObject(): "
2182 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2186 infostream<<"ClientEnvironment::addActiveObject(): "
2187 <<"added (id="<<object->getId()<<")"<<std::endl;
2188 m_active_objects.insert(object->getId(), object);
2189 object->addToScene(m_smgr, m_texturesource, m_irr);
2190 { // Update lighting immediately
2194 v3s16 p = object->getLightPosition();
2195 MapNode n = m_map->getNode(p);
2196 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2198 catch(InvalidPositionException &e){
2199 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2201 object->updateLight(light);
2203 return object->getId();
2206 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2207 const std::string &init_data)
2209 ClientActiveObject* obj =
2210 ClientActiveObject::create(type, m_gamedef, this);
2213 infostream<<"ClientEnvironment::addActiveObject(): "
2214 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2221 obj->initialize(init_data);
2223 addActiveObject(obj);
2226 void ClientEnvironment::removeActiveObject(u16 id)
2228 infostream<<"ClientEnvironment::removeActiveObject(): "
2229 <<"id="<<id<<std::endl;
2230 ClientActiveObject* obj = getActiveObject(id);
2233 infostream<<"ClientEnvironment::removeActiveObject(): "
2234 <<"id="<<id<<" not found"<<std::endl;
2237 obj->removeFromScene();
2239 m_active_objects.remove(id);
2242 void ClientEnvironment::processActiveObjectMessage(u16 id,
2243 const std::string &data)
2245 ClientActiveObject* obj = getActiveObject(id);
2248 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2249 <<" got message for id="<<id<<", which doesn't exist."
2253 obj->processMessage(data);
2257 Callbacks for activeobjects
2260 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2262 LocalPlayer *lplayer = getLocalPlayer();
2266 if(lplayer->hp > damage)
2267 lplayer->hp -= damage;
2272 ClientEnvEvent event;
2273 event.type = CEE_PLAYER_DAMAGE;
2274 event.player_damage.amount = damage;
2275 event.player_damage.send_to_server = handle_hp;
2276 m_client_event_queue.push_back(event);
2280 Client likes to call these
2283 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2284 core::array<DistanceSortedActiveObject> &dest)
2286 for(core::map<u16, ClientActiveObject*>::Iterator
2287 i = m_active_objects.getIterator();
2288 i.atEnd()==false; i++)
2290 ClientActiveObject* obj = i.getNode()->getValue();
2292 f32 d = (obj->getPosition() - origin).getLength();
2297 DistanceSortedActiveObject dso(obj, d);
2299 dest.push_back(dso);
2303 ClientEnvEvent ClientEnvironment::getClientEvent()
2305 if(m_client_event_queue.size() == 0)
2307 ClientEnvEvent event;
2308 event.type = CEE_NONE;
2311 return m_client_event_queue.pop_front();
2314 #endif // #ifndef SERVER