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 void ServerEnvironment::clearAllObjects()
757 infostream<<"ServerEnvironment::clearAllObjects(): "
758 <<"Removing all active objects"<<std::endl;
759 core::list<u16> objects_to_remove;
760 for(core::map<u16, ServerActiveObject*>::Iterator
761 i = m_active_objects.getIterator();
762 i.atEnd()==false; i++)
764 ServerActiveObject* obj = i.getNode()->getValue();
765 u16 id = i.getNode()->getKey();
766 v3f objectpos = obj->getBasePosition();
767 // Delete static object if block is loaded
768 if(obj->m_static_exists){
769 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
771 block->m_static_objects.remove(id);
772 block->raiseModified(MOD_STATE_WRITE_NEEDED,
774 obj->m_static_exists = false;
777 // If known by some client, don't delete immediately
778 if(obj->m_known_by_count > 0){
779 obj->m_pending_deactivation = true;
780 obj->m_removed = true;
784 // Tell the object about removal
785 obj->removingFromEnvironment();
786 // Deregister in scripting api
787 scriptapi_rm_object_reference(m_lua, obj);
789 // Delete active object
790 if(obj->environmentDeletes())
792 // Id to be removed from m_active_objects
793 objects_to_remove.push_back(id);
795 // Remove references from m_active_objects
796 for(core::list<u16>::Iterator i = objects_to_remove.begin();
797 i != objects_to_remove.end(); i++)
799 m_active_objects.remove(*i);
802 core::list<v3s16> loadable_blocks;
803 infostream<<"ServerEnvironment::clearAllObjects(): "
804 <<"Listing all loadable blocks"<<std::endl;
805 m_map->listAllLoadableBlocks(loadable_blocks);
806 infostream<<"ServerEnvironment::clearAllObjects(): "
807 <<"Done listing all loadable blocks: "
808 <<loadable_blocks.size()
809 <<", now clearing"<<std::endl;
810 u32 report_interval = loadable_blocks.size() / 10;
811 u32 num_blocks_checked = 0;
812 u32 num_blocks_cleared = 0;
813 u32 num_objs_cleared = 0;
814 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
815 i != loadable_blocks.end(); i++)
818 MapBlock *block = m_map->emergeBlock(p, false);
820 errorstream<<"ServerEnvironment::clearAllObjects(): "
821 <<"Failed to emerge block "<<PP(p)<<std::endl;
824 u32 num_stored = block->m_static_objects.m_stored.size();
825 u32 num_active = block->m_static_objects.m_active.size();
826 if(num_stored != 0 || num_active != 0){
827 block->m_static_objects.m_stored.clear();
828 block->m_static_objects.m_active.clear();
829 block->raiseModified(MOD_STATE_WRITE_NEEDED,
831 num_objs_cleared += num_stored + num_active;
832 num_blocks_cleared++;
834 num_blocks_checked++;
836 if(num_blocks_checked % report_interval == 0){
837 float percent = 100.0 * (float)num_blocks_checked /
838 loadable_blocks.size();
839 infostream<<"ServerEnvironment::clearAllObjects(): "
840 <<"Cleared "<<num_objs_cleared<<" objects"
841 <<" in "<<num_blocks_cleared<<" blocks ("
842 <<percent<<"%)"<<std::endl;
845 infostream<<"ServerEnvironment::clearAllObjects(): "
846 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
847 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
850 void ServerEnvironment::step(float dtime)
852 DSTACK(__FUNCTION_NAME);
854 //TimeTaker timer("ServerEnv step");
857 bool footprints = g_settings->getBool("footprints");
863 m_game_time_fraction_counter += dtime;
864 u32 inc_i = (u32)m_game_time_fraction_counter;
865 m_game_time += inc_i;
866 m_game_time_fraction_counter -= (float)inc_i;
873 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
874 for(core::list<Player*>::Iterator i = m_players.begin();
875 i != m_players.end(); i++)
879 // Ignore disconnected players
880 if(player->peer_id == 0)
883 v3f playerpos = player->getPosition();
886 player->move(dtime, *m_map, 100*BS);
889 Add footsteps to grass
893 // Get node that is at BS/4 under player
894 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
896 MapNode n = m_map->getNode(bottompos);
897 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
899 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
900 m_map->setNode(bottompos, n);
903 catch(InvalidPositionException &e)
911 Manage active block list
913 if(m_active_blocks_management_interval.step(dtime, 2.0))
915 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
917 Get player block positions
919 core::list<v3s16> players_blockpos;
920 for(core::list<Player*>::Iterator
921 i = m_players.begin();
922 i != m_players.end(); i++)
925 // Ignore disconnected players
926 if(player->peer_id == 0)
928 v3s16 blockpos = getNodeBlockPos(
929 floatToInt(player->getPosition(), BS));
930 players_blockpos.push_back(blockpos);
934 Update list of active blocks, collecting changes
936 const s16 active_block_range = g_settings->getS16("active_block_range");
937 core::map<v3s16, bool> blocks_removed;
938 core::map<v3s16, bool> blocks_added;
939 m_active_blocks.update(players_blockpos, active_block_range,
940 blocks_removed, blocks_added);
943 Handle removed blocks
946 // Convert active objects that are no more in active blocks to static
947 deactivateFarObjects(false);
949 for(core::map<v3s16, bool>::Iterator
950 i = blocks_removed.getIterator();
951 i.atEnd()==false; i++)
953 v3s16 p = i.getNode()->getKey();
955 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
956 <<") became inactive"<<std::endl;*/
958 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
962 // Set current time as timestamp (and let it set ChangedFlag)
963 block->setTimestamp(m_game_time);
970 for(core::map<v3s16, bool>::Iterator
971 i = blocks_added.getIterator();
972 i.atEnd()==false; i++)
974 v3s16 p = i.getNode()->getKey();
976 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
977 <<") became active"<<std::endl;*/
979 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
981 // Block needs to be fetched first
982 m_emerger->queueBlockEmerge(p, false);
983 m_active_blocks.m_list.remove(p);
987 activateBlock(block);
992 Mess around in active blocks
994 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
996 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1000 for(core::map<v3s16, bool>::Iterator
1001 i = m_active_blocks.m_list.getIterator();
1002 i.atEnd()==false; i++)
1004 v3s16 p = i.getNode()->getKey();
1006 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1007 <<") being handled"<<std::endl;*/
1009 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1013 // Reset block usage timer
1014 block->resetUsageTimer();
1016 // Set current time as timestamp
1017 block->setTimestampNoChangedFlag(m_game_time);
1018 // If time has changed much from the one on disk,
1019 // set block to be saved when it is unloaded
1020 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1021 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1022 "Timestamp older than 60s (step)");
1024 // Run node metadata
1025 bool changed = block->m_node_metadata->step(dtime);
1029 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1031 m_map->dispatchEvent(&event);
1033 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1034 "node metadata modified in step");
1039 const float abm_interval = 1.0;
1040 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1042 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1043 TimeTaker timer("modify in active blocks");
1045 // Initialize handling of ActiveBlockModifiers
1046 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1048 for(core::map<v3s16, bool>::Iterator
1049 i = m_active_blocks.m_list.getIterator();
1050 i.atEnd()==false; i++)
1052 v3s16 p = i.getNode()->getKey();
1054 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1055 <<") being handled"<<std::endl;*/
1057 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1061 // Set current time as timestamp
1062 block->setTimestampNoChangedFlag(m_game_time);
1064 /* Handle ActiveBlockModifiers */
1065 abmhandler.apply(block);
1068 u32 time_ms = timer.stop(true);
1069 u32 max_time_ms = 200;
1070 if(time_ms > max_time_ms){
1071 infostream<<"WARNING: active block modifiers took "
1072 <<time_ms<<"ms (longer than "
1073 <<max_time_ms<<"ms)"<<std::endl;
1078 Step script environment (run global on_step())
1080 scriptapi_environment_step(m_lua, dtime);
1086 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1087 //TimeTaker timer("Step active objects");
1089 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1091 // This helps the objects to send data at the same time
1092 bool send_recommended = false;
1093 m_send_recommended_timer += dtime;
1094 if(m_send_recommended_timer > getSendRecommendedInterval())
1096 m_send_recommended_timer -= getSendRecommendedInterval();
1097 send_recommended = true;
1100 for(core::map<u16, ServerActiveObject*>::Iterator
1101 i = m_active_objects.getIterator();
1102 i.atEnd()==false; i++)
1104 ServerActiveObject* obj = i.getNode()->getValue();
1105 // Remove non-peaceful mobs on peaceful mode
1106 if(g_settings->getBool("only_peaceful_mobs")){
1107 if(!obj->isPeaceful())
1108 obj->m_removed = true;
1110 // Don't step if is to be removed or stored statically
1111 if(obj->m_removed || obj->m_pending_deactivation)
1114 obj->step(dtime, send_recommended);
1115 // Read messages from object
1116 while(obj->m_messages_out.size() > 0)
1118 m_active_object_messages.push_back(
1119 obj->m_messages_out.pop_front());
1125 Manage active objects
1127 if(m_object_management_interval.step(dtime, 0.5))
1129 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1131 Remove objects that satisfy (m_removed && m_known_by_count==0)
1133 removeRemovedObjects();
1137 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1139 core::map<u16, ServerActiveObject*>::Node *n;
1140 n = m_active_objects.find(id);
1143 return n->getValue();
1146 bool isFreeServerActiveObjectId(u16 id,
1147 core::map<u16, ServerActiveObject*> &objects)
1152 for(core::map<u16, ServerActiveObject*>::Iterator
1153 i = objects.getIterator();
1154 i.atEnd()==false; i++)
1156 if(i.getNode()->getKey() == id)
1162 u16 getFreeServerActiveObjectId(
1163 core::map<u16, ServerActiveObject*> &objects)
1168 if(isFreeServerActiveObjectId(new_id, objects))
1178 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1181 u16 id = addActiveObjectRaw(object, true);
1185 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1189 v3f objectpos = obj->getBasePosition();
1191 // The block in which the object resides in
1192 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1195 Update the static data
1198 // Create new static object
1199 std::string staticdata = obj->getStaticData();
1200 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1201 // Add to the block where the object is located in
1202 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1203 // Get or generate the block
1204 MapBlock *block = m_map->emergeBlock(blockpos);
1206 bool succeeded = false;
1210 block->m_static_objects.insert(0, s_obj);
1211 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1212 "addActiveObjectAsStatic");
1216 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1217 <<"Could not find or generate "
1218 <<"a block for storing static object"<<std::endl;
1222 if(obj->environmentDeletes())
1229 Finds out what new objects have been added to
1230 inside a radius around a position
1232 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1233 core::map<u16, bool> ¤t_objects,
1234 core::map<u16, bool> &added_objects)
1236 v3f pos_f = intToFloat(pos, BS);
1237 f32 radius_f = radius * BS;
1239 Go through the object list,
1240 - discard m_removed objects,
1241 - discard objects that are too far away,
1242 - discard objects that are found in current_objects.
1243 - add remaining objects to added_objects
1245 for(core::map<u16, ServerActiveObject*>::Iterator
1246 i = m_active_objects.getIterator();
1247 i.atEnd()==false; i++)
1249 u16 id = i.getNode()->getKey();
1251 ServerActiveObject *object = i.getNode()->getValue();
1254 // Discard if removed
1255 if(object->m_removed)
1257 if(object->unlimitedTransferDistance() == false){
1258 // Discard if too far
1259 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1260 if(distance_f > radius_f)
1263 // Discard if already on current_objects
1264 core::map<u16, bool>::Node *n;
1265 n = current_objects.find(id);
1268 // Add to added_objects
1269 added_objects.insert(id, false);
1274 Finds out what objects have been removed from
1275 inside a radius around a position
1277 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1278 core::map<u16, bool> ¤t_objects,
1279 core::map<u16, bool> &removed_objects)
1281 v3f pos_f = intToFloat(pos, BS);
1282 f32 radius_f = radius * BS;
1284 Go through current_objects; object is removed if:
1285 - object is not found in m_active_objects (this is actually an
1286 error condition; objects should be set m_removed=true and removed
1287 only after all clients have been informed about removal), or
1288 - object has m_removed=true, or
1289 - object is too far away
1291 for(core::map<u16, bool>::Iterator
1292 i = current_objects.getIterator();
1293 i.atEnd()==false; i++)
1295 u16 id = i.getNode()->getKey();
1296 ServerActiveObject *object = getActiveObject(id);
1299 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1300 <<" object in current_objects is NULL"<<std::endl;
1301 removed_objects.insert(id, false);
1305 if(object->m_removed)
1307 removed_objects.insert(id, false);
1311 // If transfer distance is unlimited, don't remove
1312 if(object->unlimitedTransferDistance())
1315 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1317 if(distance_f >= radius_f)
1319 removed_objects.insert(id, false);
1327 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1329 if(m_active_object_messages.size() == 0)
1330 return ActiveObjectMessage(0);
1332 return m_active_object_messages.pop_front();
1336 ************ Private methods *************
1339 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1343 if(object->getId() == 0){
1344 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1347 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1348 <<"no free ids available"<<std::endl;
1349 if(object->environmentDeletes())
1353 object->setId(new_id);
1356 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1357 <<"supplied with id "<<object->getId()<<std::endl;
1359 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1361 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1362 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1363 if(object->environmentDeletes())
1367 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1368 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1370 m_active_objects.insert(object->getId(), object);
1372 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1373 <<"Added id="<<object->getId()<<"; there are now "
1374 <<m_active_objects.size()<<" active objects."
1377 // Register reference in scripting api (must be done before post-init)
1378 scriptapi_add_object_reference(m_lua, object);
1379 // Post-initialize object
1380 object->addedToEnvironment();
1382 // Add static data to block
1383 if(object->isStaticAllowed())
1385 // Add static object to active static list of the block
1386 v3f objectpos = object->getBasePosition();
1387 std::string staticdata = object->getStaticData();
1388 StaticObject s_obj(object->getType(), objectpos, staticdata);
1389 // Add to the block where the object is located in
1390 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1391 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1394 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1395 object->m_static_exists = true;
1396 object->m_static_block = blockpos;
1399 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1400 "addActiveObjectRaw");
1403 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1404 <<"could not find block for storing id="<<object->getId()
1405 <<" statically"<<std::endl;
1409 return object->getId();
1413 Remove objects that satisfy (m_removed && m_known_by_count==0)
1415 void ServerEnvironment::removeRemovedObjects()
1417 core::list<u16> objects_to_remove;
1418 for(core::map<u16, ServerActiveObject*>::Iterator
1419 i = m_active_objects.getIterator();
1420 i.atEnd()==false; i++)
1422 u16 id = i.getNode()->getKey();
1423 ServerActiveObject* obj = i.getNode()->getValue();
1424 // This shouldn't happen but check it
1427 infostream<<"NULL object found in ServerEnvironment"
1428 <<" while finding removed objects. id="<<id<<std::endl;
1429 // Id to be removed from m_active_objects
1430 objects_to_remove.push_back(id);
1435 We will delete objects that are marked as removed or thatare
1436 waiting for deletion after deactivation
1438 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1442 Delete static data from block if is marked as removed
1444 if(obj->m_static_exists && obj->m_removed)
1446 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1449 block->m_static_objects.remove(id);
1450 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1451 "removeRemovedObjects");
1452 obj->m_static_exists = false;
1456 // If m_known_by_count > 0, don't actually remove.
1457 if(obj->m_known_by_count > 0)
1460 // Tell the object about removal
1461 obj->removingFromEnvironment();
1462 // Deregister in scripting api
1463 scriptapi_rm_object_reference(m_lua, obj);
1466 if(obj->environmentDeletes())
1468 // Id to be removed from m_active_objects
1469 objects_to_remove.push_back(id);
1471 // Remove references from m_active_objects
1472 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1473 i != objects_to_remove.end(); i++)
1475 m_active_objects.remove(*i);
1479 static void print_hexdump(std::ostream &o, const std::string &data)
1481 const int linelength = 16;
1482 for(int l=0; ; l++){
1483 int i0 = linelength * l;
1484 bool at_end = false;
1485 int thislinelength = linelength;
1486 if(i0 + thislinelength > (int)data.size()){
1487 thislinelength = data.size() - i0;
1490 for(int di=0; di<linelength; di++){
1493 if(di<thislinelength)
1494 snprintf(buf, 4, "%.2x ", data[i]);
1496 snprintf(buf, 4, " ");
1500 for(int di=0; di<thislinelength; di++){
1514 Convert stored objects from blocks near the players to active.
1516 void ServerEnvironment::activateObjects(MapBlock *block)
1520 // Ignore if no stored objects (to not set changed flag)
1521 if(block->m_static_objects.m_stored.size() == 0)
1523 verbosestream<<"ServerEnvironment::activateObjects(): "
1524 <<"activating objects of block "<<PP(block->getPos())
1525 <<" ("<<block->m_static_objects.m_stored.size()
1526 <<" objects)"<<std::endl;
1527 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1529 errorstream<<"suspiciously large amount of objects detected: "
1530 <<block->m_static_objects.m_stored.size()<<" in "
1531 <<PP(block->getPos())
1532 <<"; removing all of them."<<std::endl;
1533 // Clear stored list
1534 block->m_static_objects.m_stored.clear();
1535 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1536 "stored list cleared in activateObjects due to "
1537 "large amount of objects");
1540 // A list for objects that couldn't be converted to static for some
1541 // reason. They will be stored back.
1542 core::list<StaticObject> new_stored;
1543 // Loop through stored static objects
1544 for(core::list<StaticObject>::Iterator
1545 i = block->m_static_objects.m_stored.begin();
1546 i != block->m_static_objects.m_stored.end(); i++)
1548 /*infostream<<"Server: Creating an active object from "
1549 <<"static data"<<std::endl;*/
1550 StaticObject &s_obj = *i;
1551 // Create an active object from the data
1552 ServerActiveObject *obj = ServerActiveObject::create
1553 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1554 // If couldn't create object, store static data back.
1557 errorstream<<"ServerEnvironment::activateObjects(): "
1558 <<"failed to create active object from static object "
1559 <<"in block "<<PP(s_obj.pos/BS)
1560 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1561 print_hexdump(verbosestream, s_obj.data);
1563 new_stored.push_back(s_obj);
1566 verbosestream<<"ServerEnvironment::activateObjects(): "
1567 <<"activated static object pos="<<PP(s_obj.pos/BS)
1568 <<" type="<<(int)s_obj.type<<std::endl;
1569 // This will also add the object to the active static list
1570 addActiveObjectRaw(obj, false);
1572 // Clear stored list
1573 block->m_static_objects.m_stored.clear();
1574 // Add leftover failed stuff to stored list
1575 for(core::list<StaticObject>::Iterator
1576 i = new_stored.begin();
1577 i != new_stored.end(); i++)
1579 StaticObject &s_obj = *i;
1580 block->m_static_objects.m_stored.push_back(s_obj);
1583 Note: Block hasn't really been modified here.
1584 The objects have just been activated and moved from the stored
1585 static list to the active static list.
1586 As such, the block is essentially the same.
1587 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1588 Otherwise there would be a huge amount of unnecessary I/O.
1593 Convert objects that are not standing inside active blocks to static.
1595 If m_known_by_count != 0, active object is not deleted, but static
1596 data is still updated.
1598 If force_delete is set, active object is deleted nevertheless. It
1599 shall only be set so in the destructor of the environment.
1601 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1603 core::list<u16> objects_to_remove;
1604 for(core::map<u16, ServerActiveObject*>::Iterator
1605 i = m_active_objects.getIterator();
1606 i.atEnd()==false; i++)
1608 ServerActiveObject* obj = i.getNode()->getValue();
1611 // Do not deactivate if static data creation not allowed
1612 if(!force_delete && !obj->isStaticAllowed())
1615 // If pending deactivation, let removeRemovedObjects() do it
1616 if(!force_delete && obj->m_pending_deactivation)
1619 u16 id = i.getNode()->getKey();
1620 v3f objectpos = obj->getBasePosition();
1622 // The block in which the object resides in
1623 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1625 // If block is active, don't remove
1626 if(!force_delete && m_active_blocks.contains(blockpos_o))
1629 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1630 <<"deactivating object id="<<id<<" on inactive block "
1631 <<PP(blockpos_o)<<std::endl;
1633 // If known by some client, don't immediately delete.
1634 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1637 Update the static data
1640 if(obj->isStaticAllowed())
1642 // Create new static object
1643 std::string staticdata_new = obj->getStaticData();
1644 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1646 bool stays_in_same_block = false;
1647 bool data_changed = true;
1649 if(obj->m_static_exists){
1650 if(obj->m_static_block == blockpos_o)
1651 stays_in_same_block = true;
1653 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1655 core::map<u16, StaticObject>::Node *n =
1656 block->m_static_objects.m_active.find(id);
1658 StaticObject static_old = n->getValue();
1660 float save_movem = obj->getMinimumSavedMovement();
1662 if(static_old.data == staticdata_new &&
1663 (static_old.pos - objectpos).getLength() < save_movem)
1664 data_changed = false;
1666 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1667 <<"id="<<id<<" m_static_exists=true but "
1668 <<"static data doesn't actually exist in "
1669 <<PP(obj->m_static_block)<<std::endl;
1673 bool shall_be_written = (!stays_in_same_block || data_changed);
1675 // Delete old static object
1676 if(obj->m_static_exists)
1678 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1681 block->m_static_objects.remove(id);
1682 obj->m_static_exists = false;
1683 // Only mark block as modified if data changed considerably
1684 if(shall_be_written)
1685 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1686 "deactivateFarObjects: Static data "
1687 "changed considerably");
1691 // Add to the block where the object is located in
1692 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1693 // Get or generate the block
1694 MapBlock *block = m_map->emergeBlock(blockpos);
1698 if(block->m_static_objects.m_stored.size() >= 49){
1699 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1700 <<" statically but block "<<PP(blockpos)
1701 <<" already contains "
1702 <<block->m_static_objects.m_stored.size()
1703 <<" (over 49) objects."
1704 <<" Forcing delete."<<std::endl;
1705 force_delete = true;
1707 u16 new_id = pending_delete ? id : 0;
1708 block->m_static_objects.insert(new_id, s_obj);
1710 // Only mark block as modified if data changed considerably
1711 if(shall_be_written)
1712 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1713 "deactivateFarObjects: Static data "
1714 "changed considerably");
1716 obj->m_static_exists = true;
1717 obj->m_static_block = block->getPos();
1722 errorstream<<"ServerEnv: Could not find or generate "
1723 <<"a block for storing id="<<obj->getId()
1724 <<" statically"<<std::endl;
1731 If known by some client, set pending deactivation.
1732 Otherwise delete it immediately.
1735 if(pending_delete && !force_delete)
1737 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1738 <<"object id="<<id<<" is known by clients"
1739 <<"; not deleting yet"<<std::endl;
1741 obj->m_pending_deactivation = true;
1745 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1746 <<"object id="<<id<<" is not known by clients"
1747 <<"; deleting"<<std::endl;
1749 // Tell the object about removal
1750 obj->removingFromEnvironment();
1751 // Deregister in scripting api
1752 scriptapi_rm_object_reference(m_lua, obj);
1754 // Delete active object
1755 if(obj->environmentDeletes())
1757 // Id to be removed from m_active_objects
1758 objects_to_remove.push_back(id);
1761 // Remove references from m_active_objects
1762 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1763 i != objects_to_remove.end(); i++)
1765 m_active_objects.remove(*i);
1776 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1777 ITextureSource *texturesource, IGameDef *gamedef,
1778 IrrlichtDevice *irr):
1781 m_texturesource(texturesource),
1787 ClientEnvironment::~ClientEnvironment()
1789 // delete active objects
1790 for(core::map<u16, ClientActiveObject*>::Iterator
1791 i = m_active_objects.getIterator();
1792 i.atEnd()==false; i++)
1794 delete i.getNode()->getValue();
1801 void ClientEnvironment::addPlayer(Player *player)
1803 DSTACK(__FUNCTION_NAME);
1805 It is a failure if player is local and there already is a local
1808 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1810 Environment::addPlayer(player);
1813 LocalPlayer * ClientEnvironment::getLocalPlayer()
1815 for(core::list<Player*>::Iterator i = m_players.begin();
1816 i != m_players.end(); i++)
1818 Player *player = *i;
1819 if(player->isLocal())
1820 return (LocalPlayer*)player;
1825 void ClientEnvironment::step(float dtime)
1827 DSTACK(__FUNCTION_NAME);
1829 // Get some settings
1830 bool free_move = g_settings->getBool("free_move");
1831 bool footprints = g_settings->getBool("footprints");
1834 LocalPlayer *lplayer = getLocalPlayer();
1836 // collision info queue
1837 core::list<CollisionInfo> player_collisions;
1840 Get the speed the player is going
1842 bool is_climbing = lplayer->is_climbing;
1844 f32 player_speed = lplayer->getSpeed().getLength();
1847 Maximum position increment
1849 //f32 position_max_increment = 0.05*BS;
1850 f32 position_max_increment = 0.1*BS;
1852 // Maximum time increment (for collision detection etc)
1853 // time = distance / speed
1854 f32 dtime_max_increment = 1;
1855 if(player_speed > 0.001)
1856 dtime_max_increment = position_max_increment / player_speed;
1858 // Maximum time increment is 10ms or lower
1859 if(dtime_max_increment > 0.01)
1860 dtime_max_increment = 0.01;
1862 // Don't allow overly huge dtime
1866 f32 dtime_downcount = dtime;
1869 Stuff that has a maximum time increment
1878 if(dtime_downcount > dtime_max_increment)
1880 dtime_part = dtime_max_increment;
1881 dtime_downcount -= dtime_part;
1885 dtime_part = dtime_downcount;
1887 Setting this to 0 (no -=dtime_part) disables an infinite loop
1888 when dtime_part is so small that dtime_downcount -= dtime_part
1891 dtime_downcount = 0;
1899 v3f lplayerpos = lplayer->getPosition();
1902 if(free_move == false && is_climbing == false)
1905 v3f speed = lplayer->getSpeed();
1906 if(lplayer->swimming_up == false)
1907 speed.Y -= 9.81 * BS * dtime_part * 2;
1910 if(lplayer->in_water_stable || lplayer->in_water)
1912 f32 max_down = 2.0*BS;
1913 if(speed.Y < -max_down) speed.Y = -max_down;
1916 if(speed.getLength() > max)
1918 speed = speed / speed.getLength() * max;
1922 lplayer->setSpeed(speed);
1927 This also does collision detection.
1929 lplayer->move(dtime_part, *m_map, position_max_increment,
1930 &player_collisions);
1933 while(dtime_downcount > 0.001);
1935 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1937 for(core::list<CollisionInfo>::Iterator
1938 i = player_collisions.begin();
1939 i != player_collisions.end(); i++)
1941 CollisionInfo &info = *i;
1942 if(info.t == COLLISION_FALL)
1944 //f32 tolerance = BS*10; // 2 without damage
1945 f32 tolerance = BS*12; // 3 without damage
1947 if(info.speed > tolerance)
1949 f32 damage_f = (info.speed - tolerance)/BS*factor;
1950 u16 damage = (u16)(damage_f+0.5);
1951 if(lplayer->hp > damage)
1952 lplayer->hp -= damage;
1956 ClientEnvEvent event;
1957 event.type = CEE_PLAYER_DAMAGE;
1958 event.player_damage.amount = damage;
1959 event.player_damage.send_to_server = true;
1960 m_client_event_queue.push_back(event);
1966 A quick draft of lava damage
1968 if(m_lava_hurt_interval.step(dtime, 1.0))
1970 v3f pf = lplayer->getPosition();
1972 // Feet, middle and head
1973 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1974 MapNode n1 = m_map->getNodeNoEx(p1);
1975 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1976 MapNode n2 = m_map->getNodeNoEx(p2);
1977 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1978 MapNode n3 = m_map->getNodeNoEx(p2);
1980 u32 damage_per_second = 0;
1981 damage_per_second = MYMAX(damage_per_second,
1982 m_gamedef->ndef()->get(n1).damage_per_second);
1983 damage_per_second = MYMAX(damage_per_second,
1984 m_gamedef->ndef()->get(n2).damage_per_second);
1985 damage_per_second = MYMAX(damage_per_second,
1986 m_gamedef->ndef()->get(n3).damage_per_second);
1988 if(damage_per_second != 0)
1990 ClientEnvEvent event;
1991 event.type = CEE_PLAYER_DAMAGE;
1992 event.player_damage.amount = damage_per_second;
1993 event.player_damage.send_to_server = true;
1994 m_client_event_queue.push_back(event);
1999 Stuff that can be done in an arbitarily large dtime
2001 for(core::list<Player*>::Iterator i = m_players.begin();
2002 i != m_players.end(); i++)
2004 Player *player = *i;
2005 v3f playerpos = player->getPosition();
2008 Handle non-local players
2010 if(player->isLocal() == false)
2013 player->move(dtime, *m_map, 100*BS);
2017 // Update lighting on all players on client
2018 u8 light = LIGHT_MAX;
2021 v3s16 p = player->getLightPosition();
2022 MapNode n = m_map->getNode(p);
2023 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2025 catch(InvalidPositionException &e) {}
2026 player->updateLight(light);
2029 Add footsteps to grass
2033 // Get node that is at BS/4 under player
2034 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2036 MapNode n = m_map->getNode(bottompos);
2037 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2039 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2040 m_map->setNode(bottompos, n);
2041 // Update mesh on client
2042 if(m_map->mapType() == MAPTYPE_CLIENT)
2044 v3s16 p_blocks = getNodeBlockPos(bottompos);
2045 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2046 //b->updateMesh(getDayNightRatio());
2047 b->setMeshExpired(true);
2051 catch(InvalidPositionException &e)
2058 Step active objects and update lighting of them
2061 for(core::map<u16, ClientActiveObject*>::Iterator
2062 i = m_active_objects.getIterator();
2063 i.atEnd()==false; i++)
2065 ClientActiveObject* obj = i.getNode()->getValue();
2067 obj->step(dtime, this);
2069 if(m_active_object_light_update_interval.step(dtime, 0.21))
2072 //u8 light = LIGHT_MAX;
2076 v3s16 p = obj->getLightPosition();
2077 MapNode n = m_map->getNode(p);
2078 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2080 catch(InvalidPositionException &e) {}
2081 obj->updateLight(light);
2086 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2088 m_map->updateMeshes(blockpos, getDayNightRatio());
2091 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2093 m_map->expireMeshes(only_daynight_diffed);
2096 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2098 core::map<u16, ClientActiveObject*>::Node *n;
2099 n = m_active_objects.find(id);
2102 return n->getValue();
2105 bool isFreeClientActiveObjectId(u16 id,
2106 core::map<u16, ClientActiveObject*> &objects)
2111 for(core::map<u16, ClientActiveObject*>::Iterator
2112 i = objects.getIterator();
2113 i.atEnd()==false; i++)
2115 if(i.getNode()->getKey() == id)
2121 u16 getFreeClientActiveObjectId(
2122 core::map<u16, ClientActiveObject*> &objects)
2127 if(isFreeClientActiveObjectId(new_id, objects))
2137 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2140 if(object->getId() == 0)
2142 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2145 infostream<<"ClientEnvironment::addActiveObject(): "
2146 <<"no free ids available"<<std::endl;
2150 object->setId(new_id);
2152 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2154 infostream<<"ClientEnvironment::addActiveObject(): "
2155 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2159 infostream<<"ClientEnvironment::addActiveObject(): "
2160 <<"added (id="<<object->getId()<<")"<<std::endl;
2161 m_active_objects.insert(object->getId(), object);
2162 object->addToScene(m_smgr, m_texturesource, m_irr);
2163 { // Update lighting immediately
2167 v3s16 p = object->getLightPosition();
2168 MapNode n = m_map->getNode(p);
2169 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2171 catch(InvalidPositionException &e) {}
2172 object->updateLight(light);
2174 return object->getId();
2177 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2178 const std::string &init_data)
2180 ClientActiveObject* obj =
2181 ClientActiveObject::create(type, m_gamedef, this);
2184 infostream<<"ClientEnvironment::addActiveObject(): "
2185 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2192 obj->initialize(init_data);
2194 addActiveObject(obj);
2197 void ClientEnvironment::removeActiveObject(u16 id)
2199 infostream<<"ClientEnvironment::removeActiveObject(): "
2200 <<"id="<<id<<std::endl;
2201 ClientActiveObject* obj = getActiveObject(id);
2204 infostream<<"ClientEnvironment::removeActiveObject(): "
2205 <<"id="<<id<<" not found"<<std::endl;
2208 obj->removeFromScene();
2210 m_active_objects.remove(id);
2213 void ClientEnvironment::processActiveObjectMessage(u16 id,
2214 const std::string &data)
2216 ClientActiveObject* obj = getActiveObject(id);
2219 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2220 <<" got message for id="<<id<<", which doesn't exist."
2224 obj->processMessage(data);
2228 Callbacks for activeobjects
2231 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2233 LocalPlayer *lplayer = getLocalPlayer();
2237 if(lplayer->hp > damage)
2238 lplayer->hp -= damage;
2243 ClientEnvEvent event;
2244 event.type = CEE_PLAYER_DAMAGE;
2245 event.player_damage.amount = damage;
2246 event.player_damage.send_to_server = handle_hp;
2247 m_client_event_queue.push_back(event);
2251 Client likes to call these
2254 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2255 core::array<DistanceSortedActiveObject> &dest)
2257 for(core::map<u16, ClientActiveObject*>::Iterator
2258 i = m_active_objects.getIterator();
2259 i.atEnd()==false; i++)
2261 ClientActiveObject* obj = i.getNode()->getValue();
2263 f32 d = (obj->getPosition() - origin).getLength();
2268 DistanceSortedActiveObject dso(obj, d);
2270 dest.push_back(dso);
2274 ClientEnvEvent ClientEnvironment::getClientEvent()
2276 if(m_client_event_queue.size() == 0)
2278 ClientEnvEvent event;
2279 event.type = CEE_NONE;
2282 return m_client_event_queue.pop_front();
2285 #endif // #ifndef SERVER