3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
40 #include "serverremoteplayer.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 Environment::Environment():
49 Environment::~Environment()
52 for(core::list<Player*>::Iterator i = m_players.begin();
53 i != m_players.end(); i++)
59 void Environment::addPlayer(Player *player)
61 DSTACK(__FUNCTION_NAME);
63 Check that peer_ids are unique.
64 Also check that names are unique.
65 Exception: there can be multiple players with peer_id=0
67 // If peer id is non-zero, it has to be unique.
68 if(player->peer_id != 0)
69 assert(getPlayer(player->peer_id) == NULL);
70 // Name has to be unique.
71 assert(getPlayer(player->getName()) == NULL);
73 m_players.push_back(player);
76 void Environment::removePlayer(u16 peer_id)
78 DSTACK(__FUNCTION_NAME);
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id != peer_id)
89 // See if there is an another one
90 // (shouldn't be, but just to be sure)
95 Player * Environment::getPlayer(u16 peer_id)
97 for(core::list<Player*>::Iterator i = m_players.begin();
98 i != m_players.end(); i++)
101 if(player->peer_id == peer_id)
107 Player * Environment::getPlayer(const char *name)
109 for(core::list<Player*>::Iterator i = m_players.begin();
110 i != m_players.end(); i++)
113 if(strcmp(player->getName(), name) == 0)
119 Player * Environment::getRandomConnectedPlayer()
121 core::list<Player*> connected_players = getPlayers(true);
122 u32 chosen_one = myrand() % connected_players.size();
124 for(core::list<Player*>::Iterator
125 i = connected_players.begin();
126 i != connected_players.end(); i++)
138 Player * Environment::getNearestConnectedPlayer(v3f pos)
140 core::list<Player*> connected_players = getPlayers(true);
142 Player *nearest_player = NULL;
143 for(core::list<Player*>::Iterator
144 i = connected_players.begin();
145 i != connected_players.end(); i++)
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL)
152 nearest_player = player;
155 return nearest_player;
158 core::list<Player*> Environment::getPlayers()
163 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
165 core::list<Player*> newlist;
166 for(core::list<Player*>::Iterator
167 i = m_players.begin();
168 i != m_players.end(); i++)
172 if(ignore_disconnected)
174 // Ignore disconnected players
175 if(player->peer_id == 0)
179 newlist.push_back(player);
184 void Environment::printPlayers(std::ostream &o)
186 o<<"Players in environment:"<<std::endl;
187 for(core::list<Player*>::Iterator i = m_players.begin();
188 i != m_players.end(); i++)
191 o<<"Player peer_id="<<player->peer_id<<std::endl;
195 /*void Environment::setDayNightRatio(u32 r)
197 getDayNightRatio() = r;
200 u32 Environment::getDayNightRatio()
202 //return getDayNightRatio();
203 return time_to_daynight_ratio(m_time_of_day);
210 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
214 // Initialize timer to random value to spread processing
215 float itv = abm->getTriggerInterval();
216 timer = myrand_range(-0.51*itv, 0.51*itv);
223 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
226 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
227 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
228 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
235 void ActiveBlockList::update(core::list<v3s16> &active_positions,
237 core::map<v3s16, bool> &blocks_removed,
238 core::map<v3s16, bool> &blocks_added)
243 core::map<v3s16, bool> newlist;
244 for(core::list<v3s16>::Iterator i = active_positions.begin();
245 i != active_positions.end(); i++)
247 fillRadiusBlock(*i, radius, newlist);
251 Find out which blocks on the old list are not on the new list
253 // Go through old list
254 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
255 i.atEnd()==false; i++)
257 v3s16 p = i.getNode()->getKey();
258 // If not on new list, it's been removed
259 if(newlist.find(p) == NULL)
260 blocks_removed.insert(p, true);
264 Find out which blocks on the new list are not on the old list
266 // Go through new list
267 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
268 i.atEnd()==false; i++)
270 v3s16 p = i.getNode()->getKey();
271 // If not on old list, it's been added
272 if(m_list.find(p) == NULL)
273 blocks_added.insert(p, true);
280 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
281 i.atEnd()==false; i++)
283 v3s16 p = i.getNode()->getKey();
284 m_list.insert(p, true);
292 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
293 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
298 m_random_spawn_timer(3),
299 m_send_recommended_timer(0),
301 m_game_time_fraction_counter(0)
305 ServerEnvironment::~ServerEnvironment()
307 // Clear active block list.
308 // This makes the next one delete all active objects.
309 m_active_blocks.clear();
311 // Convert all objects to static and delete the active objects
312 deactivateFarObjects(true);
317 // Delete ActiveBlockModifiers
318 for(core::list<ABMWithState>::Iterator
319 i = m_abms.begin(); i != m_abms.end(); i++){
324 void ServerEnvironment::serializePlayers(const std::string &savedir)
326 std::string players_path = savedir + "/players";
327 fs::CreateDir(players_path);
329 core::map<Player*, bool> saved_players;
331 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
332 for(u32 i=0; i<player_files.size(); i++)
334 if(player_files[i].dir)
337 // Full path to this file
338 std::string path = players_path + "/" + player_files[i].name;
340 //infostream<<"Checking player file "<<path<<std::endl;
342 // Load player to see what is its name
343 ServerRemotePlayer testplayer(this);
345 // Open file and deserialize
346 std::ifstream is(path.c_str(), std::ios_base::binary);
347 if(is.good() == false)
349 infostream<<"Failed to read "<<path<<std::endl;
352 testplayer.deSerialize(is);
355 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
357 // Search for the player
358 std::string playername = testplayer.getName();
359 Player *player = getPlayer(playername.c_str());
362 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
366 //infostream<<"Found matching player, overwriting."<<std::endl;
368 // OK, found. Save player there.
370 // Open file and serialize
371 std::ofstream os(path.c_str(), std::ios_base::binary);
372 if(os.good() == false)
374 infostream<<"Failed to overwrite "<<path<<std::endl;
377 player->serialize(os);
378 saved_players.insert(player, true);
382 for(core::list<Player*>::Iterator i = m_players.begin();
383 i != m_players.end(); i++)
386 if(saved_players.find(player) != NULL)
388 /*infostream<<"Player "<<player->getName()
389 <<" was already saved."<<std::endl;*/
392 std::string playername = player->getName();
393 // Don't save unnamed player
396 //infostream<<"Not saving unnamed player."<<std::endl;
402 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
403 playername = "player";
404 std::string path = players_path + "/" + playername;
406 for(u32 i=0; i<1000; i++)
408 if(fs::PathExists(path) == false)
413 path = players_path + "/" + playername + itos(i);
417 infostream<<"Didn't find free file for player"<<std::endl;
422 /*infostream<<"Saving player "<<player->getName()<<" to "
424 // Open file and serialize
425 std::ofstream os(path.c_str(), std::ios_base::binary);
426 if(os.good() == false)
428 infostream<<"Failed to overwrite "<<path<<std::endl;
431 player->serialize(os);
432 saved_players.insert(player, true);
436 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
439 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
441 std::string players_path = savedir + "/players";
443 core::map<Player*, bool> saved_players;
445 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
446 for(u32 i=0; i<player_files.size(); i++)
448 if(player_files[i].dir)
451 // Full path to this file
452 std::string path = players_path + "/" + player_files[i].name;
454 infostream<<"Checking player file "<<path<<std::endl;
456 // Load player to see what is its name
457 ServerRemotePlayer testplayer(this);
459 // Open file and deserialize
460 std::ifstream is(path.c_str(), std::ios_base::binary);
461 if(is.good() == false)
463 infostream<<"Failed to read "<<path<<std::endl;
466 testplayer.deSerialize(is);
469 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
471 infostream<<"Not loading player with invalid name: "
472 <<testplayer.getName()<<std::endl;
475 infostream<<"Loaded test player with name "<<testplayer.getName()
478 // Search for the player
479 std::string playername = testplayer.getName();
480 Player *player = getPlayer(playername.c_str());
481 bool newplayer = false;
484 infostream<<"Is a new player"<<std::endl;
485 player = new ServerRemotePlayer(this);
489 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
493 infostream<<"Reading player "<<testplayer.getName()<<" from "
495 // Open file and deserialize
496 std::ifstream is(path.c_str(), std::ios_base::binary);
497 if(is.good() == false)
499 infostream<<"Failed to read "<<path<<std::endl;
502 srp->deSerialize(is);
503 srp->m_last_good_position = srp->getBasePosition();
504 srp->m_last_good_position_age = 0;
514 void ServerEnvironment::saveMeta(const std::string &savedir)
516 std::string path = savedir + "/env_meta.txt";
518 // Open file and serialize
519 std::ofstream os(path.c_str(), std::ios_base::binary);
520 if(os.good() == false)
522 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
524 throw SerializationError("Couldn't save env meta");
528 args.setU64("game_time", m_game_time);
529 args.setU64("time_of_day", getTimeOfDay());
534 void ServerEnvironment::loadMeta(const std::string &savedir)
536 std::string path = savedir + "/env_meta.txt";
538 // Open file and deserialize
539 std::ifstream is(path.c_str(), std::ios_base::binary);
540 if(is.good() == false)
542 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
544 throw SerializationError("Couldn't load env meta");
552 throw SerializationError
553 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
555 std::getline(is, line);
556 std::string trimmedline = trim(line);
557 if(trimmedline == "EnvArgsEnd")
559 args.parseConfigLine(line);
563 m_game_time = args.getU64("game_time");
564 }catch(SettingNotFoundException &e){
565 // Getting this is crucial, otherwise timestamps are useless
566 throw SerializationError("Couldn't load env meta game_time");
570 m_time_of_day = args.getU64("time_of_day");
571 }catch(SettingNotFoundException &e){
572 // This is not as important
573 m_time_of_day = 9000;
579 ActiveBlockModifier *abm;
581 std::set<content_t> required_neighbors;
587 ServerEnvironment *m_env;
588 std::map<content_t, std::list<ActiveABM> > m_aabms;
590 ABMHandler(core::list<ABMWithState> &abms,
591 float dtime_s, ServerEnvironment *env,
597 INodeDefManager *ndef = env->getGameDef()->ndef();
598 for(core::list<ABMWithState>::Iterator
599 i = abms.begin(); i != abms.end(); i++){
600 ActiveBlockModifier *abm = i->abm;
601 float trigger_interval = abm->getTriggerInterval();
602 if(trigger_interval < 0.001)
603 trigger_interval = 0.001;
604 float actual_interval = dtime_s;
607 if(i->timer < trigger_interval)
609 i->timer -= trigger_interval;
610 actual_interval = trigger_interval;
614 float intervals = actual_interval / trigger_interval;
615 float chance = abm->getTriggerChance();
618 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
622 std::set<std::string> required_neighbors_s
623 = abm->getRequiredNeighbors();
624 for(std::set<std::string>::iterator
625 i = required_neighbors_s.begin();
626 i != required_neighbors_s.end(); i++){
627 content_t c = ndef->getId(*i);
628 if(c == CONTENT_IGNORE)
630 aabm.required_neighbors.insert(c);
633 std::set<std::string> contents_s = abm->getTriggerContents();
634 for(std::set<std::string>::iterator
635 i = contents_s.begin(); i != contents_s.end(); i++){
636 content_t c = ndef->getId(*i);
637 if(c == CONTENT_IGNORE)
639 std::map<content_t, std::list<ActiveABM> >::iterator j;
641 if(j == m_aabms.end()){
642 std::list<ActiveABM> aabmlist;
643 m_aabms[c] = aabmlist;
646 j->second.push_back(aabm);
650 void apply(MapBlock *block)
655 ServerMap *map = &m_env->getServerMap();
658 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
659 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
660 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
662 MapNode n = block->getNodeNoEx(p0);
663 content_t c = n.getContent();
664 v3s16 p = p0 + block->getPosRelative();
666 std::map<content_t, std::list<ActiveABM> >::iterator j;
668 if(j == m_aabms.end())
671 for(std::list<ActiveABM>::iterator
672 i = j->second.begin(); i != j->second.end(); i++)
674 if(myrand() % i->chance != 0)
678 if(!i->required_neighbors.empty())
681 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
682 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
683 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
687 MapNode n = map->getNodeNoEx(p1);
688 content_t c = n.getContent();
689 std::set<content_t>::const_iterator k;
690 k = i->required_neighbors.find(c);
691 if(k != i->required_neighbors.end()){
695 // No required neighbor found
700 // Find out how many objects the block contains
701 u32 active_object_count = block->m_static_objects.m_active.size();
702 // Find out how many objects this and all the neighbors contain
703 u32 active_object_count_wider = 0;
704 for(s16 x=-1; x<=1; x++)
705 for(s16 y=-1; y<=1; y++)
706 for(s16 z=-1; z<=1; z++)
708 MapBlock *block2 = map->getBlockNoCreateNoEx(
709 block->getPos() + v3s16(x,y,z));
712 active_object_count_wider +=
713 block2->m_static_objects.m_active.size()
714 + block2->m_static_objects.m_stored.size();
717 // Call all the trigger variations
718 i->abm->trigger(m_env, p, n);
719 i->abm->trigger(m_env, p, n,
720 active_object_count, active_object_count_wider);
726 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
728 // Get time difference
730 u32 stamp = block->getTimestamp();
731 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
732 dtime_s = m_game_time - block->getTimestamp();
733 dtime_s += additional_dtime;
735 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
736 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
738 // Set current time as timestamp
739 block->setTimestampNoChangedFlag(m_game_time);
741 /*infostream<<"ServerEnvironment::activateBlock(): block is "
742 <<dtime_s<<" seconds old."<<std::endl;*/
744 // Activate stored objects
745 activateObjects(block);
748 bool changed = block->m_node_metadata->step((float)dtime_s);
752 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
753 event.p = block->getPos();
754 m_map->dispatchEvent(&event);
756 block->raiseModified(MOD_STATE_WRITE_NEEDED,
757 "node metadata modified in activateBlock");
760 /* Handle ActiveBlockModifiers */
761 ABMHandler abmhandler(m_abms, dtime_s, this, false);
762 abmhandler.apply(block);
765 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
767 m_abms.push_back(ABMWithState(abm));
770 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
772 std::set<u16> objects;
773 for(core::map<u16, ServerActiveObject*>::Iterator
774 i = m_active_objects.getIterator();
775 i.atEnd()==false; i++)
777 ServerActiveObject* obj = i.getNode()->getValue();
778 u16 id = i.getNode()->getKey();
779 v3f objectpos = obj->getBasePosition();
780 if(objectpos.getDistanceFrom(pos) > radius)
787 void ServerEnvironment::clearAllObjects()
789 infostream<<"ServerEnvironment::clearAllObjects(): "
790 <<"Removing all active objects"<<std::endl;
791 core::list<u16> objects_to_remove;
792 for(core::map<u16, ServerActiveObject*>::Iterator
793 i = m_active_objects.getIterator();
794 i.atEnd()==false; i++)
796 ServerActiveObject* obj = i.getNode()->getValue();
797 u16 id = i.getNode()->getKey();
798 v3f objectpos = obj->getBasePosition();
799 // Delete static object if block is loaded
800 if(obj->m_static_exists){
801 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
803 block->m_static_objects.remove(id);
804 block->raiseModified(MOD_STATE_WRITE_NEEDED,
806 obj->m_static_exists = false;
809 // If known by some client, don't delete immediately
810 if(obj->m_known_by_count > 0){
811 obj->m_pending_deactivation = true;
812 obj->m_removed = true;
816 // Tell the object about removal
817 obj->removingFromEnvironment();
818 // Deregister in scripting api
819 scriptapi_rm_object_reference(m_lua, obj);
821 // Delete active object
822 if(obj->environmentDeletes())
824 // Id to be removed from m_active_objects
825 objects_to_remove.push_back(id);
827 // Remove references from m_active_objects
828 for(core::list<u16>::Iterator i = objects_to_remove.begin();
829 i != objects_to_remove.end(); i++)
831 m_active_objects.remove(*i);
834 core::list<v3s16> loadable_blocks;
835 infostream<<"ServerEnvironment::clearAllObjects(): "
836 <<"Listing all loadable blocks"<<std::endl;
837 m_map->listAllLoadableBlocks(loadable_blocks);
838 infostream<<"ServerEnvironment::clearAllObjects(): "
839 <<"Done listing all loadable blocks: "
840 <<loadable_blocks.size()
841 <<", now clearing"<<std::endl;
842 u32 report_interval = loadable_blocks.size() / 10;
843 u32 num_blocks_checked = 0;
844 u32 num_blocks_cleared = 0;
845 u32 num_objs_cleared = 0;
846 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
847 i != loadable_blocks.end(); i++)
850 MapBlock *block = m_map->emergeBlock(p, false);
852 errorstream<<"ServerEnvironment::clearAllObjects(): "
853 <<"Failed to emerge block "<<PP(p)<<std::endl;
856 u32 num_stored = block->m_static_objects.m_stored.size();
857 u32 num_active = block->m_static_objects.m_active.size();
858 if(num_stored != 0 || num_active != 0){
859 block->m_static_objects.m_stored.clear();
860 block->m_static_objects.m_active.clear();
861 block->raiseModified(MOD_STATE_WRITE_NEEDED,
863 num_objs_cleared += num_stored + num_active;
864 num_blocks_cleared++;
866 num_blocks_checked++;
868 if(num_blocks_checked % report_interval == 0){
869 float percent = 100.0 * (float)num_blocks_checked /
870 loadable_blocks.size();
871 infostream<<"ServerEnvironment::clearAllObjects(): "
872 <<"Cleared "<<num_objs_cleared<<" objects"
873 <<" in "<<num_blocks_cleared<<" blocks ("
874 <<percent<<"%)"<<std::endl;
877 infostream<<"ServerEnvironment::clearAllObjects(): "
878 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
879 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
882 void ServerEnvironment::step(float dtime)
884 DSTACK(__FUNCTION_NAME);
886 //TimeTaker timer("ServerEnv step");
889 bool footprints = g_settings->getBool("footprints");
895 m_game_time_fraction_counter += dtime;
896 u32 inc_i = (u32)m_game_time_fraction_counter;
897 m_game_time += inc_i;
898 m_game_time_fraction_counter -= (float)inc_i;
905 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
906 for(core::list<Player*>::Iterator i = m_players.begin();
907 i != m_players.end(); i++)
911 // Ignore disconnected players
912 if(player->peer_id == 0)
915 v3f playerpos = player->getPosition();
918 player->move(dtime, *m_map, 100*BS);
921 Add footsteps to grass
925 // Get node that is at BS/4 under player
926 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
928 MapNode n = m_map->getNode(bottompos);
929 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
931 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
932 m_map->setNode(bottompos, n);
935 catch(InvalidPositionException &e)
943 Manage active block list
945 if(m_active_blocks_management_interval.step(dtime, 2.0))
947 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
949 Get player block positions
951 core::list<v3s16> players_blockpos;
952 for(core::list<Player*>::Iterator
953 i = m_players.begin();
954 i != m_players.end(); i++)
957 // Ignore disconnected players
958 if(player->peer_id == 0)
960 v3s16 blockpos = getNodeBlockPos(
961 floatToInt(player->getPosition(), BS));
962 players_blockpos.push_back(blockpos);
966 Update list of active blocks, collecting changes
968 const s16 active_block_range = g_settings->getS16("active_block_range");
969 core::map<v3s16, bool> blocks_removed;
970 core::map<v3s16, bool> blocks_added;
971 m_active_blocks.update(players_blockpos, active_block_range,
972 blocks_removed, blocks_added);
975 Handle removed blocks
978 // Convert active objects that are no more in active blocks to static
979 deactivateFarObjects(false);
981 for(core::map<v3s16, bool>::Iterator
982 i = blocks_removed.getIterator();
983 i.atEnd()==false; i++)
985 v3s16 p = i.getNode()->getKey();
987 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
988 <<") became inactive"<<std::endl;*/
990 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
994 // Set current time as timestamp (and let it set ChangedFlag)
995 block->setTimestamp(m_game_time);
1002 for(core::map<v3s16, bool>::Iterator
1003 i = blocks_added.getIterator();
1004 i.atEnd()==false; i++)
1006 v3s16 p = i.getNode()->getKey();
1008 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1009 <<") became active"<<std::endl;*/
1011 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1013 // Block needs to be fetched first
1014 m_emerger->queueBlockEmerge(p, false);
1015 m_active_blocks.m_list.remove(p);
1019 activateBlock(block);
1024 Mess around in active blocks
1026 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1028 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1032 for(core::map<v3s16, bool>::Iterator
1033 i = m_active_blocks.m_list.getIterator();
1034 i.atEnd()==false; i++)
1036 v3s16 p = i.getNode()->getKey();
1038 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1039 <<") being handled"<<std::endl;*/
1041 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1045 // Reset block usage timer
1046 block->resetUsageTimer();
1048 // Set current time as timestamp
1049 block->setTimestampNoChangedFlag(m_game_time);
1050 // If time has changed much from the one on disk,
1051 // set block to be saved when it is unloaded
1052 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1053 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1054 "Timestamp older than 60s (step)");
1056 // Run node metadata
1057 bool changed = block->m_node_metadata->step(dtime);
1061 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1063 m_map->dispatchEvent(&event);
1065 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1066 "node metadata modified in step");
1071 const float abm_interval = 1.0;
1072 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1074 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1075 TimeTaker timer("modify in active blocks");
1077 // Initialize handling of ActiveBlockModifiers
1078 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1080 for(core::map<v3s16, bool>::Iterator
1081 i = m_active_blocks.m_list.getIterator();
1082 i.atEnd()==false; i++)
1084 v3s16 p = i.getNode()->getKey();
1086 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1087 <<") being handled"<<std::endl;*/
1089 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1093 // Set current time as timestamp
1094 block->setTimestampNoChangedFlag(m_game_time);
1096 /* Handle ActiveBlockModifiers */
1097 abmhandler.apply(block);
1100 u32 time_ms = timer.stop(true);
1101 u32 max_time_ms = 200;
1102 if(time_ms > max_time_ms){
1103 infostream<<"WARNING: active block modifiers took "
1104 <<time_ms<<"ms (longer than "
1105 <<max_time_ms<<"ms)"<<std::endl;
1110 Step script environment (run global on_step())
1112 scriptapi_environment_step(m_lua, dtime);
1118 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1119 //TimeTaker timer("Step active objects");
1121 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1123 // This helps the objects to send data at the same time
1124 bool send_recommended = false;
1125 m_send_recommended_timer += dtime;
1126 if(m_send_recommended_timer > getSendRecommendedInterval())
1128 m_send_recommended_timer -= getSendRecommendedInterval();
1129 send_recommended = true;
1132 for(core::map<u16, ServerActiveObject*>::Iterator
1133 i = m_active_objects.getIterator();
1134 i.atEnd()==false; i++)
1136 ServerActiveObject* obj = i.getNode()->getValue();
1137 // Remove non-peaceful mobs on peaceful mode
1138 if(g_settings->getBool("only_peaceful_mobs")){
1139 if(!obj->isPeaceful())
1140 obj->m_removed = true;
1142 // Don't step if is to be removed or stored statically
1143 if(obj->m_removed || obj->m_pending_deactivation)
1146 obj->step(dtime, send_recommended);
1147 // Read messages from object
1148 while(obj->m_messages_out.size() > 0)
1150 m_active_object_messages.push_back(
1151 obj->m_messages_out.pop_front());
1157 Manage active objects
1159 if(m_object_management_interval.step(dtime, 0.5))
1161 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1163 Remove objects that satisfy (m_removed && m_known_by_count==0)
1165 removeRemovedObjects();
1169 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1171 core::map<u16, ServerActiveObject*>::Node *n;
1172 n = m_active_objects.find(id);
1175 return n->getValue();
1178 bool isFreeServerActiveObjectId(u16 id,
1179 core::map<u16, ServerActiveObject*> &objects)
1184 for(core::map<u16, ServerActiveObject*>::Iterator
1185 i = objects.getIterator();
1186 i.atEnd()==false; i++)
1188 if(i.getNode()->getKey() == id)
1194 u16 getFreeServerActiveObjectId(
1195 core::map<u16, ServerActiveObject*> &objects)
1200 if(isFreeServerActiveObjectId(new_id, objects))
1210 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1213 u16 id = addActiveObjectRaw(object, true);
1217 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1221 v3f objectpos = obj->getBasePosition();
1223 // The block in which the object resides in
1224 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1227 Update the static data
1230 // Create new static object
1231 std::string staticdata = obj->getStaticData();
1232 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1233 // Add to the block where the object is located in
1234 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1235 // Get or generate the block
1236 MapBlock *block = m_map->emergeBlock(blockpos);
1238 bool succeeded = false;
1242 block->m_static_objects.insert(0, s_obj);
1243 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1244 "addActiveObjectAsStatic");
1248 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1249 <<"Could not find or generate "
1250 <<"a block for storing static object"<<std::endl;
1254 if(obj->environmentDeletes())
1261 Finds out what new objects have been added to
1262 inside a radius around a position
1264 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1265 core::map<u16, bool> ¤t_objects,
1266 core::map<u16, bool> &added_objects)
1268 v3f pos_f = intToFloat(pos, BS);
1269 f32 radius_f = radius * BS;
1271 Go through the object list,
1272 - discard m_removed objects,
1273 - discard objects that are too far away,
1274 - discard objects that are found in current_objects.
1275 - add remaining objects to added_objects
1277 for(core::map<u16, ServerActiveObject*>::Iterator
1278 i = m_active_objects.getIterator();
1279 i.atEnd()==false; i++)
1281 u16 id = i.getNode()->getKey();
1283 ServerActiveObject *object = i.getNode()->getValue();
1286 // Discard if removed
1287 if(object->m_removed)
1289 if(object->unlimitedTransferDistance() == false){
1290 // Discard if too far
1291 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1292 if(distance_f > radius_f)
1295 // Discard if already on current_objects
1296 core::map<u16, bool>::Node *n;
1297 n = current_objects.find(id);
1300 // Add to added_objects
1301 added_objects.insert(id, false);
1306 Finds out what objects have been removed from
1307 inside a radius around a position
1309 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1310 core::map<u16, bool> ¤t_objects,
1311 core::map<u16, bool> &removed_objects)
1313 v3f pos_f = intToFloat(pos, BS);
1314 f32 radius_f = radius * BS;
1316 Go through current_objects; object is removed if:
1317 - object is not found in m_active_objects (this is actually an
1318 error condition; objects should be set m_removed=true and removed
1319 only after all clients have been informed about removal), or
1320 - object has m_removed=true, or
1321 - object is too far away
1323 for(core::map<u16, bool>::Iterator
1324 i = current_objects.getIterator();
1325 i.atEnd()==false; i++)
1327 u16 id = i.getNode()->getKey();
1328 ServerActiveObject *object = getActiveObject(id);
1331 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1332 <<" object in current_objects is NULL"<<std::endl;
1333 removed_objects.insert(id, false);
1337 if(object->m_removed)
1339 removed_objects.insert(id, false);
1343 // If transfer distance is unlimited, don't remove
1344 if(object->unlimitedTransferDistance())
1347 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1349 if(distance_f >= radius_f)
1351 removed_objects.insert(id, false);
1359 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1361 if(m_active_object_messages.size() == 0)
1362 return ActiveObjectMessage(0);
1364 return m_active_object_messages.pop_front();
1368 ************ Private methods *************
1371 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1375 if(object->getId() == 0){
1376 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1379 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1380 <<"no free ids available"<<std::endl;
1381 if(object->environmentDeletes())
1385 object->setId(new_id);
1388 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1389 <<"supplied with id "<<object->getId()<<std::endl;
1391 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1393 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1394 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1395 if(object->environmentDeletes())
1399 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1400 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1402 m_active_objects.insert(object->getId(), object);
1404 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1405 <<"Added id="<<object->getId()<<"; there are now "
1406 <<m_active_objects.size()<<" active objects."
1409 // Register reference in scripting api (must be done before post-init)
1410 scriptapi_add_object_reference(m_lua, object);
1411 // Post-initialize object
1412 object->addedToEnvironment();
1414 // Add static data to block
1415 if(object->isStaticAllowed())
1417 // Add static object to active static list of the block
1418 v3f objectpos = object->getBasePosition();
1419 std::string staticdata = object->getStaticData();
1420 StaticObject s_obj(object->getType(), objectpos, staticdata);
1421 // Add to the block where the object is located in
1422 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1423 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1426 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1427 object->m_static_exists = true;
1428 object->m_static_block = blockpos;
1431 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1432 "addActiveObjectRaw");
1435 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1436 <<"could not find block for storing id="<<object->getId()
1437 <<" statically"<<std::endl;
1441 return object->getId();
1445 Remove objects that satisfy (m_removed && m_known_by_count==0)
1447 void ServerEnvironment::removeRemovedObjects()
1449 core::list<u16> objects_to_remove;
1450 for(core::map<u16, ServerActiveObject*>::Iterator
1451 i = m_active_objects.getIterator();
1452 i.atEnd()==false; i++)
1454 u16 id = i.getNode()->getKey();
1455 ServerActiveObject* obj = i.getNode()->getValue();
1456 // This shouldn't happen but check it
1459 infostream<<"NULL object found in ServerEnvironment"
1460 <<" while finding removed objects. id="<<id<<std::endl;
1461 // Id to be removed from m_active_objects
1462 objects_to_remove.push_back(id);
1467 We will delete objects that are marked as removed or thatare
1468 waiting for deletion after deactivation
1470 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1474 Delete static data from block if is marked as removed
1476 if(obj->m_static_exists && obj->m_removed)
1478 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1481 block->m_static_objects.remove(id);
1482 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1483 "removeRemovedObjects");
1484 obj->m_static_exists = false;
1488 // If m_known_by_count > 0, don't actually remove.
1489 if(obj->m_known_by_count > 0)
1492 // Tell the object about removal
1493 obj->removingFromEnvironment();
1494 // Deregister in scripting api
1495 scriptapi_rm_object_reference(m_lua, obj);
1498 if(obj->environmentDeletes())
1500 // Id to be removed from m_active_objects
1501 objects_to_remove.push_back(id);
1503 // Remove references from m_active_objects
1504 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1505 i != objects_to_remove.end(); i++)
1507 m_active_objects.remove(*i);
1511 static void print_hexdump(std::ostream &o, const std::string &data)
1513 const int linelength = 16;
1514 for(int l=0; ; l++){
1515 int i0 = linelength * l;
1516 bool at_end = false;
1517 int thislinelength = linelength;
1518 if(i0 + thislinelength > (int)data.size()){
1519 thislinelength = data.size() - i0;
1522 for(int di=0; di<linelength; di++){
1525 if(di<thislinelength)
1526 snprintf(buf, 4, "%.2x ", data[i]);
1528 snprintf(buf, 4, " ");
1532 for(int di=0; di<thislinelength; di++){
1546 Convert stored objects from blocks near the players to active.
1548 void ServerEnvironment::activateObjects(MapBlock *block)
1552 // Ignore if no stored objects (to not set changed flag)
1553 if(block->m_static_objects.m_stored.size() == 0)
1555 verbosestream<<"ServerEnvironment::activateObjects(): "
1556 <<"activating objects of block "<<PP(block->getPos())
1557 <<" ("<<block->m_static_objects.m_stored.size()
1558 <<" objects)"<<std::endl;
1559 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1561 errorstream<<"suspiciously large amount of objects detected: "
1562 <<block->m_static_objects.m_stored.size()<<" in "
1563 <<PP(block->getPos())
1564 <<"; removing all of them."<<std::endl;
1565 // Clear stored list
1566 block->m_static_objects.m_stored.clear();
1567 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1568 "stored list cleared in activateObjects due to "
1569 "large amount of objects");
1572 // A list for objects that couldn't be converted to static for some
1573 // reason. They will be stored back.
1574 core::list<StaticObject> new_stored;
1575 // Loop through stored static objects
1576 for(core::list<StaticObject>::Iterator
1577 i = block->m_static_objects.m_stored.begin();
1578 i != block->m_static_objects.m_stored.end(); i++)
1580 /*infostream<<"Server: Creating an active object from "
1581 <<"static data"<<std::endl;*/
1582 StaticObject &s_obj = *i;
1583 // Create an active object from the data
1584 ServerActiveObject *obj = ServerActiveObject::create
1585 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1586 // If couldn't create object, store static data back.
1589 errorstream<<"ServerEnvironment::activateObjects(): "
1590 <<"failed to create active object from static object "
1591 <<"in block "<<PP(s_obj.pos/BS)
1592 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1593 print_hexdump(verbosestream, s_obj.data);
1595 new_stored.push_back(s_obj);
1598 verbosestream<<"ServerEnvironment::activateObjects(): "
1599 <<"activated static object pos="<<PP(s_obj.pos/BS)
1600 <<" type="<<(int)s_obj.type<<std::endl;
1601 // This will also add the object to the active static list
1602 addActiveObjectRaw(obj, false);
1604 // Clear stored list
1605 block->m_static_objects.m_stored.clear();
1606 // Add leftover failed stuff to stored list
1607 for(core::list<StaticObject>::Iterator
1608 i = new_stored.begin();
1609 i != new_stored.end(); i++)
1611 StaticObject &s_obj = *i;
1612 block->m_static_objects.m_stored.push_back(s_obj);
1615 Note: Block hasn't really been modified here.
1616 The objects have just been activated and moved from the stored
1617 static list to the active static list.
1618 As such, the block is essentially the same.
1619 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1620 Otherwise there would be a huge amount of unnecessary I/O.
1625 Convert objects that are not standing inside active blocks to static.
1627 If m_known_by_count != 0, active object is not deleted, but static
1628 data is still updated.
1630 If force_delete is set, active object is deleted nevertheless. It
1631 shall only be set so in the destructor of the environment.
1633 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1635 core::list<u16> objects_to_remove;
1636 for(core::map<u16, ServerActiveObject*>::Iterator
1637 i = m_active_objects.getIterator();
1638 i.atEnd()==false; i++)
1640 ServerActiveObject* obj = i.getNode()->getValue();
1643 // Do not deactivate if static data creation not allowed
1644 if(!force_delete && !obj->isStaticAllowed())
1647 // If pending deactivation, let removeRemovedObjects() do it
1648 if(!force_delete && obj->m_pending_deactivation)
1651 u16 id = i.getNode()->getKey();
1652 v3f objectpos = obj->getBasePosition();
1654 // The block in which the object resides in
1655 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1657 // If block is active, don't remove
1658 if(!force_delete && m_active_blocks.contains(blockpos_o))
1661 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1662 <<"deactivating object id="<<id<<" on inactive block "
1663 <<PP(blockpos_o)<<std::endl;
1665 // If known by some client, don't immediately delete.
1666 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1669 Update the static data
1672 if(obj->isStaticAllowed())
1674 // Create new static object
1675 std::string staticdata_new = obj->getStaticData();
1676 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1678 bool stays_in_same_block = false;
1679 bool data_changed = true;
1681 if(obj->m_static_exists){
1682 if(obj->m_static_block == blockpos_o)
1683 stays_in_same_block = true;
1685 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1687 core::map<u16, StaticObject>::Node *n =
1688 block->m_static_objects.m_active.find(id);
1690 StaticObject static_old = n->getValue();
1692 float save_movem = obj->getMinimumSavedMovement();
1694 if(static_old.data == staticdata_new &&
1695 (static_old.pos - objectpos).getLength() < save_movem)
1696 data_changed = false;
1698 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1699 <<"id="<<id<<" m_static_exists=true but "
1700 <<"static data doesn't actually exist in "
1701 <<PP(obj->m_static_block)<<std::endl;
1705 bool shall_be_written = (!stays_in_same_block || data_changed);
1707 // Delete old static object
1708 if(obj->m_static_exists)
1710 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1713 block->m_static_objects.remove(id);
1714 obj->m_static_exists = false;
1715 // Only mark block as modified if data changed considerably
1716 if(shall_be_written)
1717 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1718 "deactivateFarObjects: Static data "
1719 "changed considerably");
1723 // Add to the block where the object is located in
1724 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1725 // Get or generate the block
1726 MapBlock *block = m_map->emergeBlock(blockpos);
1730 if(block->m_static_objects.m_stored.size() >= 49){
1731 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1732 <<" statically but block "<<PP(blockpos)
1733 <<" already contains "
1734 <<block->m_static_objects.m_stored.size()
1735 <<" (over 49) objects."
1736 <<" Forcing delete."<<std::endl;
1737 force_delete = true;
1739 u16 new_id = pending_delete ? id : 0;
1740 block->m_static_objects.insert(new_id, s_obj);
1742 // Only mark block as modified if data changed considerably
1743 if(shall_be_written)
1744 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1745 "deactivateFarObjects: Static data "
1746 "changed considerably");
1748 obj->m_static_exists = true;
1749 obj->m_static_block = block->getPos();
1754 errorstream<<"ServerEnv: Could not find or generate "
1755 <<"a block for storing id="<<obj->getId()
1756 <<" statically"<<std::endl;
1763 If known by some client, set pending deactivation.
1764 Otherwise delete it immediately.
1767 if(pending_delete && !force_delete)
1769 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1770 <<"object id="<<id<<" is known by clients"
1771 <<"; not deleting yet"<<std::endl;
1773 obj->m_pending_deactivation = true;
1777 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1778 <<"object id="<<id<<" is not known by clients"
1779 <<"; deleting"<<std::endl;
1781 // Tell the object about removal
1782 obj->removingFromEnvironment();
1783 // Deregister in scripting api
1784 scriptapi_rm_object_reference(m_lua, obj);
1786 // Delete active object
1787 if(obj->environmentDeletes())
1789 // Id to be removed from m_active_objects
1790 objects_to_remove.push_back(id);
1793 // Remove references from m_active_objects
1794 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1795 i != objects_to_remove.end(); i++)
1797 m_active_objects.remove(*i);
1808 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1809 ITextureSource *texturesource, IGameDef *gamedef,
1810 IrrlichtDevice *irr):
1813 m_texturesource(texturesource),
1819 ClientEnvironment::~ClientEnvironment()
1821 // delete active objects
1822 for(core::map<u16, ClientActiveObject*>::Iterator
1823 i = m_active_objects.getIterator();
1824 i.atEnd()==false; i++)
1826 delete i.getNode()->getValue();
1833 void ClientEnvironment::addPlayer(Player *player)
1835 DSTACK(__FUNCTION_NAME);
1837 It is a failure if player is local and there already is a local
1840 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1842 Environment::addPlayer(player);
1845 LocalPlayer * ClientEnvironment::getLocalPlayer()
1847 for(core::list<Player*>::Iterator i = m_players.begin();
1848 i != m_players.end(); i++)
1850 Player *player = *i;
1851 if(player->isLocal())
1852 return (LocalPlayer*)player;
1857 void ClientEnvironment::step(float dtime)
1859 DSTACK(__FUNCTION_NAME);
1861 // Get some settings
1862 bool free_move = g_settings->getBool("free_move");
1863 bool footprints = g_settings->getBool("footprints");
1866 LocalPlayer *lplayer = getLocalPlayer();
1868 // collision info queue
1869 core::list<CollisionInfo> player_collisions;
1872 Get the speed the player is going
1874 bool is_climbing = lplayer->is_climbing;
1876 f32 player_speed = lplayer->getSpeed().getLength();
1879 Maximum position increment
1881 //f32 position_max_increment = 0.05*BS;
1882 f32 position_max_increment = 0.1*BS;
1884 // Maximum time increment (for collision detection etc)
1885 // time = distance / speed
1886 f32 dtime_max_increment = 1;
1887 if(player_speed > 0.001)
1888 dtime_max_increment = position_max_increment / player_speed;
1890 // Maximum time increment is 10ms or lower
1891 if(dtime_max_increment > 0.01)
1892 dtime_max_increment = 0.01;
1894 // Don't allow overly huge dtime
1898 f32 dtime_downcount = dtime;
1901 Stuff that has a maximum time increment
1910 if(dtime_downcount > dtime_max_increment)
1912 dtime_part = dtime_max_increment;
1913 dtime_downcount -= dtime_part;
1917 dtime_part = dtime_downcount;
1919 Setting this to 0 (no -=dtime_part) disables an infinite loop
1920 when dtime_part is so small that dtime_downcount -= dtime_part
1923 dtime_downcount = 0;
1931 v3f lplayerpos = lplayer->getPosition();
1934 if(free_move == false && is_climbing == false)
1937 v3f speed = lplayer->getSpeed();
1938 if(lplayer->swimming_up == false)
1939 speed.Y -= 9.81 * BS * dtime_part * 2;
1942 if(lplayer->in_water_stable || lplayer->in_water)
1944 f32 max_down = 2.0*BS;
1945 if(speed.Y < -max_down) speed.Y = -max_down;
1948 if(speed.getLength() > max)
1950 speed = speed / speed.getLength() * max;
1954 lplayer->setSpeed(speed);
1959 This also does collision detection.
1961 lplayer->move(dtime_part, *m_map, position_max_increment,
1962 &player_collisions);
1965 while(dtime_downcount > 0.001);
1967 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1969 for(core::list<CollisionInfo>::Iterator
1970 i = player_collisions.begin();
1971 i != player_collisions.end(); i++)
1973 CollisionInfo &info = *i;
1974 if(info.t == COLLISION_FALL)
1976 //f32 tolerance = BS*10; // 2 without damage
1977 f32 tolerance = BS*12; // 3 without damage
1979 if(info.speed > tolerance)
1981 f32 damage_f = (info.speed - tolerance)/BS*factor;
1982 u16 damage = (u16)(damage_f+0.5);
1983 if(lplayer->hp > damage)
1984 lplayer->hp -= damage;
1988 ClientEnvEvent event;
1989 event.type = CEE_PLAYER_DAMAGE;
1990 event.player_damage.amount = damage;
1991 event.player_damage.send_to_server = true;
1992 m_client_event_queue.push_back(event);
1998 A quick draft of lava damage
2000 if(m_lava_hurt_interval.step(dtime, 1.0))
2002 v3f pf = lplayer->getPosition();
2004 // Feet, middle and head
2005 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2006 MapNode n1 = m_map->getNodeNoEx(p1);
2007 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2008 MapNode n2 = m_map->getNodeNoEx(p2);
2009 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2010 MapNode n3 = m_map->getNodeNoEx(p2);
2012 u32 damage_per_second = 0;
2013 damage_per_second = MYMAX(damage_per_second,
2014 m_gamedef->ndef()->get(n1).damage_per_second);
2015 damage_per_second = MYMAX(damage_per_second,
2016 m_gamedef->ndef()->get(n2).damage_per_second);
2017 damage_per_second = MYMAX(damage_per_second,
2018 m_gamedef->ndef()->get(n3).damage_per_second);
2020 if(damage_per_second != 0)
2022 ClientEnvEvent event;
2023 event.type = CEE_PLAYER_DAMAGE;
2024 event.player_damage.amount = damage_per_second;
2025 event.player_damage.send_to_server = true;
2026 m_client_event_queue.push_back(event);
2031 Stuff that can be done in an arbitarily large dtime
2033 for(core::list<Player*>::Iterator i = m_players.begin();
2034 i != m_players.end(); i++)
2036 Player *player = *i;
2037 v3f playerpos = player->getPosition();
2040 Handle non-local players
2042 if(player->isLocal() == false)
2045 player->move(dtime, *m_map, 100*BS);
2049 // Update lighting on all players on client
2050 u8 light = LIGHT_MAX;
2053 v3s16 p = player->getLightPosition();
2054 MapNode n = m_map->getNode(p);
2055 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2057 catch(InvalidPositionException &e) {}
2058 player->updateLight(light);
2061 Add footsteps to grass
2065 // Get node that is at BS/4 under player
2066 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2068 MapNode n = m_map->getNode(bottompos);
2069 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2071 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2072 m_map->setNode(bottompos, n);
2073 // Update mesh on client
2074 if(m_map->mapType() == MAPTYPE_CLIENT)
2076 v3s16 p_blocks = getNodeBlockPos(bottompos);
2077 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2078 //b->updateMesh(getDayNightRatio());
2079 b->setMeshExpired(true);
2083 catch(InvalidPositionException &e)
2090 Step active objects and update lighting of them
2093 for(core::map<u16, ClientActiveObject*>::Iterator
2094 i = m_active_objects.getIterator();
2095 i.atEnd()==false; i++)
2097 ClientActiveObject* obj = i.getNode()->getValue();
2099 obj->step(dtime, this);
2101 if(m_active_object_light_update_interval.step(dtime, 0.21))
2104 //u8 light = LIGHT_MAX;
2108 v3s16 p = obj->getLightPosition();
2109 MapNode n = m_map->getNode(p);
2110 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2112 catch(InvalidPositionException &e) {}
2113 obj->updateLight(light);
2118 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2120 m_map->updateMeshes(blockpos, getDayNightRatio());
2123 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2125 m_map->expireMeshes(only_daynight_diffed);
2128 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2130 core::map<u16, ClientActiveObject*>::Node *n;
2131 n = m_active_objects.find(id);
2134 return n->getValue();
2137 bool isFreeClientActiveObjectId(u16 id,
2138 core::map<u16, ClientActiveObject*> &objects)
2143 for(core::map<u16, ClientActiveObject*>::Iterator
2144 i = objects.getIterator();
2145 i.atEnd()==false; i++)
2147 if(i.getNode()->getKey() == id)
2153 u16 getFreeClientActiveObjectId(
2154 core::map<u16, ClientActiveObject*> &objects)
2159 if(isFreeClientActiveObjectId(new_id, objects))
2169 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2172 if(object->getId() == 0)
2174 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2177 infostream<<"ClientEnvironment::addActiveObject(): "
2178 <<"no free ids available"<<std::endl;
2182 object->setId(new_id);
2184 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2186 infostream<<"ClientEnvironment::addActiveObject(): "
2187 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2191 infostream<<"ClientEnvironment::addActiveObject(): "
2192 <<"added (id="<<object->getId()<<")"<<std::endl;
2193 m_active_objects.insert(object->getId(), object);
2194 object->addToScene(m_smgr, m_texturesource, m_irr);
2195 { // Update lighting immediately
2199 v3s16 p = object->getLightPosition();
2200 MapNode n = m_map->getNode(p);
2201 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2203 catch(InvalidPositionException &e) {}
2204 object->updateLight(light);
2206 return object->getId();
2209 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2210 const std::string &init_data)
2212 ClientActiveObject* obj =
2213 ClientActiveObject::create(type, m_gamedef, this);
2216 infostream<<"ClientEnvironment::addActiveObject(): "
2217 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2224 obj->initialize(init_data);
2226 addActiveObject(obj);
2229 void ClientEnvironment::removeActiveObject(u16 id)
2231 infostream<<"ClientEnvironment::removeActiveObject(): "
2232 <<"id="<<id<<std::endl;
2233 ClientActiveObject* obj = getActiveObject(id);
2236 infostream<<"ClientEnvironment::removeActiveObject(): "
2237 <<"id="<<id<<" not found"<<std::endl;
2240 obj->removeFromScene();
2242 m_active_objects.remove(id);
2245 void ClientEnvironment::processActiveObjectMessage(u16 id,
2246 const std::string &data)
2248 ClientActiveObject* obj = getActiveObject(id);
2251 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2252 <<" got message for id="<<id<<", which doesn't exist."
2256 obj->processMessage(data);
2260 Callbacks for activeobjects
2263 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2265 LocalPlayer *lplayer = getLocalPlayer();
2269 if(lplayer->hp > damage)
2270 lplayer->hp -= damage;
2275 ClientEnvEvent event;
2276 event.type = CEE_PLAYER_DAMAGE;
2277 event.player_damage.amount = damage;
2278 event.player_damage.send_to_server = handle_hp;
2279 m_client_event_queue.push_back(event);
2283 Client likes to call these
2286 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2287 core::array<DistanceSortedActiveObject> &dest)
2289 for(core::map<u16, ClientActiveObject*>::Iterator
2290 i = m_active_objects.getIterator();
2291 i.atEnd()==false; i++)
2293 ClientActiveObject* obj = i.getNode()->getValue();
2295 f32 d = (obj->getPosition() - origin).getLength();
2300 DistanceSortedActiveObject dso(obj, d);
2302 dest.push_back(dso);
2306 ClientEnvEvent ClientEnvironment::getClientEvent()
2308 if(m_client_event_queue.size() == 0)
2310 ClientEnvEvent event;
2311 event.type = CEE_NONE;
2314 return m_client_event_queue.pop_front();
2317 #endif // #ifndef SERVER