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 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
498 // Open file and deserialize
499 std::ifstream is(path.c_str(), std::ios_base::binary);
500 if(is.good() == false)
502 infostream<<"Failed to read "<<path<<std::endl;
505 srp->deSerialize(is);
506 srp->m_last_good_position = srp->getBasePosition();
507 srp->m_last_good_position_age = 0;
517 void ServerEnvironment::saveMeta(const std::string &savedir)
519 std::string path = savedir + "/env_meta.txt";
521 // Open file and serialize
522 std::ofstream os(path.c_str(), std::ios_base::binary);
523 if(os.good() == false)
525 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
527 throw SerializationError("Couldn't save env meta");
531 args.setU64("game_time", m_game_time);
532 args.setU64("time_of_day", getTimeOfDay());
537 void ServerEnvironment::loadMeta(const std::string &savedir)
539 std::string path = savedir + "/env_meta.txt";
541 // Open file and deserialize
542 std::ifstream is(path.c_str(), std::ios_base::binary);
543 if(is.good() == false)
545 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
547 throw SerializationError("Couldn't load env meta");
555 throw SerializationError
556 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
558 std::getline(is, line);
559 std::string trimmedline = trim(line);
560 if(trimmedline == "EnvArgsEnd")
562 args.parseConfigLine(line);
566 m_game_time = args.getU64("game_time");
567 }catch(SettingNotFoundException &e){
568 // Getting this is crucial, otherwise timestamps are useless
569 throw SerializationError("Couldn't load env meta game_time");
573 m_time_of_day = args.getU64("time_of_day");
574 }catch(SettingNotFoundException &e){
575 // This is not as important
576 m_time_of_day = 9000;
582 ActiveBlockModifier *abm;
584 std::set<content_t> required_neighbors;
590 ServerEnvironment *m_env;
591 std::map<content_t, std::list<ActiveABM> > m_aabms;
593 ABMHandler(core::list<ABMWithState> &abms,
594 float dtime_s, ServerEnvironment *env,
600 INodeDefManager *ndef = env->getGameDef()->ndef();
601 for(core::list<ABMWithState>::Iterator
602 i = abms.begin(); i != abms.end(); i++){
603 ActiveBlockModifier *abm = i->abm;
604 float trigger_interval = abm->getTriggerInterval();
605 if(trigger_interval < 0.001)
606 trigger_interval = 0.001;
607 float actual_interval = dtime_s;
610 if(i->timer < trigger_interval)
612 i->timer -= trigger_interval;
613 actual_interval = trigger_interval;
617 float intervals = actual_interval / trigger_interval;
618 float chance = abm->getTriggerChance();
621 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
625 std::set<std::string> required_neighbors_s
626 = abm->getRequiredNeighbors();
627 for(std::set<std::string>::iterator
628 i = required_neighbors_s.begin();
629 i != required_neighbors_s.end(); i++){
630 content_t c = ndef->getId(*i);
631 if(c == CONTENT_IGNORE)
633 aabm.required_neighbors.insert(c);
636 std::set<std::string> contents_s = abm->getTriggerContents();
637 for(std::set<std::string>::iterator
638 i = contents_s.begin(); i != contents_s.end(); i++){
639 content_t c = ndef->getId(*i);
640 if(c == CONTENT_IGNORE)
642 std::map<content_t, std::list<ActiveABM> >::iterator j;
644 if(j == m_aabms.end()){
645 std::list<ActiveABM> aabmlist;
646 m_aabms[c] = aabmlist;
649 j->second.push_back(aabm);
653 void apply(MapBlock *block)
658 ServerMap *map = &m_env->getServerMap();
661 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
662 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
663 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
665 MapNode n = block->getNodeNoEx(p0);
666 content_t c = n.getContent();
667 v3s16 p = p0 + block->getPosRelative();
669 std::map<content_t, std::list<ActiveABM> >::iterator j;
671 if(j == m_aabms.end())
674 for(std::list<ActiveABM>::iterator
675 i = j->second.begin(); i != j->second.end(); i++)
677 if(myrand() % i->chance != 0)
681 if(!i->required_neighbors.empty())
684 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
685 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
686 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
690 MapNode n = map->getNodeNoEx(p1);
691 content_t c = n.getContent();
692 std::set<content_t>::const_iterator k;
693 k = i->required_neighbors.find(c);
694 if(k != i->required_neighbors.end()){
698 // No required neighbor found
703 // Find out how many objects the block contains
704 u32 active_object_count = block->m_static_objects.m_active.size();
705 // Find out how many objects this and all the neighbors contain
706 u32 active_object_count_wider = 0;
707 for(s16 x=-1; x<=1; x++)
708 for(s16 y=-1; y<=1; y++)
709 for(s16 z=-1; z<=1; z++)
711 MapBlock *block2 = map->getBlockNoCreateNoEx(
712 block->getPos() + v3s16(x,y,z));
715 active_object_count_wider +=
716 block2->m_static_objects.m_active.size()
717 + block2->m_static_objects.m_stored.size();
720 // Call all the trigger variations
721 i->abm->trigger(m_env, p, n);
722 i->abm->trigger(m_env, p, n,
723 active_object_count, active_object_count_wider);
729 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
731 // Get time difference
733 u32 stamp = block->getTimestamp();
734 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
735 dtime_s = m_game_time - block->getTimestamp();
736 dtime_s += additional_dtime;
738 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
739 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
741 // Set current time as timestamp
742 block->setTimestampNoChangedFlag(m_game_time);
744 /*infostream<<"ServerEnvironment::activateBlock(): block is "
745 <<dtime_s<<" seconds old."<<std::endl;*/
747 // Activate stored objects
748 activateObjects(block);
751 bool changed = block->m_node_metadata->step((float)dtime_s);
755 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
756 event.p = block->getPos();
757 m_map->dispatchEvent(&event);
759 block->raiseModified(MOD_STATE_WRITE_NEEDED,
760 "node metadata modified in activateBlock");
763 /* Handle ActiveBlockModifiers */
764 ABMHandler abmhandler(m_abms, dtime_s, this, false);
765 abmhandler.apply(block);
768 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
770 m_abms.push_back(ABMWithState(abm));
773 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
775 std::set<u16> objects;
776 for(core::map<u16, ServerActiveObject*>::Iterator
777 i = m_active_objects.getIterator();
778 i.atEnd()==false; i++)
780 ServerActiveObject* obj = i.getNode()->getValue();
781 u16 id = i.getNode()->getKey();
782 v3f objectpos = obj->getBasePosition();
783 if(objectpos.getDistanceFrom(pos) > radius)
790 void ServerEnvironment::clearAllObjects()
792 infostream<<"ServerEnvironment::clearAllObjects(): "
793 <<"Removing all active objects"<<std::endl;
794 core::list<u16> objects_to_remove;
795 for(core::map<u16, ServerActiveObject*>::Iterator
796 i = m_active_objects.getIterator();
797 i.atEnd()==false; i++)
799 ServerActiveObject* obj = i.getNode()->getValue();
800 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
802 u16 id = i.getNode()->getKey();
803 v3f objectpos = obj->getBasePosition();
804 // Delete static object if block is loaded
805 if(obj->m_static_exists){
806 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
808 block->m_static_objects.remove(id);
809 block->raiseModified(MOD_STATE_WRITE_NEEDED,
811 obj->m_static_exists = false;
814 // If known by some client, don't delete immediately
815 if(obj->m_known_by_count > 0){
816 obj->m_pending_deactivation = true;
817 obj->m_removed = true;
821 // Tell the object about removal
822 obj->removingFromEnvironment();
823 // Deregister in scripting api
824 scriptapi_rm_object_reference(m_lua, obj);
826 // Delete active object
827 if(obj->environmentDeletes())
829 // Id to be removed from m_active_objects
830 objects_to_remove.push_back(id);
832 // Remove references from m_active_objects
833 for(core::list<u16>::Iterator i = objects_to_remove.begin();
834 i != objects_to_remove.end(); i++)
836 m_active_objects.remove(*i);
839 core::list<v3s16> loadable_blocks;
840 infostream<<"ServerEnvironment::clearAllObjects(): "
841 <<"Listing all loadable blocks"<<std::endl;
842 m_map->listAllLoadableBlocks(loadable_blocks);
843 infostream<<"ServerEnvironment::clearAllObjects(): "
844 <<"Done listing all loadable blocks: "
845 <<loadable_blocks.size()
846 <<", now clearing"<<std::endl;
847 u32 report_interval = loadable_blocks.size() / 10;
848 u32 num_blocks_checked = 0;
849 u32 num_blocks_cleared = 0;
850 u32 num_objs_cleared = 0;
851 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
852 i != loadable_blocks.end(); i++)
855 MapBlock *block = m_map->emergeBlock(p, false);
857 errorstream<<"ServerEnvironment::clearAllObjects(): "
858 <<"Failed to emerge block "<<PP(p)<<std::endl;
861 u32 num_stored = block->m_static_objects.m_stored.size();
862 u32 num_active = block->m_static_objects.m_active.size();
863 if(num_stored != 0 || num_active != 0){
864 block->m_static_objects.m_stored.clear();
865 block->m_static_objects.m_active.clear();
866 block->raiseModified(MOD_STATE_WRITE_NEEDED,
868 num_objs_cleared += num_stored + num_active;
869 num_blocks_cleared++;
871 num_blocks_checked++;
873 if(num_blocks_checked % report_interval == 0){
874 float percent = 100.0 * (float)num_blocks_checked /
875 loadable_blocks.size();
876 infostream<<"ServerEnvironment::clearAllObjects(): "
877 <<"Cleared "<<num_objs_cleared<<" objects"
878 <<" in "<<num_blocks_cleared<<" blocks ("
879 <<percent<<"%)"<<std::endl;
882 infostream<<"ServerEnvironment::clearAllObjects(): "
883 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
884 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
887 void ServerEnvironment::step(float dtime)
889 DSTACK(__FUNCTION_NAME);
891 //TimeTaker timer("ServerEnv step");
897 m_game_time_fraction_counter += dtime;
898 u32 inc_i = (u32)m_game_time_fraction_counter;
899 m_game_time += inc_i;
900 m_game_time_fraction_counter -= (float)inc_i;
907 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
908 for(core::list<Player*>::Iterator i = m_players.begin();
909 i != m_players.end(); i++)
913 // Ignore disconnected players
914 if(player->peer_id == 0)
917 v3f playerpos = player->getPosition();
920 player->move(dtime, *m_map, 100*BS);
925 Manage active block list
927 if(m_active_blocks_management_interval.step(dtime, 2.0))
929 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
931 Get player block positions
933 core::list<v3s16> players_blockpos;
934 for(core::list<Player*>::Iterator
935 i = m_players.begin();
936 i != m_players.end(); i++)
939 // Ignore disconnected players
940 if(player->peer_id == 0)
942 v3s16 blockpos = getNodeBlockPos(
943 floatToInt(player->getPosition(), BS));
944 players_blockpos.push_back(blockpos);
948 Update list of active blocks, collecting changes
950 const s16 active_block_range = g_settings->getS16("active_block_range");
951 core::map<v3s16, bool> blocks_removed;
952 core::map<v3s16, bool> blocks_added;
953 m_active_blocks.update(players_blockpos, active_block_range,
954 blocks_removed, blocks_added);
957 Handle removed blocks
960 // Convert active objects that are no more in active blocks to static
961 deactivateFarObjects(false);
963 for(core::map<v3s16, bool>::Iterator
964 i = blocks_removed.getIterator();
965 i.atEnd()==false; i++)
967 v3s16 p = i.getNode()->getKey();
969 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
970 <<") became inactive"<<std::endl;*/
972 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
976 // Set current time as timestamp (and let it set ChangedFlag)
977 block->setTimestamp(m_game_time);
984 for(core::map<v3s16, bool>::Iterator
985 i = blocks_added.getIterator();
986 i.atEnd()==false; i++)
988 v3s16 p = i.getNode()->getKey();
990 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
991 <<") became active"<<std::endl;*/
993 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
995 // Block needs to be fetched first
996 m_emerger->queueBlockEmerge(p, false);
997 m_active_blocks.m_list.remove(p);
1001 activateBlock(block);
1006 Mess around in active blocks
1008 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1010 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1014 for(core::map<v3s16, bool>::Iterator
1015 i = m_active_blocks.m_list.getIterator();
1016 i.atEnd()==false; i++)
1018 v3s16 p = i.getNode()->getKey();
1020 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1021 <<") being handled"<<std::endl;*/
1023 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1027 // Reset block usage timer
1028 block->resetUsageTimer();
1030 // Set current time as timestamp
1031 block->setTimestampNoChangedFlag(m_game_time);
1032 // If time has changed much from the one on disk,
1033 // set block to be saved when it is unloaded
1034 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1035 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1036 "Timestamp older than 60s (step)");
1038 // Run node metadata
1039 bool changed = block->m_node_metadata->step(dtime);
1043 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1045 m_map->dispatchEvent(&event);
1047 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1048 "node metadata modified in step");
1053 const float abm_interval = 1.0;
1054 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1056 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1057 TimeTaker timer("modify in active blocks");
1059 // Initialize handling of ActiveBlockModifiers
1060 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1062 for(core::map<v3s16, bool>::Iterator
1063 i = m_active_blocks.m_list.getIterator();
1064 i.atEnd()==false; i++)
1066 v3s16 p = i.getNode()->getKey();
1068 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1069 <<") being handled"<<std::endl;*/
1071 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1075 // Set current time as timestamp
1076 block->setTimestampNoChangedFlag(m_game_time);
1078 /* Handle ActiveBlockModifiers */
1079 abmhandler.apply(block);
1082 u32 time_ms = timer.stop(true);
1083 u32 max_time_ms = 200;
1084 if(time_ms > max_time_ms){
1085 infostream<<"WARNING: active block modifiers took "
1086 <<time_ms<<"ms (longer than "
1087 <<max_time_ms<<"ms)"<<std::endl;
1092 Step script environment (run global on_step())
1094 scriptapi_environment_step(m_lua, dtime);
1100 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1101 //TimeTaker timer("Step active objects");
1103 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1105 // This helps the objects to send data at the same time
1106 bool send_recommended = false;
1107 m_send_recommended_timer += dtime;
1108 if(m_send_recommended_timer > getSendRecommendedInterval())
1110 m_send_recommended_timer -= getSendRecommendedInterval();
1111 send_recommended = true;
1114 for(core::map<u16, ServerActiveObject*>::Iterator
1115 i = m_active_objects.getIterator();
1116 i.atEnd()==false; i++)
1118 ServerActiveObject* obj = i.getNode()->getValue();
1119 // Remove non-peaceful mobs on peaceful mode
1120 if(g_settings->getBool("only_peaceful_mobs")){
1121 if(!obj->isPeaceful())
1122 obj->m_removed = true;
1124 // Don't step if is to be removed or stored statically
1125 if(obj->m_removed || obj->m_pending_deactivation)
1128 obj->step(dtime, send_recommended);
1129 // Read messages from object
1130 while(obj->m_messages_out.size() > 0)
1132 m_active_object_messages.push_back(
1133 obj->m_messages_out.pop_front());
1139 Manage active objects
1141 if(m_object_management_interval.step(dtime, 0.5))
1143 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1145 Remove objects that satisfy (m_removed && m_known_by_count==0)
1147 removeRemovedObjects();
1151 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1153 core::map<u16, ServerActiveObject*>::Node *n;
1154 n = m_active_objects.find(id);
1157 return n->getValue();
1160 bool isFreeServerActiveObjectId(u16 id,
1161 core::map<u16, ServerActiveObject*> &objects)
1166 for(core::map<u16, ServerActiveObject*>::Iterator
1167 i = objects.getIterator();
1168 i.atEnd()==false; i++)
1170 if(i.getNode()->getKey() == id)
1176 u16 getFreeServerActiveObjectId(
1177 core::map<u16, ServerActiveObject*> &objects)
1182 if(isFreeServerActiveObjectId(new_id, objects))
1192 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1195 u16 id = addActiveObjectRaw(object, true);
1199 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1203 v3f objectpos = obj->getBasePosition();
1205 // The block in which the object resides in
1206 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1209 Update the static data
1212 // Create new static object
1213 std::string staticdata = obj->getStaticData();
1214 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1215 // Add to the block where the object is located in
1216 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1217 // Get or generate the block
1218 MapBlock *block = m_map->emergeBlock(blockpos);
1220 bool succeeded = false;
1224 block->m_static_objects.insert(0, s_obj);
1225 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1226 "addActiveObjectAsStatic");
1230 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1231 <<"Could not find or generate "
1232 <<"a block for storing static object"<<std::endl;
1236 if(obj->environmentDeletes())
1243 Finds out what new objects have been added to
1244 inside a radius around a position
1246 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1247 core::map<u16, bool> ¤t_objects,
1248 core::map<u16, bool> &added_objects)
1250 v3f pos_f = intToFloat(pos, BS);
1251 f32 radius_f = radius * BS;
1253 Go through the object list,
1254 - discard m_removed objects,
1255 - discard objects that are too far away,
1256 - discard objects that are found in current_objects.
1257 - add remaining objects to added_objects
1259 for(core::map<u16, ServerActiveObject*>::Iterator
1260 i = m_active_objects.getIterator();
1261 i.atEnd()==false; i++)
1263 u16 id = i.getNode()->getKey();
1265 ServerActiveObject *object = i.getNode()->getValue();
1268 // Discard if removed
1269 if(object->m_removed)
1271 if(object->unlimitedTransferDistance() == false){
1272 // Discard if too far
1273 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1274 if(distance_f > radius_f)
1277 // Discard if already on current_objects
1278 core::map<u16, bool>::Node *n;
1279 n = current_objects.find(id);
1282 // Add to added_objects
1283 added_objects.insert(id, false);
1288 Finds out what objects have been removed from
1289 inside a radius around a position
1291 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1292 core::map<u16, bool> ¤t_objects,
1293 core::map<u16, bool> &removed_objects)
1295 v3f pos_f = intToFloat(pos, BS);
1296 f32 radius_f = radius * BS;
1298 Go through current_objects; object is removed if:
1299 - object is not found in m_active_objects (this is actually an
1300 error condition; objects should be set m_removed=true and removed
1301 only after all clients have been informed about removal), or
1302 - object has m_removed=true, or
1303 - object is too far away
1305 for(core::map<u16, bool>::Iterator
1306 i = current_objects.getIterator();
1307 i.atEnd()==false; i++)
1309 u16 id = i.getNode()->getKey();
1310 ServerActiveObject *object = getActiveObject(id);
1313 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1314 <<" object in current_objects is NULL"<<std::endl;
1315 removed_objects.insert(id, false);
1319 if(object->m_removed)
1321 removed_objects.insert(id, false);
1325 // If transfer distance is unlimited, don't remove
1326 if(object->unlimitedTransferDistance())
1329 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1331 if(distance_f >= radius_f)
1333 removed_objects.insert(id, false);
1341 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1343 if(m_active_object_messages.size() == 0)
1344 return ActiveObjectMessage(0);
1346 return m_active_object_messages.pop_front();
1350 ************ Private methods *************
1353 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1357 if(object->getId() == 0){
1358 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1361 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1362 <<"no free ids available"<<std::endl;
1363 if(object->environmentDeletes())
1367 object->setId(new_id);
1370 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1371 <<"supplied with id "<<object->getId()<<std::endl;
1373 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1375 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1376 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1377 if(object->environmentDeletes())
1381 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1382 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1384 m_active_objects.insert(object->getId(), object);
1386 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1387 <<"Added id="<<object->getId()<<"; there are now "
1388 <<m_active_objects.size()<<" active objects."
1391 // Register reference in scripting api (must be done before post-init)
1392 scriptapi_add_object_reference(m_lua, object);
1393 // Post-initialize object
1394 object->addedToEnvironment();
1396 // Add static data to block
1397 if(object->isStaticAllowed())
1399 // Add static object to active static list of the block
1400 v3f objectpos = object->getBasePosition();
1401 std::string staticdata = object->getStaticData();
1402 StaticObject s_obj(object->getType(), objectpos, staticdata);
1403 // Add to the block where the object is located in
1404 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1405 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1408 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1409 object->m_static_exists = true;
1410 object->m_static_block = blockpos;
1413 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1414 "addActiveObjectRaw");
1417 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1418 <<"could not find block for storing id="<<object->getId()
1419 <<" statically"<<std::endl;
1423 return object->getId();
1427 Remove objects that satisfy (m_removed && m_known_by_count==0)
1429 void ServerEnvironment::removeRemovedObjects()
1431 core::list<u16> objects_to_remove;
1432 for(core::map<u16, ServerActiveObject*>::Iterator
1433 i = m_active_objects.getIterator();
1434 i.atEnd()==false; i++)
1436 u16 id = i.getNode()->getKey();
1437 ServerActiveObject* obj = i.getNode()->getValue();
1438 // This shouldn't happen but check it
1441 infostream<<"NULL object found in ServerEnvironment"
1442 <<" while finding removed objects. id="<<id<<std::endl;
1443 // Id to be removed from m_active_objects
1444 objects_to_remove.push_back(id);
1449 We will delete objects that are marked as removed or thatare
1450 waiting for deletion after deactivation
1452 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1456 Delete static data from block if is marked as removed
1458 if(obj->m_static_exists && obj->m_removed)
1460 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1463 block->m_static_objects.remove(id);
1464 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1465 "removeRemovedObjects");
1466 obj->m_static_exists = false;
1470 // If m_known_by_count > 0, don't actually remove.
1471 if(obj->m_known_by_count > 0)
1474 // Tell the object about removal
1475 obj->removingFromEnvironment();
1476 // Deregister in scripting api
1477 scriptapi_rm_object_reference(m_lua, obj);
1480 if(obj->environmentDeletes())
1482 // Id to be removed from m_active_objects
1483 objects_to_remove.push_back(id);
1485 // Remove references from m_active_objects
1486 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1487 i != objects_to_remove.end(); i++)
1489 m_active_objects.remove(*i);
1493 static void print_hexdump(std::ostream &o, const std::string &data)
1495 const int linelength = 16;
1496 for(int l=0; ; l++){
1497 int i0 = linelength * l;
1498 bool at_end = false;
1499 int thislinelength = linelength;
1500 if(i0 + thislinelength > (int)data.size()){
1501 thislinelength = data.size() - i0;
1504 for(int di=0; di<linelength; di++){
1507 if(di<thislinelength)
1508 snprintf(buf, 4, "%.2x ", data[i]);
1510 snprintf(buf, 4, " ");
1514 for(int di=0; di<thislinelength; di++){
1528 Convert stored objects from blocks near the players to active.
1530 void ServerEnvironment::activateObjects(MapBlock *block)
1534 // Ignore if no stored objects (to not set changed flag)
1535 if(block->m_static_objects.m_stored.size() == 0)
1537 verbosestream<<"ServerEnvironment::activateObjects(): "
1538 <<"activating objects of block "<<PP(block->getPos())
1539 <<" ("<<block->m_static_objects.m_stored.size()
1540 <<" objects)"<<std::endl;
1541 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1543 errorstream<<"suspiciously large amount of objects detected: "
1544 <<block->m_static_objects.m_stored.size()<<" in "
1545 <<PP(block->getPos())
1546 <<"; removing all of them."<<std::endl;
1547 // Clear stored list
1548 block->m_static_objects.m_stored.clear();
1549 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1550 "stored list cleared in activateObjects due to "
1551 "large amount of objects");
1554 // A list for objects that couldn't be converted to static for some
1555 // reason. They will be stored back.
1556 core::list<StaticObject> new_stored;
1557 // Loop through stored static objects
1558 for(core::list<StaticObject>::Iterator
1559 i = block->m_static_objects.m_stored.begin();
1560 i != block->m_static_objects.m_stored.end(); i++)
1562 /*infostream<<"Server: Creating an active object from "
1563 <<"static data"<<std::endl;*/
1564 StaticObject &s_obj = *i;
1565 // Create an active object from the data
1566 ServerActiveObject *obj = ServerActiveObject::create
1567 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1568 // If couldn't create object, store static data back.
1571 errorstream<<"ServerEnvironment::activateObjects(): "
1572 <<"failed to create active object from static object "
1573 <<"in block "<<PP(s_obj.pos/BS)
1574 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1575 print_hexdump(verbosestream, s_obj.data);
1577 new_stored.push_back(s_obj);
1580 verbosestream<<"ServerEnvironment::activateObjects(): "
1581 <<"activated static object pos="<<PP(s_obj.pos/BS)
1582 <<" type="<<(int)s_obj.type<<std::endl;
1583 // This will also add the object to the active static list
1584 addActiveObjectRaw(obj, false);
1586 // Clear stored list
1587 block->m_static_objects.m_stored.clear();
1588 // Add leftover failed stuff to stored list
1589 for(core::list<StaticObject>::Iterator
1590 i = new_stored.begin();
1591 i != new_stored.end(); i++)
1593 StaticObject &s_obj = *i;
1594 block->m_static_objects.m_stored.push_back(s_obj);
1597 Note: Block hasn't really been modified here.
1598 The objects have just been activated and moved from the stored
1599 static list to the active static list.
1600 As such, the block is essentially the same.
1601 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1602 Otherwise there would be a huge amount of unnecessary I/O.
1607 Convert objects that are not standing inside active blocks to static.
1609 If m_known_by_count != 0, active object is not deleted, but static
1610 data is still updated.
1612 If force_delete is set, active object is deleted nevertheless. It
1613 shall only be set so in the destructor of the environment.
1615 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1617 core::list<u16> objects_to_remove;
1618 for(core::map<u16, ServerActiveObject*>::Iterator
1619 i = m_active_objects.getIterator();
1620 i.atEnd()==false; i++)
1622 ServerActiveObject* obj = i.getNode()->getValue();
1625 // Do not deactivate if static data creation not allowed
1626 if(!force_delete && !obj->isStaticAllowed())
1629 // If pending deactivation, let removeRemovedObjects() do it
1630 if(!force_delete && obj->m_pending_deactivation)
1633 u16 id = i.getNode()->getKey();
1634 v3f objectpos = obj->getBasePosition();
1636 // The block in which the object resides in
1637 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1639 // If block is active, don't remove
1640 if(!force_delete && m_active_blocks.contains(blockpos_o))
1643 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1644 <<"deactivating object id="<<id<<" on inactive block "
1645 <<PP(blockpos_o)<<std::endl;
1647 // If known by some client, don't immediately delete.
1648 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1651 Update the static data
1654 if(obj->isStaticAllowed())
1656 // Create new static object
1657 std::string staticdata_new = obj->getStaticData();
1658 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1660 bool stays_in_same_block = false;
1661 bool data_changed = true;
1663 if(obj->m_static_exists){
1664 if(obj->m_static_block == blockpos_o)
1665 stays_in_same_block = true;
1667 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1669 core::map<u16, StaticObject>::Node *n =
1670 block->m_static_objects.m_active.find(id);
1672 StaticObject static_old = n->getValue();
1674 float save_movem = obj->getMinimumSavedMovement();
1676 if(static_old.data == staticdata_new &&
1677 (static_old.pos - objectpos).getLength() < save_movem)
1678 data_changed = false;
1680 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1681 <<"id="<<id<<" m_static_exists=true but "
1682 <<"static data doesn't actually exist in "
1683 <<PP(obj->m_static_block)<<std::endl;
1687 bool shall_be_written = (!stays_in_same_block || data_changed);
1689 // Delete old static object
1690 if(obj->m_static_exists)
1692 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1695 block->m_static_objects.remove(id);
1696 obj->m_static_exists = false;
1697 // Only mark block as modified if data changed considerably
1698 if(shall_be_written)
1699 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1700 "deactivateFarObjects: Static data "
1701 "changed considerably");
1705 // Add to the block where the object is located in
1706 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1707 // Get or generate the block
1708 MapBlock *block = m_map->emergeBlock(blockpos);
1712 if(block->m_static_objects.m_stored.size() >= 49){
1713 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1714 <<" statically but block "<<PP(blockpos)
1715 <<" already contains "
1716 <<block->m_static_objects.m_stored.size()
1717 <<" (over 49) objects."
1718 <<" Forcing delete."<<std::endl;
1719 force_delete = true;
1721 u16 new_id = pending_delete ? id : 0;
1722 block->m_static_objects.insert(new_id, s_obj);
1724 // Only mark block as modified if data changed considerably
1725 if(shall_be_written)
1726 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1727 "deactivateFarObjects: Static data "
1728 "changed considerably");
1730 obj->m_static_exists = true;
1731 obj->m_static_block = block->getPos();
1736 errorstream<<"ServerEnv: Could not find or generate "
1737 <<"a block for storing id="<<obj->getId()
1738 <<" statically"<<std::endl;
1745 If known by some client, set pending deactivation.
1746 Otherwise delete it immediately.
1749 if(pending_delete && !force_delete)
1751 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1752 <<"object id="<<id<<" is known by clients"
1753 <<"; not deleting yet"<<std::endl;
1755 obj->m_pending_deactivation = true;
1759 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1760 <<"object id="<<id<<" is not known by clients"
1761 <<"; deleting"<<std::endl;
1763 // Tell the object about removal
1764 obj->removingFromEnvironment();
1765 // Deregister in scripting api
1766 scriptapi_rm_object_reference(m_lua, obj);
1768 // Delete active object
1769 if(obj->environmentDeletes())
1771 // Id to be removed from m_active_objects
1772 objects_to_remove.push_back(id);
1775 // Remove references from m_active_objects
1776 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1777 i != objects_to_remove.end(); i++)
1779 m_active_objects.remove(*i);
1786 #include "clientsimpleobject.h"
1792 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1793 ITextureSource *texturesource, IGameDef *gamedef,
1794 IrrlichtDevice *irr):
1797 m_texturesource(texturesource),
1803 ClientEnvironment::~ClientEnvironment()
1805 // delete active objects
1806 for(core::map<u16, ClientActiveObject*>::Iterator
1807 i = m_active_objects.getIterator();
1808 i.atEnd()==false; i++)
1810 delete i.getNode()->getValue();
1813 for(core::list<ClientSimpleObject*>::Iterator
1814 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1823 void ClientEnvironment::addPlayer(Player *player)
1825 DSTACK(__FUNCTION_NAME);
1827 It is a failure if player is local and there already is a local
1830 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1832 Environment::addPlayer(player);
1835 LocalPlayer * ClientEnvironment::getLocalPlayer()
1837 for(core::list<Player*>::Iterator i = m_players.begin();
1838 i != m_players.end(); i++)
1840 Player *player = *i;
1841 if(player->isLocal())
1842 return (LocalPlayer*)player;
1847 void ClientEnvironment::step(float dtime)
1849 DSTACK(__FUNCTION_NAME);
1851 // Get some settings
1852 bool free_move = g_settings->getBool("free_move");
1855 LocalPlayer *lplayer = getLocalPlayer();
1857 // collision info queue
1858 core::list<CollisionInfo> player_collisions;
1861 Get the speed the player is going
1863 bool is_climbing = lplayer->is_climbing;
1865 f32 player_speed = lplayer->getSpeed().getLength();
1868 Maximum position increment
1870 //f32 position_max_increment = 0.05*BS;
1871 f32 position_max_increment = 0.1*BS;
1873 // Maximum time increment (for collision detection etc)
1874 // time = distance / speed
1875 f32 dtime_max_increment = 1;
1876 if(player_speed > 0.001)
1877 dtime_max_increment = position_max_increment / player_speed;
1879 // Maximum time increment is 10ms or lower
1880 if(dtime_max_increment > 0.01)
1881 dtime_max_increment = 0.01;
1883 // Don't allow overly huge dtime
1887 f32 dtime_downcount = dtime;
1890 Stuff that has a maximum time increment
1899 if(dtime_downcount > dtime_max_increment)
1901 dtime_part = dtime_max_increment;
1902 dtime_downcount -= dtime_part;
1906 dtime_part = dtime_downcount;
1908 Setting this to 0 (no -=dtime_part) disables an infinite loop
1909 when dtime_part is so small that dtime_downcount -= dtime_part
1912 dtime_downcount = 0;
1920 v3f lplayerpos = lplayer->getPosition();
1923 if(free_move == false && is_climbing == false)
1926 v3f speed = lplayer->getSpeed();
1927 if(lplayer->swimming_up == false)
1928 speed.Y -= 9.81 * BS * dtime_part * 2;
1931 if(lplayer->in_water_stable || lplayer->in_water)
1933 f32 max_down = 2.0*BS;
1934 if(speed.Y < -max_down) speed.Y = -max_down;
1937 if(speed.getLength() > max)
1939 speed = speed / speed.getLength() * max;
1943 lplayer->setSpeed(speed);
1948 This also does collision detection.
1950 lplayer->move(dtime_part, *m_map, position_max_increment,
1951 &player_collisions);
1954 while(dtime_downcount > 0.001);
1956 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1958 for(core::list<CollisionInfo>::Iterator
1959 i = player_collisions.begin();
1960 i != player_collisions.end(); i++)
1962 CollisionInfo &info = *i;
1963 if(info.t == COLLISION_FALL)
1965 //f32 tolerance = BS*10; // 2 without damage
1966 f32 tolerance = BS*12; // 3 without damage
1968 if(info.speed > tolerance)
1970 f32 damage_f = (info.speed - tolerance)/BS*factor;
1971 u16 damage = (u16)(damage_f+0.5);
1972 damageLocalPlayer(damage, true);
1978 A quick draft of lava damage
1980 if(m_lava_hurt_interval.step(dtime, 1.0))
1982 v3f pf = lplayer->getPosition();
1984 // Feet, middle and head
1985 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1986 MapNode n1 = m_map->getNodeNoEx(p1);
1987 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1988 MapNode n2 = m_map->getNodeNoEx(p2);
1989 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1990 MapNode n3 = m_map->getNodeNoEx(p2);
1992 u32 damage_per_second = 0;
1993 damage_per_second = MYMAX(damage_per_second,
1994 m_gamedef->ndef()->get(n1).damage_per_second);
1995 damage_per_second = MYMAX(damage_per_second,
1996 m_gamedef->ndef()->get(n2).damage_per_second);
1997 damage_per_second = MYMAX(damage_per_second,
1998 m_gamedef->ndef()->get(n3).damage_per_second);
2000 if(damage_per_second != 0)
2002 damageLocalPlayer(damage_per_second, true);
2007 Stuff that can be done in an arbitarily large dtime
2009 for(core::list<Player*>::Iterator i = m_players.begin();
2010 i != m_players.end(); i++)
2012 Player *player = *i;
2013 v3f playerpos = player->getPosition();
2016 Handle non-local players
2018 if(player->isLocal() == false)
2021 player->move(dtime, *m_map, 100*BS);
2025 // Update lighting on all players on client
2026 u8 light = LIGHT_MAX;
2029 v3s16 p = player->getLightPosition();
2030 MapNode n = m_map->getNode(p);
2031 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2033 catch(InvalidPositionException &e){
2034 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2036 player->updateLight(light);
2040 Step active objects and update lighting of them
2043 for(core::map<u16, ClientActiveObject*>::Iterator
2044 i = m_active_objects.getIterator();
2045 i.atEnd()==false; i++)
2047 ClientActiveObject* obj = i.getNode()->getValue();
2049 obj->step(dtime, this);
2051 if(m_active_object_light_update_interval.step(dtime, 0.21))
2057 v3s16 p = obj->getLightPosition();
2058 MapNode n = m_map->getNode(p);
2059 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2061 catch(InvalidPositionException &e){
2062 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2064 obj->updateLight(light);
2069 Step and handle simple objects
2071 for(core::list<ClientSimpleObject*>::Iterator
2072 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2074 ClientSimpleObject *simple = *i;
2075 core::list<ClientSimpleObject*>::Iterator cur = i;
2077 simple->step(dtime);
2078 if(simple->m_to_be_removed){
2080 m_simple_objects.erase(cur);
2085 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2087 m_simple_objects.push_back(simple);
2090 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2092 core::map<u16, ClientActiveObject*>::Node *n;
2093 n = m_active_objects.find(id);
2096 return n->getValue();
2099 bool isFreeClientActiveObjectId(u16 id,
2100 core::map<u16, ClientActiveObject*> &objects)
2105 for(core::map<u16, ClientActiveObject*>::Iterator
2106 i = objects.getIterator();
2107 i.atEnd()==false; i++)
2109 if(i.getNode()->getKey() == id)
2115 u16 getFreeClientActiveObjectId(
2116 core::map<u16, ClientActiveObject*> &objects)
2121 if(isFreeClientActiveObjectId(new_id, objects))
2131 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2134 if(object->getId() == 0)
2136 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2139 infostream<<"ClientEnvironment::addActiveObject(): "
2140 <<"no free ids available"<<std::endl;
2144 object->setId(new_id);
2146 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2148 infostream<<"ClientEnvironment::addActiveObject(): "
2149 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2153 infostream<<"ClientEnvironment::addActiveObject(): "
2154 <<"added (id="<<object->getId()<<")"<<std::endl;
2155 m_active_objects.insert(object->getId(), object);
2156 object->addToScene(m_smgr, m_texturesource, m_irr);
2157 { // Update lighting immediately
2161 v3s16 p = object->getLightPosition();
2162 MapNode n = m_map->getNode(p);
2163 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2165 catch(InvalidPositionException &e){
2166 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2168 object->updateLight(light);
2170 return object->getId();
2173 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2174 const std::string &init_data)
2176 ClientActiveObject* obj =
2177 ClientActiveObject::create(type, m_gamedef, this);
2180 infostream<<"ClientEnvironment::addActiveObject(): "
2181 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2188 obj->initialize(init_data);
2190 addActiveObject(obj);
2193 void ClientEnvironment::removeActiveObject(u16 id)
2195 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2196 <<"id="<<id<<std::endl;
2197 ClientActiveObject* obj = getActiveObject(id);
2200 infostream<<"ClientEnvironment::removeActiveObject(): "
2201 <<"id="<<id<<" not found"<<std::endl;
2204 obj->removeFromScene();
2206 m_active_objects.remove(id);
2209 void ClientEnvironment::processActiveObjectMessage(u16 id,
2210 const std::string &data)
2212 ClientActiveObject* obj = getActiveObject(id);
2215 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2216 <<" got message for id="<<id<<", which doesn't exist."
2220 obj->processMessage(data);
2224 Callbacks for activeobjects
2227 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2229 LocalPlayer *lplayer = getLocalPlayer();
2233 if(lplayer->hp > damage)
2234 lplayer->hp -= damage;
2239 ClientEnvEvent event;
2240 event.type = CEE_PLAYER_DAMAGE;
2241 event.player_damage.amount = damage;
2242 event.player_damage.send_to_server = handle_hp;
2243 m_client_event_queue.push_back(event);
2247 Client likes to call these
2250 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2251 core::array<DistanceSortedActiveObject> &dest)
2253 for(core::map<u16, ClientActiveObject*>::Iterator
2254 i = m_active_objects.getIterator();
2255 i.atEnd()==false; i++)
2257 ClientActiveObject* obj = i.getNode()->getValue();
2259 f32 d = (obj->getPosition() - origin).getLength();
2264 DistanceSortedActiveObject dso(obj, d);
2266 dest.push_back(dso);
2270 ClientEnvEvent ClientEnvironment::getClientEvent()
2272 if(m_client_event_queue.size() == 0)
2274 ClientEnvEvent event;
2275 event.type = CEE_NONE;
2278 return m_client_event_queue.pop_front();
2281 #endif // #ifndef SERVER