3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
40 #include "serverremoteplayer.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 Environment::Environment():
49 Environment::~Environment()
52 for(core::list<Player*>::Iterator i = m_players.begin();
53 i != m_players.end(); i++)
59 void Environment::addPlayer(Player *player)
61 DSTACK(__FUNCTION_NAME);
63 Check that peer_ids are unique.
64 Also check that names are unique.
65 Exception: there can be multiple players with peer_id=0
67 // If peer id is non-zero, it has to be unique.
68 if(player->peer_id != 0)
69 assert(getPlayer(player->peer_id) == NULL);
70 // Name has to be unique.
71 assert(getPlayer(player->getName()) == NULL);
73 m_players.push_back(player);
76 void Environment::removePlayer(u16 peer_id)
78 DSTACK(__FUNCTION_NAME);
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id != peer_id)
89 // See if there is an another one
90 // (shouldn't be, but just to be sure)
95 Player * Environment::getPlayer(u16 peer_id)
97 for(core::list<Player*>::Iterator i = m_players.begin();
98 i != m_players.end(); i++)
101 if(player->peer_id == peer_id)
107 Player * Environment::getPlayer(const char *name)
109 for(core::list<Player*>::Iterator i = m_players.begin();
110 i != m_players.end(); i++)
113 if(strcmp(player->getName(), name) == 0)
119 Player * Environment::getRandomConnectedPlayer()
121 core::list<Player*> connected_players = getPlayers(true);
122 u32 chosen_one = myrand() % connected_players.size();
124 for(core::list<Player*>::Iterator
125 i = connected_players.begin();
126 i != connected_players.end(); i++)
138 Player * Environment::getNearestConnectedPlayer(v3f pos)
140 core::list<Player*> connected_players = getPlayers(true);
142 Player *nearest_player = NULL;
143 for(core::list<Player*>::Iterator
144 i = connected_players.begin();
145 i != connected_players.end(); i++)
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL)
152 nearest_player = player;
155 return nearest_player;
158 core::list<Player*> Environment::getPlayers()
163 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
165 core::list<Player*> newlist;
166 for(core::list<Player*>::Iterator
167 i = m_players.begin();
168 i != m_players.end(); i++)
172 if(ignore_disconnected)
174 // Ignore disconnected players
175 if(player->peer_id == 0)
179 newlist.push_back(player);
184 void Environment::printPlayers(std::ostream &o)
186 o<<"Players in environment:"<<std::endl;
187 for(core::list<Player*>::Iterator i = m_players.begin();
188 i != m_players.end(); i++)
191 o<<"Player peer_id="<<player->peer_id<<std::endl;
195 /*void Environment::setDayNightRatio(u32 r)
197 getDayNightRatio() = r;
200 u32 Environment::getDayNightRatio()
202 //return getDayNightRatio();
203 return time_to_daynight_ratio(m_time_of_day);
210 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
214 // Initialize timer to random value to spread processing
215 float itv = abm->getTriggerInterval();
216 itv = MYMAX(0.001, itv); // No less than 1ms
217 int minval = MYMAX(-0.51*itv, -60); // Clamp to
218 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
219 timer = myrand_range(minval, maxval);
226 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
229 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
230 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
231 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
238 void ActiveBlockList::update(core::list<v3s16> &active_positions,
240 core::map<v3s16, bool> &blocks_removed,
241 core::map<v3s16, bool> &blocks_added)
246 core::map<v3s16, bool> newlist;
247 for(core::list<v3s16>::Iterator i = active_positions.begin();
248 i != active_positions.end(); i++)
250 fillRadiusBlock(*i, radius, newlist);
254 Find out which blocks on the old list are not on the new list
256 // Go through old list
257 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
258 i.atEnd()==false; i++)
260 v3s16 p = i.getNode()->getKey();
261 // If not on new list, it's been removed
262 if(newlist.find(p) == NULL)
263 blocks_removed.insert(p, true);
267 Find out which blocks on the new list are not on the old list
269 // Go through new list
270 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
271 i.atEnd()==false; i++)
273 v3s16 p = i.getNode()->getKey();
274 // If not on old list, it's been added
275 if(m_list.find(p) == NULL)
276 blocks_added.insert(p, true);
283 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
284 i.atEnd()==false; i++)
286 v3s16 p = i.getNode()->getKey();
287 m_list.insert(p, true);
295 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
296 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
301 m_random_spawn_timer(3),
302 m_send_recommended_timer(0),
304 m_game_time_fraction_counter(0)
308 ServerEnvironment::~ServerEnvironment()
310 // Clear active block list.
311 // This makes the next one delete all active objects.
312 m_active_blocks.clear();
314 // Convert all objects to static and delete the active objects
315 deactivateFarObjects(true);
320 // Delete ActiveBlockModifiers
321 for(core::list<ABMWithState>::Iterator
322 i = m_abms.begin(); i != m_abms.end(); i++){
327 void ServerEnvironment::serializePlayers(const std::string &savedir)
329 std::string players_path = savedir + "/players";
330 fs::CreateDir(players_path);
332 core::map<Player*, bool> saved_players;
334 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
335 for(u32 i=0; i<player_files.size(); i++)
337 if(player_files[i].dir)
340 // Full path to this file
341 std::string path = players_path + "/" + player_files[i].name;
343 //infostream<<"Checking player file "<<path<<std::endl;
345 // Load player to see what is its name
346 ServerRemotePlayer testplayer(this);
348 // Open file and deserialize
349 std::ifstream is(path.c_str(), std::ios_base::binary);
350 if(is.good() == false)
352 infostream<<"Failed to read "<<path<<std::endl;
355 testplayer.deSerialize(is);
358 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
360 // Search for the player
361 std::string playername = testplayer.getName();
362 Player *player = getPlayer(playername.c_str());
365 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
369 //infostream<<"Found matching player, overwriting."<<std::endl;
371 // OK, found. Save player there.
373 // Open file and serialize
374 std::ofstream os(path.c_str(), std::ios_base::binary);
375 if(os.good() == false)
377 infostream<<"Failed to overwrite "<<path<<std::endl;
380 player->serialize(os);
381 saved_players.insert(player, true);
385 for(core::list<Player*>::Iterator i = m_players.begin();
386 i != m_players.end(); i++)
389 if(saved_players.find(player) != NULL)
391 /*infostream<<"Player "<<player->getName()
392 <<" was already saved."<<std::endl;*/
395 std::string playername = player->getName();
396 // Don't save unnamed player
399 //infostream<<"Not saving unnamed player."<<std::endl;
405 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
406 playername = "player";
407 std::string path = players_path + "/" + playername;
409 for(u32 i=0; i<1000; i++)
411 if(fs::PathExists(path) == false)
416 path = players_path + "/" + playername + itos(i);
420 infostream<<"Didn't find free file for player"<<std::endl;
425 /*infostream<<"Saving player "<<player->getName()<<" to "
427 // Open file and serialize
428 std::ofstream os(path.c_str(), std::ios_base::binary);
429 if(os.good() == false)
431 infostream<<"Failed to overwrite "<<path<<std::endl;
434 player->serialize(os);
435 saved_players.insert(player, true);
439 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
442 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
444 std::string players_path = savedir + "/players";
446 core::map<Player*, bool> saved_players;
448 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
449 for(u32 i=0; i<player_files.size(); i++)
451 if(player_files[i].dir)
454 // Full path to this file
455 std::string path = players_path + "/" + player_files[i].name;
457 infostream<<"Checking player file "<<path<<std::endl;
459 // Load player to see what is its name
460 ServerRemotePlayer testplayer(this);
462 // Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
464 if(is.good() == false)
466 infostream<<"Failed to read "<<path<<std::endl;
469 testplayer.deSerialize(is);
472 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
474 infostream<<"Not loading player with invalid name: "
475 <<testplayer.getName()<<std::endl;
478 infostream<<"Loaded test player with name "<<testplayer.getName()
481 // Search for the player
482 std::string playername = testplayer.getName();
483 Player *player = getPlayer(playername.c_str());
484 bool newplayer = false;
487 infostream<<"Is a new player"<<std::endl;
488 player = new ServerRemotePlayer(this);
492 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
496 infostream<<"Reading player "<<testplayer.getName()<<" from "
498 // Open file and deserialize
499 std::ifstream is(path.c_str(), std::ios_base::binary);
500 if(is.good() == false)
502 infostream<<"Failed to read "<<path<<std::endl;
505 srp->deSerialize(is);
506 srp->m_last_good_position = srp->getBasePosition();
507 srp->m_last_good_position_age = 0;
517 void ServerEnvironment::saveMeta(const std::string &savedir)
519 std::string path = savedir + "/env_meta.txt";
521 // Open file and serialize
522 std::ofstream os(path.c_str(), std::ios_base::binary);
523 if(os.good() == false)
525 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
527 throw SerializationError("Couldn't save env meta");
531 args.setU64("game_time", m_game_time);
532 args.setU64("time_of_day", getTimeOfDay());
537 void ServerEnvironment::loadMeta(const std::string &savedir)
539 std::string path = savedir + "/env_meta.txt";
541 // Open file and deserialize
542 std::ifstream is(path.c_str(), std::ios_base::binary);
543 if(is.good() == false)
545 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
547 throw SerializationError("Couldn't load env meta");
555 throw SerializationError
556 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
558 std::getline(is, line);
559 std::string trimmedline = trim(line);
560 if(trimmedline == "EnvArgsEnd")
562 args.parseConfigLine(line);
566 m_game_time = args.getU64("game_time");
567 }catch(SettingNotFoundException &e){
568 // Getting this is crucial, otherwise timestamps are useless
569 throw SerializationError("Couldn't load env meta game_time");
573 m_time_of_day = args.getU64("time_of_day");
574 }catch(SettingNotFoundException &e){
575 // This is not as important
576 m_time_of_day = 9000;
582 ActiveBlockModifier *abm;
584 std::set<content_t> required_neighbors;
590 ServerEnvironment *m_env;
591 std::map<content_t, std::list<ActiveABM> > m_aabms;
593 ABMHandler(core::list<ABMWithState> &abms,
594 float dtime_s, ServerEnvironment *env,
600 INodeDefManager *ndef = env->getGameDef()->ndef();
601 for(core::list<ABMWithState>::Iterator
602 i = abms.begin(); i != abms.end(); i++){
603 ActiveBlockModifier *abm = i->abm;
604 float trigger_interval = abm->getTriggerInterval();
605 if(trigger_interval < 0.001)
606 trigger_interval = 0.001;
607 float actual_interval = dtime_s;
610 if(i->timer < trigger_interval)
612 i->timer -= trigger_interval;
613 actual_interval = trigger_interval;
617 float intervals = actual_interval / trigger_interval;
618 float chance = abm->getTriggerChance();
621 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
625 std::set<std::string> required_neighbors_s
626 = abm->getRequiredNeighbors();
627 for(std::set<std::string>::iterator
628 i = required_neighbors_s.begin();
629 i != required_neighbors_s.end(); i++){
630 content_t c = ndef->getId(*i);
631 if(c == CONTENT_IGNORE)
633 aabm.required_neighbors.insert(c);
636 std::set<std::string> contents_s = abm->getTriggerContents();
637 for(std::set<std::string>::iterator
638 i = contents_s.begin(); i != contents_s.end(); i++){
639 content_t c = ndef->getId(*i);
640 if(c == CONTENT_IGNORE)
642 std::map<content_t, std::list<ActiveABM> >::iterator j;
644 if(j == m_aabms.end()){
645 std::list<ActiveABM> aabmlist;
646 m_aabms[c] = aabmlist;
649 j->second.push_back(aabm);
653 void apply(MapBlock *block)
658 ServerMap *map = &m_env->getServerMap();
661 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
662 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
663 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
665 MapNode n = block->getNodeNoEx(p0);
666 content_t c = n.getContent();
667 v3s16 p = p0 + block->getPosRelative();
669 std::map<content_t, std::list<ActiveABM> >::iterator j;
671 if(j == m_aabms.end())
674 for(std::list<ActiveABM>::iterator
675 i = j->second.begin(); i != j->second.end(); i++)
677 if(myrand() % i->chance != 0)
681 if(!i->required_neighbors.empty())
684 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
685 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
686 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
690 MapNode n = map->getNodeNoEx(p1);
691 content_t c = n.getContent();
692 std::set<content_t>::const_iterator k;
693 k = i->required_neighbors.find(c);
694 if(k != i->required_neighbors.end()){
698 // No required neighbor found
703 // Find out how many objects the block contains
704 u32 active_object_count = block->m_static_objects.m_active.size();
705 // Find out how many objects this and all the neighbors contain
706 u32 active_object_count_wider = 0;
707 for(s16 x=-1; x<=1; x++)
708 for(s16 y=-1; y<=1; y++)
709 for(s16 z=-1; z<=1; z++)
711 MapBlock *block2 = map->getBlockNoCreateNoEx(
712 block->getPos() + v3s16(x,y,z));
715 active_object_count_wider +=
716 block2->m_static_objects.m_active.size()
717 + block2->m_static_objects.m_stored.size();
720 // Call all the trigger variations
721 i->abm->trigger(m_env, p, n);
722 i->abm->trigger(m_env, p, n,
723 active_object_count, active_object_count_wider);
729 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
731 // Get time difference
733 u32 stamp = block->getTimestamp();
734 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
735 dtime_s = m_game_time - block->getTimestamp();
736 dtime_s += additional_dtime;
738 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
739 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
741 // Set current time as timestamp
742 block->setTimestampNoChangedFlag(m_game_time);
744 /*infostream<<"ServerEnvironment::activateBlock(): block is "
745 <<dtime_s<<" seconds old."<<std::endl;*/
747 // Activate stored objects
748 activateObjects(block);
751 bool changed = block->m_node_metadata->step((float)dtime_s);
755 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
756 event.p = block->getPos();
757 m_map->dispatchEvent(&event);
759 block->raiseModified(MOD_STATE_WRITE_NEEDED,
760 "node metadata modified in activateBlock");
763 /* Handle ActiveBlockModifiers */
764 ABMHandler abmhandler(m_abms, dtime_s, this, false);
765 abmhandler.apply(block);
768 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
770 m_abms.push_back(ABMWithState(abm));
773 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
775 std::set<u16> objects;
776 for(core::map<u16, ServerActiveObject*>::Iterator
777 i = m_active_objects.getIterator();
778 i.atEnd()==false; i++)
780 ServerActiveObject* obj = i.getNode()->getValue();
781 u16 id = i.getNode()->getKey();
782 v3f objectpos = obj->getBasePosition();
783 if(objectpos.getDistanceFrom(pos) > radius)
790 void ServerEnvironment::clearAllObjects()
792 infostream<<"ServerEnvironment::clearAllObjects(): "
793 <<"Removing all active objects"<<std::endl;
794 core::list<u16> objects_to_remove;
795 for(core::map<u16, ServerActiveObject*>::Iterator
796 i = m_active_objects.getIterator();
797 i.atEnd()==false; i++)
799 ServerActiveObject* obj = i.getNode()->getValue();
800 u16 id = i.getNode()->getKey();
801 v3f objectpos = obj->getBasePosition();
802 // Delete static object if block is loaded
803 if(obj->m_static_exists){
804 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
806 block->m_static_objects.remove(id);
807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
809 obj->m_static_exists = false;
812 // If known by some client, don't delete immediately
813 if(obj->m_known_by_count > 0){
814 obj->m_pending_deactivation = true;
815 obj->m_removed = true;
819 // Tell the object about removal
820 obj->removingFromEnvironment();
821 // Deregister in scripting api
822 scriptapi_rm_object_reference(m_lua, obj);
824 // Delete active object
825 if(obj->environmentDeletes())
827 // Id to be removed from m_active_objects
828 objects_to_remove.push_back(id);
830 // Remove references from m_active_objects
831 for(core::list<u16>::Iterator i = objects_to_remove.begin();
832 i != objects_to_remove.end(); i++)
834 m_active_objects.remove(*i);
837 core::list<v3s16> loadable_blocks;
838 infostream<<"ServerEnvironment::clearAllObjects(): "
839 <<"Listing all loadable blocks"<<std::endl;
840 m_map->listAllLoadableBlocks(loadable_blocks);
841 infostream<<"ServerEnvironment::clearAllObjects(): "
842 <<"Done listing all loadable blocks: "
843 <<loadable_blocks.size()
844 <<", now clearing"<<std::endl;
845 u32 report_interval = loadable_blocks.size() / 10;
846 u32 num_blocks_checked = 0;
847 u32 num_blocks_cleared = 0;
848 u32 num_objs_cleared = 0;
849 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
850 i != loadable_blocks.end(); i++)
853 MapBlock *block = m_map->emergeBlock(p, false);
855 errorstream<<"ServerEnvironment::clearAllObjects(): "
856 <<"Failed to emerge block "<<PP(p)<<std::endl;
859 u32 num_stored = block->m_static_objects.m_stored.size();
860 u32 num_active = block->m_static_objects.m_active.size();
861 if(num_stored != 0 || num_active != 0){
862 block->m_static_objects.m_stored.clear();
863 block->m_static_objects.m_active.clear();
864 block->raiseModified(MOD_STATE_WRITE_NEEDED,
866 num_objs_cleared += num_stored + num_active;
867 num_blocks_cleared++;
869 num_blocks_checked++;
871 if(num_blocks_checked % report_interval == 0){
872 float percent = 100.0 * (float)num_blocks_checked /
873 loadable_blocks.size();
874 infostream<<"ServerEnvironment::clearAllObjects(): "
875 <<"Cleared "<<num_objs_cleared<<" objects"
876 <<" in "<<num_blocks_cleared<<" blocks ("
877 <<percent<<"%)"<<std::endl;
880 infostream<<"ServerEnvironment::clearAllObjects(): "
881 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
882 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
885 void ServerEnvironment::step(float dtime)
887 DSTACK(__FUNCTION_NAME);
889 //TimeTaker timer("ServerEnv step");
892 bool footprints = g_settings->getBool("footprints");
898 m_game_time_fraction_counter += dtime;
899 u32 inc_i = (u32)m_game_time_fraction_counter;
900 m_game_time += inc_i;
901 m_game_time_fraction_counter -= (float)inc_i;
908 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
909 for(core::list<Player*>::Iterator i = m_players.begin();
910 i != m_players.end(); i++)
914 // Ignore disconnected players
915 if(player->peer_id == 0)
918 v3f playerpos = player->getPosition();
921 player->move(dtime, *m_map, 100*BS);
924 Add footsteps to grass
928 // Get node that is at BS/4 under player
929 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
931 MapNode n = m_map->getNode(bottompos);
932 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
934 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
935 m_map->setNode(bottompos, n);
938 catch(InvalidPositionException &e)
946 Manage active block list
948 if(m_active_blocks_management_interval.step(dtime, 2.0))
950 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
952 Get player block positions
954 core::list<v3s16> players_blockpos;
955 for(core::list<Player*>::Iterator
956 i = m_players.begin();
957 i != m_players.end(); i++)
960 // Ignore disconnected players
961 if(player->peer_id == 0)
963 v3s16 blockpos = getNodeBlockPos(
964 floatToInt(player->getPosition(), BS));
965 players_blockpos.push_back(blockpos);
969 Update list of active blocks, collecting changes
971 const s16 active_block_range = g_settings->getS16("active_block_range");
972 core::map<v3s16, bool> blocks_removed;
973 core::map<v3s16, bool> blocks_added;
974 m_active_blocks.update(players_blockpos, active_block_range,
975 blocks_removed, blocks_added);
978 Handle removed blocks
981 // Convert active objects that are no more in active blocks to static
982 deactivateFarObjects(false);
984 for(core::map<v3s16, bool>::Iterator
985 i = blocks_removed.getIterator();
986 i.atEnd()==false; i++)
988 v3s16 p = i.getNode()->getKey();
990 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
991 <<") became inactive"<<std::endl;*/
993 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
997 // Set current time as timestamp (and let it set ChangedFlag)
998 block->setTimestamp(m_game_time);
1005 for(core::map<v3s16, bool>::Iterator
1006 i = blocks_added.getIterator();
1007 i.atEnd()==false; i++)
1009 v3s16 p = i.getNode()->getKey();
1011 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1012 <<") became active"<<std::endl;*/
1014 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1016 // Block needs to be fetched first
1017 m_emerger->queueBlockEmerge(p, false);
1018 m_active_blocks.m_list.remove(p);
1022 activateBlock(block);
1027 Mess around in active blocks
1029 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1031 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1035 for(core::map<v3s16, bool>::Iterator
1036 i = m_active_blocks.m_list.getIterator();
1037 i.atEnd()==false; i++)
1039 v3s16 p = i.getNode()->getKey();
1041 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1042 <<") being handled"<<std::endl;*/
1044 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1048 // Reset block usage timer
1049 block->resetUsageTimer();
1051 // Set current time as timestamp
1052 block->setTimestampNoChangedFlag(m_game_time);
1053 // If time has changed much from the one on disk,
1054 // set block to be saved when it is unloaded
1055 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1056 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1057 "Timestamp older than 60s (step)");
1059 // Run node metadata
1060 bool changed = block->m_node_metadata->step(dtime);
1064 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1066 m_map->dispatchEvent(&event);
1068 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1069 "node metadata modified in step");
1074 const float abm_interval = 1.0;
1075 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1077 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1078 TimeTaker timer("modify in active blocks");
1080 // Initialize handling of ActiveBlockModifiers
1081 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1083 for(core::map<v3s16, bool>::Iterator
1084 i = m_active_blocks.m_list.getIterator();
1085 i.atEnd()==false; i++)
1087 v3s16 p = i.getNode()->getKey();
1089 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1090 <<") being handled"<<std::endl;*/
1092 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1096 // Set current time as timestamp
1097 block->setTimestampNoChangedFlag(m_game_time);
1099 /* Handle ActiveBlockModifiers */
1100 abmhandler.apply(block);
1103 u32 time_ms = timer.stop(true);
1104 u32 max_time_ms = 200;
1105 if(time_ms > max_time_ms){
1106 infostream<<"WARNING: active block modifiers took "
1107 <<time_ms<<"ms (longer than "
1108 <<max_time_ms<<"ms)"<<std::endl;
1113 Step script environment (run global on_step())
1115 scriptapi_environment_step(m_lua, dtime);
1121 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1122 //TimeTaker timer("Step active objects");
1124 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1126 // This helps the objects to send data at the same time
1127 bool send_recommended = false;
1128 m_send_recommended_timer += dtime;
1129 if(m_send_recommended_timer > getSendRecommendedInterval())
1131 m_send_recommended_timer -= getSendRecommendedInterval();
1132 send_recommended = true;
1135 for(core::map<u16, ServerActiveObject*>::Iterator
1136 i = m_active_objects.getIterator();
1137 i.atEnd()==false; i++)
1139 ServerActiveObject* obj = i.getNode()->getValue();
1140 // Remove non-peaceful mobs on peaceful mode
1141 if(g_settings->getBool("only_peaceful_mobs")){
1142 if(!obj->isPeaceful())
1143 obj->m_removed = true;
1145 // Don't step if is to be removed or stored statically
1146 if(obj->m_removed || obj->m_pending_deactivation)
1149 obj->step(dtime, send_recommended);
1150 // Read messages from object
1151 while(obj->m_messages_out.size() > 0)
1153 m_active_object_messages.push_back(
1154 obj->m_messages_out.pop_front());
1160 Manage active objects
1162 if(m_object_management_interval.step(dtime, 0.5))
1164 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1166 Remove objects that satisfy (m_removed && m_known_by_count==0)
1168 removeRemovedObjects();
1172 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1174 core::map<u16, ServerActiveObject*>::Node *n;
1175 n = m_active_objects.find(id);
1178 return n->getValue();
1181 bool isFreeServerActiveObjectId(u16 id,
1182 core::map<u16, ServerActiveObject*> &objects)
1187 for(core::map<u16, ServerActiveObject*>::Iterator
1188 i = objects.getIterator();
1189 i.atEnd()==false; i++)
1191 if(i.getNode()->getKey() == id)
1197 u16 getFreeServerActiveObjectId(
1198 core::map<u16, ServerActiveObject*> &objects)
1203 if(isFreeServerActiveObjectId(new_id, objects))
1213 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1216 u16 id = addActiveObjectRaw(object, true);
1220 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1224 v3f objectpos = obj->getBasePosition();
1226 // The block in which the object resides in
1227 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1230 Update the static data
1233 // Create new static object
1234 std::string staticdata = obj->getStaticData();
1235 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1236 // Add to the block where the object is located in
1237 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1238 // Get or generate the block
1239 MapBlock *block = m_map->emergeBlock(blockpos);
1241 bool succeeded = false;
1245 block->m_static_objects.insert(0, s_obj);
1246 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1247 "addActiveObjectAsStatic");
1251 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1252 <<"Could not find or generate "
1253 <<"a block for storing static object"<<std::endl;
1257 if(obj->environmentDeletes())
1264 Finds out what new objects have been added to
1265 inside a radius around a position
1267 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1268 core::map<u16, bool> ¤t_objects,
1269 core::map<u16, bool> &added_objects)
1271 v3f pos_f = intToFloat(pos, BS);
1272 f32 radius_f = radius * BS;
1274 Go through the object list,
1275 - discard m_removed objects,
1276 - discard objects that are too far away,
1277 - discard objects that are found in current_objects.
1278 - add remaining objects to added_objects
1280 for(core::map<u16, ServerActiveObject*>::Iterator
1281 i = m_active_objects.getIterator();
1282 i.atEnd()==false; i++)
1284 u16 id = i.getNode()->getKey();
1286 ServerActiveObject *object = i.getNode()->getValue();
1289 // Discard if removed
1290 if(object->m_removed)
1292 if(object->unlimitedTransferDistance() == false){
1293 // Discard if too far
1294 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1295 if(distance_f > radius_f)
1298 // Discard if already on current_objects
1299 core::map<u16, bool>::Node *n;
1300 n = current_objects.find(id);
1303 // Add to added_objects
1304 added_objects.insert(id, false);
1309 Finds out what objects have been removed from
1310 inside a radius around a position
1312 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1313 core::map<u16, bool> ¤t_objects,
1314 core::map<u16, bool> &removed_objects)
1316 v3f pos_f = intToFloat(pos, BS);
1317 f32 radius_f = radius * BS;
1319 Go through current_objects; object is removed if:
1320 - object is not found in m_active_objects (this is actually an
1321 error condition; objects should be set m_removed=true and removed
1322 only after all clients have been informed about removal), or
1323 - object has m_removed=true, or
1324 - object is too far away
1326 for(core::map<u16, bool>::Iterator
1327 i = current_objects.getIterator();
1328 i.atEnd()==false; i++)
1330 u16 id = i.getNode()->getKey();
1331 ServerActiveObject *object = getActiveObject(id);
1334 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1335 <<" object in current_objects is NULL"<<std::endl;
1336 removed_objects.insert(id, false);
1340 if(object->m_removed)
1342 removed_objects.insert(id, false);
1346 // If transfer distance is unlimited, don't remove
1347 if(object->unlimitedTransferDistance())
1350 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1352 if(distance_f >= radius_f)
1354 removed_objects.insert(id, false);
1362 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1364 if(m_active_object_messages.size() == 0)
1365 return ActiveObjectMessage(0);
1367 return m_active_object_messages.pop_front();
1371 ************ Private methods *************
1374 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1378 if(object->getId() == 0){
1379 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1382 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1383 <<"no free ids available"<<std::endl;
1384 if(object->environmentDeletes())
1388 object->setId(new_id);
1391 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1392 <<"supplied with id "<<object->getId()<<std::endl;
1394 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1396 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1397 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1398 if(object->environmentDeletes())
1402 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1403 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1405 m_active_objects.insert(object->getId(), object);
1407 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1408 <<"Added id="<<object->getId()<<"; there are now "
1409 <<m_active_objects.size()<<" active objects."
1412 // Register reference in scripting api (must be done before post-init)
1413 scriptapi_add_object_reference(m_lua, object);
1414 // Post-initialize object
1415 object->addedToEnvironment();
1417 // Add static data to block
1418 if(object->isStaticAllowed())
1420 // Add static object to active static list of the block
1421 v3f objectpos = object->getBasePosition();
1422 std::string staticdata = object->getStaticData();
1423 StaticObject s_obj(object->getType(), objectpos, staticdata);
1424 // Add to the block where the object is located in
1425 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1426 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1429 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1430 object->m_static_exists = true;
1431 object->m_static_block = blockpos;
1434 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1435 "addActiveObjectRaw");
1438 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1439 <<"could not find block for storing id="<<object->getId()
1440 <<" statically"<<std::endl;
1444 return object->getId();
1448 Remove objects that satisfy (m_removed && m_known_by_count==0)
1450 void ServerEnvironment::removeRemovedObjects()
1452 core::list<u16> objects_to_remove;
1453 for(core::map<u16, ServerActiveObject*>::Iterator
1454 i = m_active_objects.getIterator();
1455 i.atEnd()==false; i++)
1457 u16 id = i.getNode()->getKey();
1458 ServerActiveObject* obj = i.getNode()->getValue();
1459 // This shouldn't happen but check it
1462 infostream<<"NULL object found in ServerEnvironment"
1463 <<" while finding removed objects. id="<<id<<std::endl;
1464 // Id to be removed from m_active_objects
1465 objects_to_remove.push_back(id);
1470 We will delete objects that are marked as removed or thatare
1471 waiting for deletion after deactivation
1473 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1477 Delete static data from block if is marked as removed
1479 if(obj->m_static_exists && obj->m_removed)
1481 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1484 block->m_static_objects.remove(id);
1485 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1486 "removeRemovedObjects");
1487 obj->m_static_exists = false;
1491 // If m_known_by_count > 0, don't actually remove.
1492 if(obj->m_known_by_count > 0)
1495 // Tell the object about removal
1496 obj->removingFromEnvironment();
1497 // Deregister in scripting api
1498 scriptapi_rm_object_reference(m_lua, obj);
1501 if(obj->environmentDeletes())
1503 // Id to be removed from m_active_objects
1504 objects_to_remove.push_back(id);
1506 // Remove references from m_active_objects
1507 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1508 i != objects_to_remove.end(); i++)
1510 m_active_objects.remove(*i);
1514 static void print_hexdump(std::ostream &o, const std::string &data)
1516 const int linelength = 16;
1517 for(int l=0; ; l++){
1518 int i0 = linelength * l;
1519 bool at_end = false;
1520 int thislinelength = linelength;
1521 if(i0 + thislinelength > (int)data.size()){
1522 thislinelength = data.size() - i0;
1525 for(int di=0; di<linelength; di++){
1528 if(di<thislinelength)
1529 snprintf(buf, 4, "%.2x ", data[i]);
1531 snprintf(buf, 4, " ");
1535 for(int di=0; di<thislinelength; di++){
1549 Convert stored objects from blocks near the players to active.
1551 void ServerEnvironment::activateObjects(MapBlock *block)
1555 // Ignore if no stored objects (to not set changed flag)
1556 if(block->m_static_objects.m_stored.size() == 0)
1558 verbosestream<<"ServerEnvironment::activateObjects(): "
1559 <<"activating objects of block "<<PP(block->getPos())
1560 <<" ("<<block->m_static_objects.m_stored.size()
1561 <<" objects)"<<std::endl;
1562 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1564 errorstream<<"suspiciously large amount of objects detected: "
1565 <<block->m_static_objects.m_stored.size()<<" in "
1566 <<PP(block->getPos())
1567 <<"; removing all of them."<<std::endl;
1568 // Clear stored list
1569 block->m_static_objects.m_stored.clear();
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 "stored list cleared in activateObjects due to "
1572 "large amount of objects");
1575 // A list for objects that couldn't be converted to static for some
1576 // reason. They will be stored back.
1577 core::list<StaticObject> new_stored;
1578 // Loop through stored static objects
1579 for(core::list<StaticObject>::Iterator
1580 i = block->m_static_objects.m_stored.begin();
1581 i != block->m_static_objects.m_stored.end(); i++)
1583 /*infostream<<"Server: Creating an active object from "
1584 <<"static data"<<std::endl;*/
1585 StaticObject &s_obj = *i;
1586 // Create an active object from the data
1587 ServerActiveObject *obj = ServerActiveObject::create
1588 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1589 // If couldn't create object, store static data back.
1592 errorstream<<"ServerEnvironment::activateObjects(): "
1593 <<"failed to create active object from static object "
1594 <<"in block "<<PP(s_obj.pos/BS)
1595 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1596 print_hexdump(verbosestream, s_obj.data);
1598 new_stored.push_back(s_obj);
1601 verbosestream<<"ServerEnvironment::activateObjects(): "
1602 <<"activated static object pos="<<PP(s_obj.pos/BS)
1603 <<" type="<<(int)s_obj.type<<std::endl;
1604 // This will also add the object to the active static list
1605 addActiveObjectRaw(obj, false);
1607 // Clear stored list
1608 block->m_static_objects.m_stored.clear();
1609 // Add leftover failed stuff to stored list
1610 for(core::list<StaticObject>::Iterator
1611 i = new_stored.begin();
1612 i != new_stored.end(); i++)
1614 StaticObject &s_obj = *i;
1615 block->m_static_objects.m_stored.push_back(s_obj);
1618 Note: Block hasn't really been modified here.
1619 The objects have just been activated and moved from the stored
1620 static list to the active static list.
1621 As such, the block is essentially the same.
1622 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1623 Otherwise there would be a huge amount of unnecessary I/O.
1628 Convert objects that are not standing inside active blocks to static.
1630 If m_known_by_count != 0, active object is not deleted, but static
1631 data is still updated.
1633 If force_delete is set, active object is deleted nevertheless. It
1634 shall only be set so in the destructor of the environment.
1636 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1638 core::list<u16> objects_to_remove;
1639 for(core::map<u16, ServerActiveObject*>::Iterator
1640 i = m_active_objects.getIterator();
1641 i.atEnd()==false; i++)
1643 ServerActiveObject* obj = i.getNode()->getValue();
1646 // Do not deactivate if static data creation not allowed
1647 if(!force_delete && !obj->isStaticAllowed())
1650 // If pending deactivation, let removeRemovedObjects() do it
1651 if(!force_delete && obj->m_pending_deactivation)
1654 u16 id = i.getNode()->getKey();
1655 v3f objectpos = obj->getBasePosition();
1657 // The block in which the object resides in
1658 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1660 // If block is active, don't remove
1661 if(!force_delete && m_active_blocks.contains(blockpos_o))
1664 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1665 <<"deactivating object id="<<id<<" on inactive block "
1666 <<PP(blockpos_o)<<std::endl;
1668 // If known by some client, don't immediately delete.
1669 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1672 Update the static data
1675 if(obj->isStaticAllowed())
1677 // Create new static object
1678 std::string staticdata_new = obj->getStaticData();
1679 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1681 bool stays_in_same_block = false;
1682 bool data_changed = true;
1684 if(obj->m_static_exists){
1685 if(obj->m_static_block == blockpos_o)
1686 stays_in_same_block = true;
1688 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1690 core::map<u16, StaticObject>::Node *n =
1691 block->m_static_objects.m_active.find(id);
1693 StaticObject static_old = n->getValue();
1695 float save_movem = obj->getMinimumSavedMovement();
1697 if(static_old.data == staticdata_new &&
1698 (static_old.pos - objectpos).getLength() < save_movem)
1699 data_changed = false;
1701 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1702 <<"id="<<id<<" m_static_exists=true but "
1703 <<"static data doesn't actually exist in "
1704 <<PP(obj->m_static_block)<<std::endl;
1708 bool shall_be_written = (!stays_in_same_block || data_changed);
1710 // Delete old static object
1711 if(obj->m_static_exists)
1713 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1716 block->m_static_objects.remove(id);
1717 obj->m_static_exists = false;
1718 // Only mark block as modified if data changed considerably
1719 if(shall_be_written)
1720 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1721 "deactivateFarObjects: Static data "
1722 "changed considerably");
1726 // Add to the block where the object is located in
1727 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1728 // Get or generate the block
1729 MapBlock *block = m_map->emergeBlock(blockpos);
1733 if(block->m_static_objects.m_stored.size() >= 49){
1734 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1735 <<" statically but block "<<PP(blockpos)
1736 <<" already contains "
1737 <<block->m_static_objects.m_stored.size()
1738 <<" (over 49) objects."
1739 <<" Forcing delete."<<std::endl;
1740 force_delete = true;
1742 u16 new_id = pending_delete ? id : 0;
1743 block->m_static_objects.insert(new_id, s_obj);
1745 // Only mark block as modified if data changed considerably
1746 if(shall_be_written)
1747 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1748 "deactivateFarObjects: Static data "
1749 "changed considerably");
1751 obj->m_static_exists = true;
1752 obj->m_static_block = block->getPos();
1757 errorstream<<"ServerEnv: Could not find or generate "
1758 <<"a block for storing id="<<obj->getId()
1759 <<" statically"<<std::endl;
1766 If known by some client, set pending deactivation.
1767 Otherwise delete it immediately.
1770 if(pending_delete && !force_delete)
1772 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1773 <<"object id="<<id<<" is known by clients"
1774 <<"; not deleting yet"<<std::endl;
1776 obj->m_pending_deactivation = true;
1780 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1781 <<"object id="<<id<<" is not known by clients"
1782 <<"; deleting"<<std::endl;
1784 // Tell the object about removal
1785 obj->removingFromEnvironment();
1786 // Deregister in scripting api
1787 scriptapi_rm_object_reference(m_lua, obj);
1789 // Delete active object
1790 if(obj->environmentDeletes())
1792 // Id to be removed from m_active_objects
1793 objects_to_remove.push_back(id);
1796 // Remove references from m_active_objects
1797 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1798 i != objects_to_remove.end(); i++)
1800 m_active_objects.remove(*i);
1811 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1812 ITextureSource *texturesource, IGameDef *gamedef,
1813 IrrlichtDevice *irr):
1816 m_texturesource(texturesource),
1822 ClientEnvironment::~ClientEnvironment()
1824 // delete active objects
1825 for(core::map<u16, ClientActiveObject*>::Iterator
1826 i = m_active_objects.getIterator();
1827 i.atEnd()==false; i++)
1829 delete i.getNode()->getValue();
1836 void ClientEnvironment::addPlayer(Player *player)
1838 DSTACK(__FUNCTION_NAME);
1840 It is a failure if player is local and there already is a local
1843 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1845 Environment::addPlayer(player);
1848 LocalPlayer * ClientEnvironment::getLocalPlayer()
1850 for(core::list<Player*>::Iterator i = m_players.begin();
1851 i != m_players.end(); i++)
1853 Player *player = *i;
1854 if(player->isLocal())
1855 return (LocalPlayer*)player;
1860 void ClientEnvironment::step(float dtime)
1862 DSTACK(__FUNCTION_NAME);
1864 // Get some settings
1865 bool free_move = g_settings->getBool("free_move");
1866 bool footprints = g_settings->getBool("footprints");
1869 LocalPlayer *lplayer = getLocalPlayer();
1871 // collision info queue
1872 core::list<CollisionInfo> player_collisions;
1875 Get the speed the player is going
1877 bool is_climbing = lplayer->is_climbing;
1879 f32 player_speed = lplayer->getSpeed().getLength();
1882 Maximum position increment
1884 //f32 position_max_increment = 0.05*BS;
1885 f32 position_max_increment = 0.1*BS;
1887 // Maximum time increment (for collision detection etc)
1888 // time = distance / speed
1889 f32 dtime_max_increment = 1;
1890 if(player_speed > 0.001)
1891 dtime_max_increment = position_max_increment / player_speed;
1893 // Maximum time increment is 10ms or lower
1894 if(dtime_max_increment > 0.01)
1895 dtime_max_increment = 0.01;
1897 // Don't allow overly huge dtime
1901 f32 dtime_downcount = dtime;
1904 Stuff that has a maximum time increment
1913 if(dtime_downcount > dtime_max_increment)
1915 dtime_part = dtime_max_increment;
1916 dtime_downcount -= dtime_part;
1920 dtime_part = dtime_downcount;
1922 Setting this to 0 (no -=dtime_part) disables an infinite loop
1923 when dtime_part is so small that dtime_downcount -= dtime_part
1926 dtime_downcount = 0;
1934 v3f lplayerpos = lplayer->getPosition();
1937 if(free_move == false && is_climbing == false)
1940 v3f speed = lplayer->getSpeed();
1941 if(lplayer->swimming_up == false)
1942 speed.Y -= 9.81 * BS * dtime_part * 2;
1945 if(lplayer->in_water_stable || lplayer->in_water)
1947 f32 max_down = 2.0*BS;
1948 if(speed.Y < -max_down) speed.Y = -max_down;
1951 if(speed.getLength() > max)
1953 speed = speed / speed.getLength() * max;
1957 lplayer->setSpeed(speed);
1962 This also does collision detection.
1964 lplayer->move(dtime_part, *m_map, position_max_increment,
1965 &player_collisions);
1968 while(dtime_downcount > 0.001);
1970 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1972 for(core::list<CollisionInfo>::Iterator
1973 i = player_collisions.begin();
1974 i != player_collisions.end(); i++)
1976 CollisionInfo &info = *i;
1977 if(info.t == COLLISION_FALL)
1979 //f32 tolerance = BS*10; // 2 without damage
1980 f32 tolerance = BS*12; // 3 without damage
1982 if(info.speed > tolerance)
1984 f32 damage_f = (info.speed - tolerance)/BS*factor;
1985 u16 damage = (u16)(damage_f+0.5);
1986 if(lplayer->hp > damage)
1987 lplayer->hp -= damage;
1991 ClientEnvEvent event;
1992 event.type = CEE_PLAYER_DAMAGE;
1993 event.player_damage.amount = damage;
1994 event.player_damage.send_to_server = true;
1995 m_client_event_queue.push_back(event);
2001 A quick draft of lava damage
2003 if(m_lava_hurt_interval.step(dtime, 1.0))
2005 v3f pf = lplayer->getPosition();
2007 // Feet, middle and head
2008 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2009 MapNode n1 = m_map->getNodeNoEx(p1);
2010 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2011 MapNode n2 = m_map->getNodeNoEx(p2);
2012 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2013 MapNode n3 = m_map->getNodeNoEx(p2);
2015 u32 damage_per_second = 0;
2016 damage_per_second = MYMAX(damage_per_second,
2017 m_gamedef->ndef()->get(n1).damage_per_second);
2018 damage_per_second = MYMAX(damage_per_second,
2019 m_gamedef->ndef()->get(n2).damage_per_second);
2020 damage_per_second = MYMAX(damage_per_second,
2021 m_gamedef->ndef()->get(n3).damage_per_second);
2023 if(damage_per_second != 0)
2025 ClientEnvEvent event;
2026 event.type = CEE_PLAYER_DAMAGE;
2027 event.player_damage.amount = damage_per_second;
2028 event.player_damage.send_to_server = true;
2029 m_client_event_queue.push_back(event);
2034 Stuff that can be done in an arbitarily large dtime
2036 for(core::list<Player*>::Iterator i = m_players.begin();
2037 i != m_players.end(); i++)
2039 Player *player = *i;
2040 v3f playerpos = player->getPosition();
2043 Handle non-local players
2045 if(player->isLocal() == false)
2048 player->move(dtime, *m_map, 100*BS);
2052 // Update lighting on all players on client
2053 u8 light = LIGHT_MAX;
2056 v3s16 p = player->getLightPosition();
2057 MapNode n = m_map->getNode(p);
2058 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2060 catch(InvalidPositionException &e){
2061 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2063 player->updateLight(light);
2066 Add footsteps to grass
2070 // Get node that is at BS/4 under player
2071 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2073 MapNode n = m_map->getNode(bottompos);
2074 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2076 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2077 m_map->setNode(bottompos, n);
2078 // Update mesh on client
2079 if(m_map->mapType() == MAPTYPE_CLIENT)
2081 v3s16 p_blocks = getNodeBlockPos(bottompos);
2082 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2083 //b->updateMesh(getDayNightRatio());
2084 b->setMeshExpired(true);
2088 catch(InvalidPositionException &e)
2095 Step active objects and update lighting of them
2098 for(core::map<u16, ClientActiveObject*>::Iterator
2099 i = m_active_objects.getIterator();
2100 i.atEnd()==false; i++)
2102 ClientActiveObject* obj = i.getNode()->getValue();
2104 obj->step(dtime, this);
2106 if(m_active_object_light_update_interval.step(dtime, 0.21))
2112 v3s16 p = obj->getLightPosition();
2113 MapNode n = m_map->getNode(p);
2114 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2116 catch(InvalidPositionException &e){
2117 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2119 obj->updateLight(light);
2124 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2126 m_map->updateMeshes(blockpos, getDayNightRatio());
2129 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2131 m_map->expireMeshes(only_daynight_diffed);
2134 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2136 core::map<u16, ClientActiveObject*>::Node *n;
2137 n = m_active_objects.find(id);
2140 return n->getValue();
2143 bool isFreeClientActiveObjectId(u16 id,
2144 core::map<u16, ClientActiveObject*> &objects)
2149 for(core::map<u16, ClientActiveObject*>::Iterator
2150 i = objects.getIterator();
2151 i.atEnd()==false; i++)
2153 if(i.getNode()->getKey() == id)
2159 u16 getFreeClientActiveObjectId(
2160 core::map<u16, ClientActiveObject*> &objects)
2165 if(isFreeClientActiveObjectId(new_id, objects))
2175 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2178 if(object->getId() == 0)
2180 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2183 infostream<<"ClientEnvironment::addActiveObject(): "
2184 <<"no free ids available"<<std::endl;
2188 object->setId(new_id);
2190 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2192 infostream<<"ClientEnvironment::addActiveObject(): "
2193 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2197 infostream<<"ClientEnvironment::addActiveObject(): "
2198 <<"added (id="<<object->getId()<<")"<<std::endl;
2199 m_active_objects.insert(object->getId(), object);
2200 object->addToScene(m_smgr, m_texturesource, m_irr);
2201 { // Update lighting immediately
2205 v3s16 p = object->getLightPosition();
2206 MapNode n = m_map->getNode(p);
2207 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2209 catch(InvalidPositionException &e){
2210 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2212 object->updateLight(light);
2214 return object->getId();
2217 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2218 const std::string &init_data)
2220 ClientActiveObject* obj =
2221 ClientActiveObject::create(type, m_gamedef, this);
2224 infostream<<"ClientEnvironment::addActiveObject(): "
2225 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2232 obj->initialize(init_data);
2234 addActiveObject(obj);
2237 void ClientEnvironment::removeActiveObject(u16 id)
2239 infostream<<"ClientEnvironment::removeActiveObject(): "
2240 <<"id="<<id<<std::endl;
2241 ClientActiveObject* obj = getActiveObject(id);
2244 infostream<<"ClientEnvironment::removeActiveObject(): "
2245 <<"id="<<id<<" not found"<<std::endl;
2248 obj->removeFromScene();
2250 m_active_objects.remove(id);
2253 void ClientEnvironment::processActiveObjectMessage(u16 id,
2254 const std::string &data)
2256 ClientActiveObject* obj = getActiveObject(id);
2259 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2260 <<" got message for id="<<id<<", which doesn't exist."
2264 obj->processMessage(data);
2268 Callbacks for activeobjects
2271 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2273 LocalPlayer *lplayer = getLocalPlayer();
2277 if(lplayer->hp > damage)
2278 lplayer->hp -= damage;
2283 ClientEnvEvent event;
2284 event.type = CEE_PLAYER_DAMAGE;
2285 event.player_damage.amount = damage;
2286 event.player_damage.send_to_server = handle_hp;
2287 m_client_event_queue.push_back(event);
2291 Client likes to call these
2294 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2295 core::array<DistanceSortedActiveObject> &dest)
2297 for(core::map<u16, ClientActiveObject*>::Iterator
2298 i = m_active_objects.getIterator();
2299 i.atEnd()==false; i++)
2301 ClientActiveObject* obj = i.getNode()->getValue();
2303 f32 d = (obj->getPosition() - origin).getLength();
2308 DistanceSortedActiveObject dso(obj, d);
2310 dest.push_back(dso);
2314 ClientEnvEvent ClientEnvironment::getClientEvent()
2316 if(m_client_event_queue.size() == 0)
2318 ClientEnvEvent event;
2319 event.type = CEE_NONE;
2322 return m_client_event_queue.pop_front();
2325 #endif // #ifndef SERVER