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 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
213 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
214 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
215 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
222 void ActiveBlockList::update(core::list<v3s16> &active_positions,
224 core::map<v3s16, bool> &blocks_removed,
225 core::map<v3s16, bool> &blocks_added)
230 core::map<v3s16, bool> newlist;
231 for(core::list<v3s16>::Iterator i = active_positions.begin();
232 i != active_positions.end(); i++)
234 fillRadiusBlock(*i, radius, newlist);
238 Find out which blocks on the old list are not on the new list
240 // Go through old list
241 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
242 i.atEnd()==false; i++)
244 v3s16 p = i.getNode()->getKey();
245 // If not on new list, it's been removed
246 if(newlist.find(p) == NULL)
247 blocks_removed.insert(p, true);
251 Find out which blocks on the new list are not on the old list
253 // Go through new list
254 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
255 i.atEnd()==false; i++)
257 v3s16 p = i.getNode()->getKey();
258 // If not on old list, it's been added
259 if(m_list.find(p) == NULL)
260 blocks_added.insert(p, true);
267 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
268 i.atEnd()==false; i++)
270 v3s16 p = i.getNode()->getKey();
271 m_list.insert(p, true);
279 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
280 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
285 m_random_spawn_timer(3),
286 m_send_recommended_timer(0),
288 m_game_time_fraction_counter(0)
292 ServerEnvironment::~ServerEnvironment()
294 // Clear active block list.
295 // This makes the next one delete all active objects.
296 m_active_blocks.clear();
298 // Convert all objects to static and delete the active objects
299 deactivateFarObjects(true);
304 // Delete ActiveBlockModifiers
305 for(core::list<ABMWithState>::Iterator
306 i = m_abms.begin(); i != m_abms.end(); i++){
311 void ServerEnvironment::serializePlayers(const std::string &savedir)
313 std::string players_path = savedir + "/players";
314 fs::CreateDir(players_path);
316 core::map<Player*, bool> saved_players;
318 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
319 for(u32 i=0; i<player_files.size(); i++)
321 if(player_files[i].dir)
324 // Full path to this file
325 std::string path = players_path + "/" + player_files[i].name;
327 //infostream<<"Checking player file "<<path<<std::endl;
329 // Load player to see what is its name
330 ServerRemotePlayer testplayer(this);
332 // Open file and deserialize
333 std::ifstream is(path.c_str(), std::ios_base::binary);
334 if(is.good() == false)
336 infostream<<"Failed to read "<<path<<std::endl;
339 testplayer.deSerialize(is);
342 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
344 // Search for the player
345 std::string playername = testplayer.getName();
346 Player *player = getPlayer(playername.c_str());
349 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
353 //infostream<<"Found matching player, overwriting."<<std::endl;
355 // OK, found. Save player there.
357 // Open file and serialize
358 std::ofstream os(path.c_str(), std::ios_base::binary);
359 if(os.good() == false)
361 infostream<<"Failed to overwrite "<<path<<std::endl;
364 player->serialize(os);
365 saved_players.insert(player, true);
369 for(core::list<Player*>::Iterator i = m_players.begin();
370 i != m_players.end(); i++)
373 if(saved_players.find(player) != NULL)
375 /*infostream<<"Player "<<player->getName()
376 <<" was already saved."<<std::endl;*/
379 std::string playername = player->getName();
380 // Don't save unnamed player
383 //infostream<<"Not saving unnamed player."<<std::endl;
389 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
390 playername = "player";
391 std::string path = players_path + "/" + playername;
393 for(u32 i=0; i<1000; i++)
395 if(fs::PathExists(path) == false)
400 path = players_path + "/" + playername + itos(i);
404 infostream<<"Didn't find free file for player"<<std::endl;
409 /*infostream<<"Saving player "<<player->getName()<<" to "
411 // Open file and serialize
412 std::ofstream os(path.c_str(), std::ios_base::binary);
413 if(os.good() == false)
415 infostream<<"Failed to overwrite "<<path<<std::endl;
418 player->serialize(os);
419 saved_players.insert(player, true);
423 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
426 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
428 std::string players_path = savedir + "/players";
430 core::map<Player*, bool> saved_players;
432 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
433 for(u32 i=0; i<player_files.size(); i++)
435 if(player_files[i].dir)
438 // Full path to this file
439 std::string path = players_path + "/" + player_files[i].name;
441 infostream<<"Checking player file "<<path<<std::endl;
443 // Load player to see what is its name
444 ServerRemotePlayer testplayer(this);
446 // Open file and deserialize
447 std::ifstream is(path.c_str(), std::ios_base::binary);
448 if(is.good() == false)
450 infostream<<"Failed to read "<<path<<std::endl;
453 testplayer.deSerialize(is);
456 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
458 infostream<<"Not loading player with invalid name: "
459 <<testplayer.getName()<<std::endl;
462 infostream<<"Loaded test player with name "<<testplayer.getName()
465 // Search for the player
466 std::string playername = testplayer.getName();
467 Player *player = getPlayer(playername.c_str());
468 bool newplayer = false;
471 infostream<<"Is a new player"<<std::endl;
472 player = new ServerRemotePlayer(this);
476 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
480 infostream<<"Reading player "<<testplayer.getName()<<" from "
482 // Open file and deserialize
483 std::ifstream is(path.c_str(), std::ios_base::binary);
484 if(is.good() == false)
486 infostream<<"Failed to read "<<path<<std::endl;
489 srp->deSerialize(is);
490 srp->m_last_good_position = srp->getBasePosition();
491 srp->m_last_good_position_age = 0;
501 void ServerEnvironment::saveMeta(const std::string &savedir)
503 std::string path = savedir + "/env_meta.txt";
505 // Open file and serialize
506 std::ofstream os(path.c_str(), std::ios_base::binary);
507 if(os.good() == false)
509 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
511 throw SerializationError("Couldn't save env meta");
515 args.setU64("game_time", m_game_time);
516 args.setU64("time_of_day", getTimeOfDay());
521 void ServerEnvironment::loadMeta(const std::string &savedir)
523 std::string path = savedir + "/env_meta.txt";
525 // Open file and deserialize
526 std::ifstream is(path.c_str(), std::ios_base::binary);
527 if(is.good() == false)
529 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
531 throw SerializationError("Couldn't load env meta");
539 throw SerializationError
540 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
542 std::getline(is, line);
543 std::string trimmedline = trim(line);
544 if(trimmedline == "EnvArgsEnd")
546 args.parseConfigLine(line);
550 m_game_time = args.getU64("game_time");
551 }catch(SettingNotFoundException &e){
552 // Getting this is crucial, otherwise timestamps are useless
553 throw SerializationError("Couldn't load env meta game_time");
557 m_time_of_day = args.getU64("time_of_day");
558 }catch(SettingNotFoundException &e){
559 // This is not as important
560 m_time_of_day = 9000;
566 ActiveBlockModifier *abm;
568 std::set<content_t> required_neighbors;
574 ServerEnvironment *m_env;
575 std::map<content_t, std::list<ActiveABM> > m_aabms;
577 ABMHandler(core::list<ABMWithState> &abms,
578 float dtime_s, ServerEnvironment *env,
584 INodeDefManager *ndef = env->getGameDef()->ndef();
585 for(core::list<ABMWithState>::Iterator
586 i = abms.begin(); i != abms.end(); i++){
587 ActiveBlockModifier *abm = i->abm;
588 float trigger_interval = abm->getTriggerInterval();
589 if(trigger_interval < 0.001)
590 trigger_interval = 0.001;
593 if(i->timer < trigger_interval)
595 i->timer -= trigger_interval;
599 float intervals = dtime_s / trigger_interval;
600 float chance = abm->getTriggerChance();
603 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
607 std::set<std::string> required_neighbors_s
608 = abm->getRequiredNeighbors();
609 for(std::set<std::string>::iterator
610 i = required_neighbors_s.begin();
611 i != required_neighbors_s.end(); i++){
612 content_t c = ndef->getId(*i);
613 if(c == CONTENT_IGNORE)
615 aabm.required_neighbors.insert(c);
618 std::set<std::string> contents_s = abm->getTriggerContents();
619 for(std::set<std::string>::iterator
620 i = contents_s.begin(); i != contents_s.end(); i++){
621 content_t c = ndef->getId(*i);
622 if(c == CONTENT_IGNORE)
624 std::map<content_t, std::list<ActiveABM> >::iterator j;
626 if(j == m_aabms.end()){
627 std::list<ActiveABM> aabmlist;
628 m_aabms[c] = aabmlist;
631 j->second.push_back(aabm);
635 void apply(MapBlock *block)
640 ServerMap *map = &m_env->getServerMap();
643 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
644 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
645 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
647 MapNode n = block->getNodeNoEx(p0);
648 content_t c = n.getContent();
649 v3s16 p = p0 + block->getPosRelative();
651 std::map<content_t, std::list<ActiveABM> >::iterator j;
653 if(j == m_aabms.end())
656 for(std::list<ActiveABM>::iterator
657 i = j->second.begin(); i != j->second.end(); i++)
659 if(myrand() % i->chance != 0)
663 if(!i->required_neighbors.empty())
666 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
667 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
668 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
672 MapNode n = map->getNodeNoEx(p1);
673 content_t c = n.getContent();
674 std::set<content_t>::const_iterator k;
675 k = i->required_neighbors.find(c);
676 if(k != i->required_neighbors.end()){
680 // No required neighbor found
685 // Find out how many objects the block contains
686 u32 active_object_count = block->m_static_objects.m_active.size();
687 // Find out how many objects this and all the neighbors contain
688 u32 active_object_count_wider = 0;
689 for(s16 x=-1; x<=1; x++)
690 for(s16 y=-1; y<=1; y++)
691 for(s16 z=-1; z<=1; z++)
693 MapBlock *block2 = map->getBlockNoCreateNoEx(
694 block->getPos() + v3s16(x,y,z));
697 active_object_count_wider +=
698 block2->m_static_objects.m_active.size()
699 + block2->m_static_objects.m_stored.size();
702 // Call all the trigger variations
703 i->abm->trigger(m_env, p, n);
704 i->abm->trigger(m_env, p, n,
705 active_object_count, active_object_count_wider);
711 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
713 // Get time difference
715 u32 stamp = block->getTimestamp();
716 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
717 dtime_s = m_game_time - block->getTimestamp();
718 dtime_s += additional_dtime;
720 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
721 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
723 // Set current time as timestamp
724 block->setTimestampNoChangedFlag(m_game_time);
726 /*infostream<<"ServerEnvironment::activateBlock(): block is "
727 <<dtime_s<<" seconds old."<<std::endl;*/
729 // Activate stored objects
730 activateObjects(block);
733 bool changed = block->m_node_metadata->step((float)dtime_s);
737 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
738 event.p = block->getPos();
739 m_map->dispatchEvent(&event);
741 block->raiseModified(MOD_STATE_WRITE_NEEDED,
742 "node metadata modified in activateBlock");
745 /* Handle ActiveBlockModifiers */
746 ABMHandler abmhandler(m_abms, dtime_s, this, false);
747 abmhandler.apply(block);
750 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
752 m_abms.push_back(ABMWithState(abm));
755 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
757 std::set<u16> objects;
758 for(core::map<u16, ServerActiveObject*>::Iterator
759 i = m_active_objects.getIterator();
760 i.atEnd()==false; i++)
762 ServerActiveObject* obj = i.getNode()->getValue();
763 u16 id = i.getNode()->getKey();
764 v3f objectpos = obj->getBasePosition();
765 if(objectpos.getDistanceFrom(pos) > radius)
772 void ServerEnvironment::clearAllObjects()
774 infostream<<"ServerEnvironment::clearAllObjects(): "
775 <<"Removing all active objects"<<std::endl;
776 core::list<u16> objects_to_remove;
777 for(core::map<u16, ServerActiveObject*>::Iterator
778 i = m_active_objects.getIterator();
779 i.atEnd()==false; i++)
781 ServerActiveObject* obj = i.getNode()->getValue();
782 u16 id = i.getNode()->getKey();
783 v3f objectpos = obj->getBasePosition();
784 // Delete static object if block is loaded
785 if(obj->m_static_exists){
786 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
788 block->m_static_objects.remove(id);
789 block->raiseModified(MOD_STATE_WRITE_NEEDED,
791 obj->m_static_exists = false;
794 // If known by some client, don't delete immediately
795 if(obj->m_known_by_count > 0){
796 obj->m_pending_deactivation = true;
797 obj->m_removed = true;
801 // Tell the object about removal
802 obj->removingFromEnvironment();
803 // Deregister in scripting api
804 scriptapi_rm_object_reference(m_lua, obj);
806 // Delete active object
807 if(obj->environmentDeletes())
809 // Id to be removed from m_active_objects
810 objects_to_remove.push_back(id);
812 // Remove references from m_active_objects
813 for(core::list<u16>::Iterator i = objects_to_remove.begin();
814 i != objects_to_remove.end(); i++)
816 m_active_objects.remove(*i);
819 core::list<v3s16> loadable_blocks;
820 infostream<<"ServerEnvironment::clearAllObjects(): "
821 <<"Listing all loadable blocks"<<std::endl;
822 m_map->listAllLoadableBlocks(loadable_blocks);
823 infostream<<"ServerEnvironment::clearAllObjects(): "
824 <<"Done listing all loadable blocks: "
825 <<loadable_blocks.size()
826 <<", now clearing"<<std::endl;
827 u32 report_interval = loadable_blocks.size() / 10;
828 u32 num_blocks_checked = 0;
829 u32 num_blocks_cleared = 0;
830 u32 num_objs_cleared = 0;
831 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
832 i != loadable_blocks.end(); i++)
835 MapBlock *block = m_map->emergeBlock(p, false);
837 errorstream<<"ServerEnvironment::clearAllObjects(): "
838 <<"Failed to emerge block "<<PP(p)<<std::endl;
841 u32 num_stored = block->m_static_objects.m_stored.size();
842 u32 num_active = block->m_static_objects.m_active.size();
843 if(num_stored != 0 || num_active != 0){
844 block->m_static_objects.m_stored.clear();
845 block->m_static_objects.m_active.clear();
846 block->raiseModified(MOD_STATE_WRITE_NEEDED,
848 num_objs_cleared += num_stored + num_active;
849 num_blocks_cleared++;
851 num_blocks_checked++;
853 if(num_blocks_checked % report_interval == 0){
854 float percent = 100.0 * (float)num_blocks_checked /
855 loadable_blocks.size();
856 infostream<<"ServerEnvironment::clearAllObjects(): "
857 <<"Cleared "<<num_objs_cleared<<" objects"
858 <<" in "<<num_blocks_cleared<<" blocks ("
859 <<percent<<"%)"<<std::endl;
862 infostream<<"ServerEnvironment::clearAllObjects(): "
863 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
864 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
867 void ServerEnvironment::step(float dtime)
869 DSTACK(__FUNCTION_NAME);
871 //TimeTaker timer("ServerEnv step");
874 bool footprints = g_settings->getBool("footprints");
880 m_game_time_fraction_counter += dtime;
881 u32 inc_i = (u32)m_game_time_fraction_counter;
882 m_game_time += inc_i;
883 m_game_time_fraction_counter -= (float)inc_i;
890 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
891 for(core::list<Player*>::Iterator i = m_players.begin();
892 i != m_players.end(); i++)
896 // Ignore disconnected players
897 if(player->peer_id == 0)
900 v3f playerpos = player->getPosition();
903 player->move(dtime, *m_map, 100*BS);
906 Add footsteps to grass
910 // Get node that is at BS/4 under player
911 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
913 MapNode n = m_map->getNode(bottompos);
914 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
916 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
917 m_map->setNode(bottompos, n);
920 catch(InvalidPositionException &e)
928 Manage active block list
930 if(m_active_blocks_management_interval.step(dtime, 2.0))
932 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
934 Get player block positions
936 core::list<v3s16> players_blockpos;
937 for(core::list<Player*>::Iterator
938 i = m_players.begin();
939 i != m_players.end(); i++)
942 // Ignore disconnected players
943 if(player->peer_id == 0)
945 v3s16 blockpos = getNodeBlockPos(
946 floatToInt(player->getPosition(), BS));
947 players_blockpos.push_back(blockpos);
951 Update list of active blocks, collecting changes
953 const s16 active_block_range = g_settings->getS16("active_block_range");
954 core::map<v3s16, bool> blocks_removed;
955 core::map<v3s16, bool> blocks_added;
956 m_active_blocks.update(players_blockpos, active_block_range,
957 blocks_removed, blocks_added);
960 Handle removed blocks
963 // Convert active objects that are no more in active blocks to static
964 deactivateFarObjects(false);
966 for(core::map<v3s16, bool>::Iterator
967 i = blocks_removed.getIterator();
968 i.atEnd()==false; i++)
970 v3s16 p = i.getNode()->getKey();
972 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
973 <<") became inactive"<<std::endl;*/
975 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
979 // Set current time as timestamp (and let it set ChangedFlag)
980 block->setTimestamp(m_game_time);
987 for(core::map<v3s16, bool>::Iterator
988 i = blocks_added.getIterator();
989 i.atEnd()==false; i++)
991 v3s16 p = i.getNode()->getKey();
993 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
994 <<") became active"<<std::endl;*/
996 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
998 // Block needs to be fetched first
999 m_emerger->queueBlockEmerge(p, false);
1000 m_active_blocks.m_list.remove(p);
1004 activateBlock(block);
1009 Mess around in active blocks
1011 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1013 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1017 for(core::map<v3s16, bool>::Iterator
1018 i = m_active_blocks.m_list.getIterator();
1019 i.atEnd()==false; i++)
1021 v3s16 p = i.getNode()->getKey();
1023 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1024 <<") being handled"<<std::endl;*/
1026 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1030 // Reset block usage timer
1031 block->resetUsageTimer();
1033 // Set current time as timestamp
1034 block->setTimestampNoChangedFlag(m_game_time);
1035 // If time has changed much from the one on disk,
1036 // set block to be saved when it is unloaded
1037 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1038 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1039 "Timestamp older than 60s (step)");
1041 // Run node metadata
1042 bool changed = block->m_node_metadata->step(dtime);
1046 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1048 m_map->dispatchEvent(&event);
1050 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1051 "node metadata modified in step");
1056 const float abm_interval = 1.0;
1057 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1059 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1060 TimeTaker timer("modify in active blocks");
1062 // Initialize handling of ActiveBlockModifiers
1063 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1065 for(core::map<v3s16, bool>::Iterator
1066 i = m_active_blocks.m_list.getIterator();
1067 i.atEnd()==false; i++)
1069 v3s16 p = i.getNode()->getKey();
1071 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1072 <<") being handled"<<std::endl;*/
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1078 // Set current time as timestamp
1079 block->setTimestampNoChangedFlag(m_game_time);
1081 /* Handle ActiveBlockModifiers */
1082 abmhandler.apply(block);
1085 u32 time_ms = timer.stop(true);
1086 u32 max_time_ms = 200;
1087 if(time_ms > max_time_ms){
1088 infostream<<"WARNING: active block modifiers took "
1089 <<time_ms<<"ms (longer than "
1090 <<max_time_ms<<"ms)"<<std::endl;
1095 Step script environment (run global on_step())
1097 scriptapi_environment_step(m_lua, dtime);
1103 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1104 //TimeTaker timer("Step active objects");
1106 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1108 // This helps the objects to send data at the same time
1109 bool send_recommended = false;
1110 m_send_recommended_timer += dtime;
1111 if(m_send_recommended_timer > getSendRecommendedInterval())
1113 m_send_recommended_timer -= getSendRecommendedInterval();
1114 send_recommended = true;
1117 for(core::map<u16, ServerActiveObject*>::Iterator
1118 i = m_active_objects.getIterator();
1119 i.atEnd()==false; i++)
1121 ServerActiveObject* obj = i.getNode()->getValue();
1122 // Remove non-peaceful mobs on peaceful mode
1123 if(g_settings->getBool("only_peaceful_mobs")){
1124 if(!obj->isPeaceful())
1125 obj->m_removed = true;
1127 // Don't step if is to be removed or stored statically
1128 if(obj->m_removed || obj->m_pending_deactivation)
1131 obj->step(dtime, send_recommended);
1132 // Read messages from object
1133 while(obj->m_messages_out.size() > 0)
1135 m_active_object_messages.push_back(
1136 obj->m_messages_out.pop_front());
1142 Manage active objects
1144 if(m_object_management_interval.step(dtime, 0.5))
1146 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1148 Remove objects that satisfy (m_removed && m_known_by_count==0)
1150 removeRemovedObjects();
1154 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1156 core::map<u16, ServerActiveObject*>::Node *n;
1157 n = m_active_objects.find(id);
1160 return n->getValue();
1163 bool isFreeServerActiveObjectId(u16 id,
1164 core::map<u16, ServerActiveObject*> &objects)
1169 for(core::map<u16, ServerActiveObject*>::Iterator
1170 i = objects.getIterator();
1171 i.atEnd()==false; i++)
1173 if(i.getNode()->getKey() == id)
1179 u16 getFreeServerActiveObjectId(
1180 core::map<u16, ServerActiveObject*> &objects)
1185 if(isFreeServerActiveObjectId(new_id, objects))
1195 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1198 u16 id = addActiveObjectRaw(object, true);
1202 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1206 v3f objectpos = obj->getBasePosition();
1208 // The block in which the object resides in
1209 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1212 Update the static data
1215 // Create new static object
1216 std::string staticdata = obj->getStaticData();
1217 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1218 // Add to the block where the object is located in
1219 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1220 // Get or generate the block
1221 MapBlock *block = m_map->emergeBlock(blockpos);
1223 bool succeeded = false;
1227 block->m_static_objects.insert(0, s_obj);
1228 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1229 "addActiveObjectAsStatic");
1233 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1234 <<"Could not find or generate "
1235 <<"a block for storing static object"<<std::endl;
1239 if(obj->environmentDeletes())
1246 Finds out what new objects have been added to
1247 inside a radius around a position
1249 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1250 core::map<u16, bool> ¤t_objects,
1251 core::map<u16, bool> &added_objects)
1253 v3f pos_f = intToFloat(pos, BS);
1254 f32 radius_f = radius * BS;
1256 Go through the object list,
1257 - discard m_removed objects,
1258 - discard objects that are too far away,
1259 - discard objects that are found in current_objects.
1260 - add remaining objects to added_objects
1262 for(core::map<u16, ServerActiveObject*>::Iterator
1263 i = m_active_objects.getIterator();
1264 i.atEnd()==false; i++)
1266 u16 id = i.getNode()->getKey();
1268 ServerActiveObject *object = i.getNode()->getValue();
1271 // Discard if removed
1272 if(object->m_removed)
1274 if(object->unlimitedTransferDistance() == false){
1275 // Discard if too far
1276 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1277 if(distance_f > radius_f)
1280 // Discard if already on current_objects
1281 core::map<u16, bool>::Node *n;
1282 n = current_objects.find(id);
1285 // Add to added_objects
1286 added_objects.insert(id, false);
1291 Finds out what objects have been removed from
1292 inside a radius around a position
1294 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1295 core::map<u16, bool> ¤t_objects,
1296 core::map<u16, bool> &removed_objects)
1298 v3f pos_f = intToFloat(pos, BS);
1299 f32 radius_f = radius * BS;
1301 Go through current_objects; object is removed if:
1302 - object is not found in m_active_objects (this is actually an
1303 error condition; objects should be set m_removed=true and removed
1304 only after all clients have been informed about removal), or
1305 - object has m_removed=true, or
1306 - object is too far away
1308 for(core::map<u16, bool>::Iterator
1309 i = current_objects.getIterator();
1310 i.atEnd()==false; i++)
1312 u16 id = i.getNode()->getKey();
1313 ServerActiveObject *object = getActiveObject(id);
1316 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1317 <<" object in current_objects is NULL"<<std::endl;
1318 removed_objects.insert(id, false);
1322 if(object->m_removed)
1324 removed_objects.insert(id, false);
1328 // If transfer distance is unlimited, don't remove
1329 if(object->unlimitedTransferDistance())
1332 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1334 if(distance_f >= radius_f)
1336 removed_objects.insert(id, false);
1344 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1346 if(m_active_object_messages.size() == 0)
1347 return ActiveObjectMessage(0);
1349 return m_active_object_messages.pop_front();
1353 ************ Private methods *************
1356 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1360 if(object->getId() == 0){
1361 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1364 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1365 <<"no free ids available"<<std::endl;
1366 if(object->environmentDeletes())
1370 object->setId(new_id);
1373 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1374 <<"supplied with id "<<object->getId()<<std::endl;
1376 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1378 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1379 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1380 if(object->environmentDeletes())
1384 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1385 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1387 m_active_objects.insert(object->getId(), object);
1389 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1390 <<"Added id="<<object->getId()<<"; there are now "
1391 <<m_active_objects.size()<<" active objects."
1394 // Register reference in scripting api (must be done before post-init)
1395 scriptapi_add_object_reference(m_lua, object);
1396 // Post-initialize object
1397 object->addedToEnvironment();
1399 // Add static data to block
1400 if(object->isStaticAllowed())
1402 // Add static object to active static list of the block
1403 v3f objectpos = object->getBasePosition();
1404 std::string staticdata = object->getStaticData();
1405 StaticObject s_obj(object->getType(), objectpos, staticdata);
1406 // Add to the block where the object is located in
1407 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1408 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1411 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1412 object->m_static_exists = true;
1413 object->m_static_block = blockpos;
1416 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1417 "addActiveObjectRaw");
1420 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1421 <<"could not find block for storing id="<<object->getId()
1422 <<" statically"<<std::endl;
1426 return object->getId();
1430 Remove objects that satisfy (m_removed && m_known_by_count==0)
1432 void ServerEnvironment::removeRemovedObjects()
1434 core::list<u16> objects_to_remove;
1435 for(core::map<u16, ServerActiveObject*>::Iterator
1436 i = m_active_objects.getIterator();
1437 i.atEnd()==false; i++)
1439 u16 id = i.getNode()->getKey();
1440 ServerActiveObject* obj = i.getNode()->getValue();
1441 // This shouldn't happen but check it
1444 infostream<<"NULL object found in ServerEnvironment"
1445 <<" while finding removed objects. id="<<id<<std::endl;
1446 // Id to be removed from m_active_objects
1447 objects_to_remove.push_back(id);
1452 We will delete objects that are marked as removed or thatare
1453 waiting for deletion after deactivation
1455 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1459 Delete static data from block if is marked as removed
1461 if(obj->m_static_exists && obj->m_removed)
1463 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1466 block->m_static_objects.remove(id);
1467 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1468 "removeRemovedObjects");
1469 obj->m_static_exists = false;
1473 // If m_known_by_count > 0, don't actually remove.
1474 if(obj->m_known_by_count > 0)
1477 // Tell the object about removal
1478 obj->removingFromEnvironment();
1479 // Deregister in scripting api
1480 scriptapi_rm_object_reference(m_lua, obj);
1483 if(obj->environmentDeletes())
1485 // Id to be removed from m_active_objects
1486 objects_to_remove.push_back(id);
1488 // Remove references from m_active_objects
1489 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1490 i != objects_to_remove.end(); i++)
1492 m_active_objects.remove(*i);
1496 static void print_hexdump(std::ostream &o, const std::string &data)
1498 const int linelength = 16;
1499 for(int l=0; ; l++){
1500 int i0 = linelength * l;
1501 bool at_end = false;
1502 int thislinelength = linelength;
1503 if(i0 + thislinelength > (int)data.size()){
1504 thislinelength = data.size() - i0;
1507 for(int di=0; di<linelength; di++){
1510 if(di<thislinelength)
1511 snprintf(buf, 4, "%.2x ", data[i]);
1513 snprintf(buf, 4, " ");
1517 for(int di=0; di<thislinelength; di++){
1531 Convert stored objects from blocks near the players to active.
1533 void ServerEnvironment::activateObjects(MapBlock *block)
1537 // Ignore if no stored objects (to not set changed flag)
1538 if(block->m_static_objects.m_stored.size() == 0)
1540 verbosestream<<"ServerEnvironment::activateObjects(): "
1541 <<"activating objects of block "<<PP(block->getPos())
1542 <<" ("<<block->m_static_objects.m_stored.size()
1543 <<" objects)"<<std::endl;
1544 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1546 errorstream<<"suspiciously large amount of objects detected: "
1547 <<block->m_static_objects.m_stored.size()<<" in "
1548 <<PP(block->getPos())
1549 <<"; removing all of them."<<std::endl;
1550 // Clear stored list
1551 block->m_static_objects.m_stored.clear();
1552 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1553 "stored list cleared in activateObjects due to "
1554 "large amount of objects");
1557 // A list for objects that couldn't be converted to static for some
1558 // reason. They will be stored back.
1559 core::list<StaticObject> new_stored;
1560 // Loop through stored static objects
1561 for(core::list<StaticObject>::Iterator
1562 i = block->m_static_objects.m_stored.begin();
1563 i != block->m_static_objects.m_stored.end(); i++)
1565 /*infostream<<"Server: Creating an active object from "
1566 <<"static data"<<std::endl;*/
1567 StaticObject &s_obj = *i;
1568 // Create an active object from the data
1569 ServerActiveObject *obj = ServerActiveObject::create
1570 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1571 // If couldn't create object, store static data back.
1574 errorstream<<"ServerEnvironment::activateObjects(): "
1575 <<"failed to create active object from static object "
1576 <<"in block "<<PP(s_obj.pos/BS)
1577 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1578 print_hexdump(verbosestream, s_obj.data);
1580 new_stored.push_back(s_obj);
1583 verbosestream<<"ServerEnvironment::activateObjects(): "
1584 <<"activated static object pos="<<PP(s_obj.pos/BS)
1585 <<" type="<<(int)s_obj.type<<std::endl;
1586 // This will also add the object to the active static list
1587 addActiveObjectRaw(obj, false);
1589 // Clear stored list
1590 block->m_static_objects.m_stored.clear();
1591 // Add leftover failed stuff to stored list
1592 for(core::list<StaticObject>::Iterator
1593 i = new_stored.begin();
1594 i != new_stored.end(); i++)
1596 StaticObject &s_obj = *i;
1597 block->m_static_objects.m_stored.push_back(s_obj);
1600 Note: Block hasn't really been modified here.
1601 The objects have just been activated and moved from the stored
1602 static list to the active static list.
1603 As such, the block is essentially the same.
1604 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1605 Otherwise there would be a huge amount of unnecessary I/O.
1610 Convert objects that are not standing inside active blocks to static.
1612 If m_known_by_count != 0, active object is not deleted, but static
1613 data is still updated.
1615 If force_delete is set, active object is deleted nevertheless. It
1616 shall only be set so in the destructor of the environment.
1618 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1620 core::list<u16> objects_to_remove;
1621 for(core::map<u16, ServerActiveObject*>::Iterator
1622 i = m_active_objects.getIterator();
1623 i.atEnd()==false; i++)
1625 ServerActiveObject* obj = i.getNode()->getValue();
1628 // Do not deactivate if static data creation not allowed
1629 if(!force_delete && !obj->isStaticAllowed())
1632 // If pending deactivation, let removeRemovedObjects() do it
1633 if(!force_delete && obj->m_pending_deactivation)
1636 u16 id = i.getNode()->getKey();
1637 v3f objectpos = obj->getBasePosition();
1639 // The block in which the object resides in
1640 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1642 // If block is active, don't remove
1643 if(!force_delete && m_active_blocks.contains(blockpos_o))
1646 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1647 <<"deactivating object id="<<id<<" on inactive block "
1648 <<PP(blockpos_o)<<std::endl;
1650 // If known by some client, don't immediately delete.
1651 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1654 Update the static data
1657 if(obj->isStaticAllowed())
1659 // Create new static object
1660 std::string staticdata_new = obj->getStaticData();
1661 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1663 bool stays_in_same_block = false;
1664 bool data_changed = true;
1666 if(obj->m_static_exists){
1667 if(obj->m_static_block == blockpos_o)
1668 stays_in_same_block = true;
1670 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1672 core::map<u16, StaticObject>::Node *n =
1673 block->m_static_objects.m_active.find(id);
1675 StaticObject static_old = n->getValue();
1677 float save_movem = obj->getMinimumSavedMovement();
1679 if(static_old.data == staticdata_new &&
1680 (static_old.pos - objectpos).getLength() < save_movem)
1681 data_changed = false;
1683 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1684 <<"id="<<id<<" m_static_exists=true but "
1685 <<"static data doesn't actually exist in "
1686 <<PP(obj->m_static_block)<<std::endl;
1690 bool shall_be_written = (!stays_in_same_block || data_changed);
1692 // Delete old static object
1693 if(obj->m_static_exists)
1695 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1698 block->m_static_objects.remove(id);
1699 obj->m_static_exists = false;
1700 // Only mark block as modified if data changed considerably
1701 if(shall_be_written)
1702 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1703 "deactivateFarObjects: Static data "
1704 "changed considerably");
1708 // Add to the block where the object is located in
1709 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1710 // Get or generate the block
1711 MapBlock *block = m_map->emergeBlock(blockpos);
1715 if(block->m_static_objects.m_stored.size() >= 49){
1716 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1717 <<" statically but block "<<PP(blockpos)
1718 <<" already contains "
1719 <<block->m_static_objects.m_stored.size()
1720 <<" (over 49) objects."
1721 <<" Forcing delete."<<std::endl;
1722 force_delete = true;
1724 u16 new_id = pending_delete ? id : 0;
1725 block->m_static_objects.insert(new_id, s_obj);
1727 // Only mark block as modified if data changed considerably
1728 if(shall_be_written)
1729 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1730 "deactivateFarObjects: Static data "
1731 "changed considerably");
1733 obj->m_static_exists = true;
1734 obj->m_static_block = block->getPos();
1739 errorstream<<"ServerEnv: Could not find or generate "
1740 <<"a block for storing id="<<obj->getId()
1741 <<" statically"<<std::endl;
1748 If known by some client, set pending deactivation.
1749 Otherwise delete it immediately.
1752 if(pending_delete && !force_delete)
1754 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1755 <<"object id="<<id<<" is known by clients"
1756 <<"; not deleting yet"<<std::endl;
1758 obj->m_pending_deactivation = true;
1762 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1763 <<"object id="<<id<<" is not known by clients"
1764 <<"; deleting"<<std::endl;
1766 // Tell the object about removal
1767 obj->removingFromEnvironment();
1768 // Deregister in scripting api
1769 scriptapi_rm_object_reference(m_lua, obj);
1771 // Delete active object
1772 if(obj->environmentDeletes())
1774 // Id to be removed from m_active_objects
1775 objects_to_remove.push_back(id);
1778 // Remove references from m_active_objects
1779 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1780 i != objects_to_remove.end(); i++)
1782 m_active_objects.remove(*i);
1793 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1794 ITextureSource *texturesource, IGameDef *gamedef,
1795 IrrlichtDevice *irr):
1798 m_texturesource(texturesource),
1804 ClientEnvironment::~ClientEnvironment()
1806 // delete active objects
1807 for(core::map<u16, ClientActiveObject*>::Iterator
1808 i = m_active_objects.getIterator();
1809 i.atEnd()==false; i++)
1811 delete i.getNode()->getValue();
1818 void ClientEnvironment::addPlayer(Player *player)
1820 DSTACK(__FUNCTION_NAME);
1822 It is a failure if player is local and there already is a local
1825 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1827 Environment::addPlayer(player);
1830 LocalPlayer * ClientEnvironment::getLocalPlayer()
1832 for(core::list<Player*>::Iterator i = m_players.begin();
1833 i != m_players.end(); i++)
1835 Player *player = *i;
1836 if(player->isLocal())
1837 return (LocalPlayer*)player;
1842 void ClientEnvironment::step(float dtime)
1844 DSTACK(__FUNCTION_NAME);
1846 // Get some settings
1847 bool free_move = g_settings->getBool("free_move");
1848 bool footprints = g_settings->getBool("footprints");
1851 LocalPlayer *lplayer = getLocalPlayer();
1853 // collision info queue
1854 core::list<CollisionInfo> player_collisions;
1857 Get the speed the player is going
1859 bool is_climbing = lplayer->is_climbing;
1861 f32 player_speed = lplayer->getSpeed().getLength();
1864 Maximum position increment
1866 //f32 position_max_increment = 0.05*BS;
1867 f32 position_max_increment = 0.1*BS;
1869 // Maximum time increment (for collision detection etc)
1870 // time = distance / speed
1871 f32 dtime_max_increment = 1;
1872 if(player_speed > 0.001)
1873 dtime_max_increment = position_max_increment / player_speed;
1875 // Maximum time increment is 10ms or lower
1876 if(dtime_max_increment > 0.01)
1877 dtime_max_increment = 0.01;
1879 // Don't allow overly huge dtime
1883 f32 dtime_downcount = dtime;
1886 Stuff that has a maximum time increment
1895 if(dtime_downcount > dtime_max_increment)
1897 dtime_part = dtime_max_increment;
1898 dtime_downcount -= dtime_part;
1902 dtime_part = dtime_downcount;
1904 Setting this to 0 (no -=dtime_part) disables an infinite loop
1905 when dtime_part is so small that dtime_downcount -= dtime_part
1908 dtime_downcount = 0;
1916 v3f lplayerpos = lplayer->getPosition();
1919 if(free_move == false && is_climbing == false)
1922 v3f speed = lplayer->getSpeed();
1923 if(lplayer->swimming_up == false)
1924 speed.Y -= 9.81 * BS * dtime_part * 2;
1927 if(lplayer->in_water_stable || lplayer->in_water)
1929 f32 max_down = 2.0*BS;
1930 if(speed.Y < -max_down) speed.Y = -max_down;
1933 if(speed.getLength() > max)
1935 speed = speed / speed.getLength() * max;
1939 lplayer->setSpeed(speed);
1944 This also does collision detection.
1946 lplayer->move(dtime_part, *m_map, position_max_increment,
1947 &player_collisions);
1950 while(dtime_downcount > 0.001);
1952 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1954 for(core::list<CollisionInfo>::Iterator
1955 i = player_collisions.begin();
1956 i != player_collisions.end(); i++)
1958 CollisionInfo &info = *i;
1959 if(info.t == COLLISION_FALL)
1961 //f32 tolerance = BS*10; // 2 without damage
1962 f32 tolerance = BS*12; // 3 without damage
1964 if(info.speed > tolerance)
1966 f32 damage_f = (info.speed - tolerance)/BS*factor;
1967 u16 damage = (u16)(damage_f+0.5);
1968 if(lplayer->hp > damage)
1969 lplayer->hp -= damage;
1973 ClientEnvEvent event;
1974 event.type = CEE_PLAYER_DAMAGE;
1975 event.player_damage.amount = damage;
1976 event.player_damage.send_to_server = true;
1977 m_client_event_queue.push_back(event);
1983 A quick draft of lava damage
1985 if(m_lava_hurt_interval.step(dtime, 1.0))
1987 v3f pf = lplayer->getPosition();
1989 // Feet, middle and head
1990 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1991 MapNode n1 = m_map->getNodeNoEx(p1);
1992 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1993 MapNode n2 = m_map->getNodeNoEx(p2);
1994 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1995 MapNode n3 = m_map->getNodeNoEx(p2);
1997 u32 damage_per_second = 0;
1998 damage_per_second = MYMAX(damage_per_second,
1999 m_gamedef->ndef()->get(n1).damage_per_second);
2000 damage_per_second = MYMAX(damage_per_second,
2001 m_gamedef->ndef()->get(n2).damage_per_second);
2002 damage_per_second = MYMAX(damage_per_second,
2003 m_gamedef->ndef()->get(n3).damage_per_second);
2005 if(damage_per_second != 0)
2007 ClientEnvEvent event;
2008 event.type = CEE_PLAYER_DAMAGE;
2009 event.player_damage.amount = damage_per_second;
2010 event.player_damage.send_to_server = true;
2011 m_client_event_queue.push_back(event);
2016 Stuff that can be done in an arbitarily large dtime
2018 for(core::list<Player*>::Iterator i = m_players.begin();
2019 i != m_players.end(); i++)
2021 Player *player = *i;
2022 v3f playerpos = player->getPosition();
2025 Handle non-local players
2027 if(player->isLocal() == false)
2030 player->move(dtime, *m_map, 100*BS);
2034 // Update lighting on all players on client
2035 u8 light = LIGHT_MAX;
2038 v3s16 p = player->getLightPosition();
2039 MapNode n = m_map->getNode(p);
2040 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2042 catch(InvalidPositionException &e) {}
2043 player->updateLight(light);
2046 Add footsteps to grass
2050 // Get node that is at BS/4 under player
2051 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2053 MapNode n = m_map->getNode(bottompos);
2054 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2056 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2057 m_map->setNode(bottompos, n);
2058 // Update mesh on client
2059 if(m_map->mapType() == MAPTYPE_CLIENT)
2061 v3s16 p_blocks = getNodeBlockPos(bottompos);
2062 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2063 //b->updateMesh(getDayNightRatio());
2064 b->setMeshExpired(true);
2068 catch(InvalidPositionException &e)
2075 Step active objects and update lighting of them
2078 for(core::map<u16, ClientActiveObject*>::Iterator
2079 i = m_active_objects.getIterator();
2080 i.atEnd()==false; i++)
2082 ClientActiveObject* obj = i.getNode()->getValue();
2084 obj->step(dtime, this);
2086 if(m_active_object_light_update_interval.step(dtime, 0.21))
2089 //u8 light = LIGHT_MAX;
2093 v3s16 p = obj->getLightPosition();
2094 MapNode n = m_map->getNode(p);
2095 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2097 catch(InvalidPositionException &e) {}
2098 obj->updateLight(light);
2103 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2105 m_map->updateMeshes(blockpos, getDayNightRatio());
2108 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2110 m_map->expireMeshes(only_daynight_diffed);
2113 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2115 core::map<u16, ClientActiveObject*>::Node *n;
2116 n = m_active_objects.find(id);
2119 return n->getValue();
2122 bool isFreeClientActiveObjectId(u16 id,
2123 core::map<u16, ClientActiveObject*> &objects)
2128 for(core::map<u16, ClientActiveObject*>::Iterator
2129 i = objects.getIterator();
2130 i.atEnd()==false; i++)
2132 if(i.getNode()->getKey() == id)
2138 u16 getFreeClientActiveObjectId(
2139 core::map<u16, ClientActiveObject*> &objects)
2144 if(isFreeClientActiveObjectId(new_id, objects))
2154 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2157 if(object->getId() == 0)
2159 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2162 infostream<<"ClientEnvironment::addActiveObject(): "
2163 <<"no free ids available"<<std::endl;
2167 object->setId(new_id);
2169 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2171 infostream<<"ClientEnvironment::addActiveObject(): "
2172 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2176 infostream<<"ClientEnvironment::addActiveObject(): "
2177 <<"added (id="<<object->getId()<<")"<<std::endl;
2178 m_active_objects.insert(object->getId(), object);
2179 object->addToScene(m_smgr, m_texturesource, m_irr);
2180 { // Update lighting immediately
2184 v3s16 p = object->getLightPosition();
2185 MapNode n = m_map->getNode(p);
2186 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2188 catch(InvalidPositionException &e) {}
2189 object->updateLight(light);
2191 return object->getId();
2194 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2195 const std::string &init_data)
2197 ClientActiveObject* obj =
2198 ClientActiveObject::create(type, m_gamedef, this);
2201 infostream<<"ClientEnvironment::addActiveObject(): "
2202 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2209 obj->initialize(init_data);
2211 addActiveObject(obj);
2214 void ClientEnvironment::removeActiveObject(u16 id)
2216 infostream<<"ClientEnvironment::removeActiveObject(): "
2217 <<"id="<<id<<std::endl;
2218 ClientActiveObject* obj = getActiveObject(id);
2221 infostream<<"ClientEnvironment::removeActiveObject(): "
2222 <<"id="<<id<<" not found"<<std::endl;
2225 obj->removeFromScene();
2227 m_active_objects.remove(id);
2230 void ClientEnvironment::processActiveObjectMessage(u16 id,
2231 const std::string &data)
2233 ClientActiveObject* obj = getActiveObject(id);
2236 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2237 <<" got message for id="<<id<<", which doesn't exist."
2241 obj->processMessage(data);
2245 Callbacks for activeobjects
2248 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2250 LocalPlayer *lplayer = getLocalPlayer();
2254 if(lplayer->hp > damage)
2255 lplayer->hp -= damage;
2260 ClientEnvEvent event;
2261 event.type = CEE_PLAYER_DAMAGE;
2262 event.player_damage.amount = damage;
2263 event.player_damage.send_to_server = handle_hp;
2264 m_client_event_queue.push_back(event);
2268 Client likes to call these
2271 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2272 core::array<DistanceSortedActiveObject> &dest)
2274 for(core::map<u16, ClientActiveObject*>::Iterator
2275 i = m_active_objects.getIterator();
2276 i.atEnd()==false; i++)
2278 ClientActiveObject* obj = i.getNode()->getValue();
2280 f32 d = (obj->getPosition() - origin).getLength();
2285 DistanceSortedActiveObject dso(obj, d);
2287 dest.push_back(dso);
2291 ClientEnvEvent ClientEnvironment::getClientEvent()
2293 if(m_client_event_queue.size() == 0)
2295 ClientEnvEvent event;
2296 event.type = CEE_NONE;
2299 return m_client_event_queue.pop_front();
2302 #endif // #ifndef SERVER