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 #include "clientmap.h"
45 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
47 Environment::Environment():
52 Environment::~Environment()
55 for(core::list<Player*>::Iterator i = m_players.begin();
56 i != m_players.end(); i++)
62 void Environment::addPlayer(Player *player)
64 DSTACK(__FUNCTION_NAME);
66 Check that peer_ids are unique.
67 Also check that names are unique.
68 Exception: there can be multiple players with peer_id=0
70 // If peer id is non-zero, it has to be unique.
71 if(player->peer_id != 0)
72 assert(getPlayer(player->peer_id) == NULL);
73 // Name has to be unique.
74 assert(getPlayer(player->getName()) == NULL);
76 m_players.push_back(player);
79 void Environment::removePlayer(u16 peer_id)
81 DSTACK(__FUNCTION_NAME);
83 for(core::list<Player*>::Iterator i = m_players.begin();
84 i != m_players.end(); i++)
87 if(player->peer_id != peer_id)
92 // See if there is an another one
93 // (shouldn't be, but just to be sure)
98 Player * Environment::getPlayer(u16 peer_id)
100 for(core::list<Player*>::Iterator i = m_players.begin();
101 i != m_players.end(); i++)
104 if(player->peer_id == peer_id)
110 Player * Environment::getPlayer(const char *name)
112 for(core::list<Player*>::Iterator i = m_players.begin();
113 i != m_players.end(); i++)
116 if(strcmp(player->getName(), name) == 0)
122 Player * Environment::getRandomConnectedPlayer()
124 core::list<Player*> connected_players = getPlayers(true);
125 u32 chosen_one = myrand() % connected_players.size();
127 for(core::list<Player*>::Iterator
128 i = connected_players.begin();
129 i != connected_players.end(); i++)
141 Player * Environment::getNearestConnectedPlayer(v3f pos)
143 core::list<Player*> connected_players = getPlayers(true);
145 Player *nearest_player = NULL;
146 for(core::list<Player*>::Iterator
147 i = connected_players.begin();
148 i != connected_players.end(); i++)
151 f32 d = player->getPosition().getDistanceFrom(pos);
152 if(d < nearest_d || nearest_player == NULL)
155 nearest_player = player;
158 return nearest_player;
161 core::list<Player*> Environment::getPlayers()
166 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
168 core::list<Player*> newlist;
169 for(core::list<Player*>::Iterator
170 i = m_players.begin();
171 i != m_players.end(); i++)
175 if(ignore_disconnected)
177 // Ignore disconnected players
178 if(player->peer_id == 0)
182 newlist.push_back(player);
187 void Environment::printPlayers(std::ostream &o)
189 o<<"Players in environment:"<<std::endl;
190 for(core::list<Player*>::Iterator i = m_players.begin();
191 i != m_players.end(); i++)
194 o<<"Player peer_id="<<player->peer_id<<std::endl;
198 /*void Environment::setDayNightRatio(u32 r)
200 getDayNightRatio() = r;
203 u32 Environment::getDayNightRatio()
205 //return getDayNightRatio();
206 return time_to_daynight_ratio(m_time_of_day);
213 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
217 // Initialize timer to random value to spread processing
218 float itv = abm->getTriggerInterval();
219 itv = MYMAX(0.001, itv); // No less than 1ms
220 int minval = MYMAX(-0.51*itv, -60); // Clamp to
221 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
222 timer = myrand_range(minval, maxval);
229 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
232 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
233 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
234 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
241 void ActiveBlockList::update(core::list<v3s16> &active_positions,
243 core::map<v3s16, bool> &blocks_removed,
244 core::map<v3s16, bool> &blocks_added)
249 core::map<v3s16, bool> newlist;
250 for(core::list<v3s16>::Iterator i = active_positions.begin();
251 i != active_positions.end(); i++)
253 fillRadiusBlock(*i, radius, newlist);
257 Find out which blocks on the old list are not on the new list
259 // Go through old list
260 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
261 i.atEnd()==false; i++)
263 v3s16 p = i.getNode()->getKey();
264 // If not on new list, it's been removed
265 if(newlist.find(p) == NULL)
266 blocks_removed.insert(p, true);
270 Find out which blocks on the new list are not on the old list
272 // Go through new list
273 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
274 i.atEnd()==false; i++)
276 v3s16 p = i.getNode()->getKey();
277 // If not on old list, it's been added
278 if(m_list.find(p) == NULL)
279 blocks_added.insert(p, true);
286 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
287 i.atEnd()==false; i++)
289 v3s16 p = i.getNode()->getKey();
290 m_list.insert(p, true);
298 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
299 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
304 m_random_spawn_timer(3),
305 m_send_recommended_timer(0),
307 m_game_time_fraction_counter(0)
311 ServerEnvironment::~ServerEnvironment()
313 // Clear active block list.
314 // This makes the next one delete all active objects.
315 m_active_blocks.clear();
317 // Convert all objects to static and delete the active objects
318 deactivateFarObjects(true);
323 // Delete ActiveBlockModifiers
324 for(core::list<ABMWithState>::Iterator
325 i = m_abms.begin(); i != m_abms.end(); i++){
330 void ServerEnvironment::serializePlayers(const std::string &savedir)
332 std::string players_path = savedir + "/players";
333 fs::CreateDir(players_path);
335 core::map<Player*, bool> saved_players;
337 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
338 for(u32 i=0; i<player_files.size(); i++)
340 if(player_files[i].dir)
343 // Full path to this file
344 std::string path = players_path + "/" + player_files[i].name;
346 //infostream<<"Checking player file "<<path<<std::endl;
348 // Load player to see what is its name
349 ServerRemotePlayer testplayer(this);
351 // Open file and deserialize
352 std::ifstream is(path.c_str(), std::ios_base::binary);
353 if(is.good() == false)
355 infostream<<"Failed to read "<<path<<std::endl;
358 testplayer.deSerialize(is);
361 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
363 // Search for the player
364 std::string playername = testplayer.getName();
365 Player *player = getPlayer(playername.c_str());
368 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
372 //infostream<<"Found matching player, overwriting."<<std::endl;
374 // OK, found. Save player there.
376 // Open file and serialize
377 std::ofstream os(path.c_str(), std::ios_base::binary);
378 if(os.good() == false)
380 infostream<<"Failed to overwrite "<<path<<std::endl;
383 player->serialize(os);
384 saved_players.insert(player, true);
388 for(core::list<Player*>::Iterator i = m_players.begin();
389 i != m_players.end(); i++)
392 if(saved_players.find(player) != NULL)
394 /*infostream<<"Player "<<player->getName()
395 <<" was already saved."<<std::endl;*/
398 std::string playername = player->getName();
399 // Don't save unnamed player
402 //infostream<<"Not saving unnamed player."<<std::endl;
408 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
409 playername = "player";
410 std::string path = players_path + "/" + playername;
412 for(u32 i=0; i<1000; i++)
414 if(fs::PathExists(path) == false)
419 path = players_path + "/" + playername + itos(i);
423 infostream<<"Didn't find free file for player"<<std::endl;
428 /*infostream<<"Saving player "<<player->getName()<<" to "
430 // Open file and serialize
431 std::ofstream os(path.c_str(), std::ios_base::binary);
432 if(os.good() == false)
434 infostream<<"Failed to overwrite "<<path<<std::endl;
437 player->serialize(os);
438 saved_players.insert(player, true);
442 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
445 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
447 std::string players_path = savedir + "/players";
449 core::map<Player*, bool> saved_players;
451 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
452 for(u32 i=0; i<player_files.size(); i++)
454 if(player_files[i].dir)
457 // Full path to this file
458 std::string path = players_path + "/" + player_files[i].name;
460 //infostream<<"Checking player file "<<path<<std::endl;
462 // Load player to see what is its name
463 ServerRemotePlayer testplayer(this);
465 // Open file and deserialize
466 std::ifstream is(path.c_str(), std::ios_base::binary);
467 if(is.good() == false)
469 infostream<<"Failed to read "<<path<<std::endl;
472 testplayer.deSerialize(is);
475 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
477 infostream<<"Not loading player with invalid name: "
478 <<testplayer.getName()<<std::endl;
481 /*infostream<<"Loaded test player with name "<<testplayer.getName()
484 // Search for the player
485 std::string playername = testplayer.getName();
486 Player *player = getPlayer(playername.c_str());
487 bool newplayer = false;
490 //infostream<<"Is a new player"<<std::endl;
491 player = new ServerRemotePlayer(this);
495 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
499 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"Failed to read "<<path<<std::endl;
508 srp->deSerialize(is);
509 srp->m_last_good_position = srp->getBasePosition();
510 srp->m_last_good_position_age = 0;
520 void ServerEnvironment::saveMeta(const std::string &savedir)
522 std::string path = savedir + "/env_meta.txt";
524 // Open file and serialize
525 std::ofstream os(path.c_str(), std::ios_base::binary);
526 if(os.good() == false)
528 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
530 throw SerializationError("Couldn't save env meta");
534 args.setU64("game_time", m_game_time);
535 args.setU64("time_of_day", getTimeOfDay());
540 void ServerEnvironment::loadMeta(const std::string &savedir)
542 std::string path = savedir + "/env_meta.txt";
544 // Open file and deserialize
545 std::ifstream is(path.c_str(), std::ios_base::binary);
546 if(is.good() == false)
548 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
550 throw SerializationError("Couldn't load env meta");
558 throw SerializationError
559 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
561 std::getline(is, line);
562 std::string trimmedline = trim(line);
563 if(trimmedline == "EnvArgsEnd")
565 args.parseConfigLine(line);
569 m_game_time = args.getU64("game_time");
570 }catch(SettingNotFoundException &e){
571 // Getting this is crucial, otherwise timestamps are useless
572 throw SerializationError("Couldn't load env meta game_time");
576 m_time_of_day = args.getU64("time_of_day");
577 }catch(SettingNotFoundException &e){
578 // This is not as important
579 m_time_of_day = 9000;
585 ActiveBlockModifier *abm;
587 std::set<content_t> required_neighbors;
593 ServerEnvironment *m_env;
594 std::map<content_t, std::list<ActiveABM> > m_aabms;
596 ABMHandler(core::list<ABMWithState> &abms,
597 float dtime_s, ServerEnvironment *env,
603 INodeDefManager *ndef = env->getGameDef()->ndef();
604 for(core::list<ABMWithState>::Iterator
605 i = abms.begin(); i != abms.end(); i++){
606 ActiveBlockModifier *abm = i->abm;
607 float trigger_interval = abm->getTriggerInterval();
608 if(trigger_interval < 0.001)
609 trigger_interval = 0.001;
610 float actual_interval = dtime_s;
613 if(i->timer < trigger_interval)
615 i->timer -= trigger_interval;
616 actual_interval = trigger_interval;
620 float intervals = actual_interval / trigger_interval;
621 float chance = abm->getTriggerChance();
624 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
628 std::set<std::string> required_neighbors_s
629 = abm->getRequiredNeighbors();
630 for(std::set<std::string>::iterator
631 i = required_neighbors_s.begin();
632 i != required_neighbors_s.end(); i++){
633 content_t c = ndef->getId(*i);
634 if(c == CONTENT_IGNORE)
636 aabm.required_neighbors.insert(c);
639 std::set<std::string> contents_s = abm->getTriggerContents();
640 for(std::set<std::string>::iterator
641 i = contents_s.begin(); i != contents_s.end(); i++){
642 content_t c = ndef->getId(*i);
643 if(c == CONTENT_IGNORE)
645 std::map<content_t, std::list<ActiveABM> >::iterator j;
647 if(j == m_aabms.end()){
648 std::list<ActiveABM> aabmlist;
649 m_aabms[c] = aabmlist;
652 j->second.push_back(aabm);
656 void apply(MapBlock *block)
661 ServerMap *map = &m_env->getServerMap();
664 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
665 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
666 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
668 MapNode n = block->getNodeNoEx(p0);
669 content_t c = n.getContent();
670 v3s16 p = p0 + block->getPosRelative();
672 std::map<content_t, std::list<ActiveABM> >::iterator j;
674 if(j == m_aabms.end())
677 for(std::list<ActiveABM>::iterator
678 i = j->second.begin(); i != j->second.end(); i++)
680 if(myrand() % i->chance != 0)
684 if(!i->required_neighbors.empty())
687 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
688 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
689 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
693 MapNode n = map->getNodeNoEx(p1);
694 content_t c = n.getContent();
695 std::set<content_t>::const_iterator k;
696 k = i->required_neighbors.find(c);
697 if(k != i->required_neighbors.end()){
701 // No required neighbor found
706 // Find out how many objects the block contains
707 u32 active_object_count = block->m_static_objects.m_active.size();
708 // Find out how many objects this and all the neighbors contain
709 u32 active_object_count_wider = 0;
710 for(s16 x=-1; x<=1; x++)
711 for(s16 y=-1; y<=1; y++)
712 for(s16 z=-1; z<=1; z++)
714 MapBlock *block2 = map->getBlockNoCreateNoEx(
715 block->getPos() + v3s16(x,y,z));
718 active_object_count_wider +=
719 block2->m_static_objects.m_active.size()
720 + block2->m_static_objects.m_stored.size();
723 // Call all the trigger variations
724 i->abm->trigger(m_env, p, n);
725 i->abm->trigger(m_env, p, n,
726 active_object_count, active_object_count_wider);
732 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
734 // Get time difference
736 u32 stamp = block->getTimestamp();
737 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
738 dtime_s = m_game_time - block->getTimestamp();
739 dtime_s += additional_dtime;
741 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
742 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
744 // Set current time as timestamp
745 block->setTimestampNoChangedFlag(m_game_time);
747 /*infostream<<"ServerEnvironment::activateBlock(): block is "
748 <<dtime_s<<" seconds old."<<std::endl;*/
750 // Activate stored objects
751 activateObjects(block);
754 bool changed = block->m_node_metadata->step((float)dtime_s);
758 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
759 event.p = block->getPos();
760 m_map->dispatchEvent(&event);
762 block->raiseModified(MOD_STATE_WRITE_NEEDED,
763 "node metadata modified in activateBlock");
766 /* Handle ActiveBlockModifiers */
767 ABMHandler abmhandler(m_abms, dtime_s, this, false);
768 abmhandler.apply(block);
771 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
773 m_abms.push_back(ABMWithState(abm));
776 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
778 std::set<u16> objects;
779 for(core::map<u16, ServerActiveObject*>::Iterator
780 i = m_active_objects.getIterator();
781 i.atEnd()==false; i++)
783 ServerActiveObject* obj = i.getNode()->getValue();
784 u16 id = i.getNode()->getKey();
785 v3f objectpos = obj->getBasePosition();
786 if(objectpos.getDistanceFrom(pos) > radius)
793 void ServerEnvironment::clearAllObjects()
795 infostream<<"ServerEnvironment::clearAllObjects(): "
796 <<"Removing all active objects"<<std::endl;
797 core::list<u16> objects_to_remove;
798 for(core::map<u16, ServerActiveObject*>::Iterator
799 i = m_active_objects.getIterator();
800 i.atEnd()==false; i++)
802 ServerActiveObject* obj = i.getNode()->getValue();
803 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
805 u16 id = i.getNode()->getKey();
806 v3f objectpos = obj->getBasePosition();
807 // Delete static object if block is loaded
808 if(obj->m_static_exists){
809 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
811 block->m_static_objects.remove(id);
812 block->raiseModified(MOD_STATE_WRITE_NEEDED,
814 obj->m_static_exists = false;
817 // If known by some client, don't delete immediately
818 if(obj->m_known_by_count > 0){
819 obj->m_pending_deactivation = true;
820 obj->m_removed = true;
824 // Tell the object about removal
825 obj->removingFromEnvironment();
826 // Deregister in scripting api
827 scriptapi_rm_object_reference(m_lua, obj);
829 // Delete active object
830 if(obj->environmentDeletes())
832 // Id to be removed from m_active_objects
833 objects_to_remove.push_back(id);
835 // Remove references from m_active_objects
836 for(core::list<u16>::Iterator i = objects_to_remove.begin();
837 i != objects_to_remove.end(); i++)
839 m_active_objects.remove(*i);
842 core::list<v3s16> loadable_blocks;
843 infostream<<"ServerEnvironment::clearAllObjects(): "
844 <<"Listing all loadable blocks"<<std::endl;
845 m_map->listAllLoadableBlocks(loadable_blocks);
846 infostream<<"ServerEnvironment::clearAllObjects(): "
847 <<"Done listing all loadable blocks: "
848 <<loadable_blocks.size()
849 <<", now clearing"<<std::endl;
850 u32 report_interval = loadable_blocks.size() / 10;
851 u32 num_blocks_checked = 0;
852 u32 num_blocks_cleared = 0;
853 u32 num_objs_cleared = 0;
854 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
855 i != loadable_blocks.end(); i++)
858 MapBlock *block = m_map->emergeBlock(p, false);
860 errorstream<<"ServerEnvironment::clearAllObjects(): "
861 <<"Failed to emerge block "<<PP(p)<<std::endl;
864 u32 num_stored = block->m_static_objects.m_stored.size();
865 u32 num_active = block->m_static_objects.m_active.size();
866 if(num_stored != 0 || num_active != 0){
867 block->m_static_objects.m_stored.clear();
868 block->m_static_objects.m_active.clear();
869 block->raiseModified(MOD_STATE_WRITE_NEEDED,
871 num_objs_cleared += num_stored + num_active;
872 num_blocks_cleared++;
874 num_blocks_checked++;
876 if(num_blocks_checked % report_interval == 0){
877 float percent = 100.0 * (float)num_blocks_checked /
878 loadable_blocks.size();
879 infostream<<"ServerEnvironment::clearAllObjects(): "
880 <<"Cleared "<<num_objs_cleared<<" objects"
881 <<" in "<<num_blocks_cleared<<" blocks ("
882 <<percent<<"%)"<<std::endl;
885 infostream<<"ServerEnvironment::clearAllObjects(): "
886 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
887 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
890 void ServerEnvironment::step(float dtime)
892 DSTACK(__FUNCTION_NAME);
894 //TimeTaker timer("ServerEnv step");
900 m_game_time_fraction_counter += dtime;
901 u32 inc_i = (u32)m_game_time_fraction_counter;
902 m_game_time += inc_i;
903 m_game_time_fraction_counter -= (float)inc_i;
910 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
911 for(core::list<Player*>::Iterator i = m_players.begin();
912 i != m_players.end(); i++)
916 // Ignore disconnected players
917 if(player->peer_id == 0)
920 v3f playerpos = player->getPosition();
923 player->move(dtime, *m_map, 100*BS);
928 Manage active block list
930 if(m_active_blocks_management_interval.step(dtime, 2.0))
932 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
934 Get player block positions
936 core::list<v3s16> players_blockpos;
937 for(core::list<Player*>::Iterator
938 i = m_players.begin();
939 i != m_players.end(); i++)
942 // Ignore disconnected players
943 if(player->peer_id == 0)
945 v3s16 blockpos = getNodeBlockPos(
946 floatToInt(player->getPosition(), BS));
947 players_blockpos.push_back(blockpos);
951 Update list of active blocks, collecting changes
953 const s16 active_block_range = g_settings->getS16("active_block_range");
954 core::map<v3s16, bool> blocks_removed;
955 core::map<v3s16, bool> blocks_added;
956 m_active_blocks.update(players_blockpos, active_block_range,
957 blocks_removed, blocks_added);
960 Handle removed blocks
963 // Convert active objects that are no more in active blocks to static
964 deactivateFarObjects(false);
966 for(core::map<v3s16, bool>::Iterator
967 i = blocks_removed.getIterator();
968 i.atEnd()==false; i++)
970 v3s16 p = i.getNode()->getKey();
972 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
973 <<") became inactive"<<std::endl;*/
975 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
979 // Set current time as timestamp (and let it set ChangedFlag)
980 block->setTimestamp(m_game_time);
987 for(core::map<v3s16, bool>::Iterator
988 i = blocks_added.getIterator();
989 i.atEnd()==false; i++)
991 v3s16 p = i.getNode()->getKey();
993 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
994 <<") became active"<<std::endl;*/
996 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
998 // Block needs to be fetched first
999 m_emerger->queueBlockEmerge(p, false);
1000 m_active_blocks.m_list.remove(p);
1004 activateBlock(block);
1009 Mess around in active blocks
1011 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1013 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1017 for(core::map<v3s16, bool>::Iterator
1018 i = m_active_blocks.m_list.getIterator();
1019 i.atEnd()==false; i++)
1021 v3s16 p = i.getNode()->getKey();
1023 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1024 <<") being handled"<<std::endl;*/
1026 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1030 // Reset block usage timer
1031 block->resetUsageTimer();
1033 // Set current time as timestamp
1034 block->setTimestampNoChangedFlag(m_game_time);
1035 // If time has changed much from the one on disk,
1036 // set block to be saved when it is unloaded
1037 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1038 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1039 "Timestamp older than 60s (step)");
1041 // Run node metadata
1042 bool changed = block->m_node_metadata->step(dtime);
1046 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1048 m_map->dispatchEvent(&event);
1050 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1051 "node metadata modified in step");
1056 const float abm_interval = 1.0;
1057 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1059 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1060 TimeTaker timer("modify in active blocks");
1062 // Initialize handling of ActiveBlockModifiers
1063 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1065 for(core::map<v3s16, bool>::Iterator
1066 i = m_active_blocks.m_list.getIterator();
1067 i.atEnd()==false; i++)
1069 v3s16 p = i.getNode()->getKey();
1071 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1072 <<") being handled"<<std::endl;*/
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1078 // Set current time as timestamp
1079 block->setTimestampNoChangedFlag(m_game_time);
1081 /* Handle ActiveBlockModifiers */
1082 abmhandler.apply(block);
1085 u32 time_ms = timer.stop(true);
1086 u32 max_time_ms = 200;
1087 if(time_ms > max_time_ms){
1088 infostream<<"WARNING: active block modifiers took "
1089 <<time_ms<<"ms (longer than "
1090 <<max_time_ms<<"ms)"<<std::endl;
1095 Step script environment (run global on_step())
1097 scriptapi_environment_step(m_lua, dtime);
1103 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1104 //TimeTaker timer("Step active objects");
1106 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1108 // This helps the objects to send data at the same time
1109 bool send_recommended = false;
1110 m_send_recommended_timer += dtime;
1111 if(m_send_recommended_timer > getSendRecommendedInterval())
1113 m_send_recommended_timer -= getSendRecommendedInterval();
1114 send_recommended = true;
1117 for(core::map<u16, ServerActiveObject*>::Iterator
1118 i = m_active_objects.getIterator();
1119 i.atEnd()==false; i++)
1121 ServerActiveObject* obj = i.getNode()->getValue();
1122 // Remove non-peaceful mobs on peaceful mode
1123 if(g_settings->getBool("only_peaceful_mobs")){
1124 if(!obj->isPeaceful())
1125 obj->m_removed = true;
1127 // Don't step if is to be removed or stored statically
1128 if(obj->m_removed || obj->m_pending_deactivation)
1131 obj->step(dtime, send_recommended);
1132 // Read messages from object
1133 while(obj->m_messages_out.size() > 0)
1135 m_active_object_messages.push_back(
1136 obj->m_messages_out.pop_front());
1142 Manage active objects
1144 if(m_object_management_interval.step(dtime, 0.5))
1146 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1148 Remove objects that satisfy (m_removed && m_known_by_count==0)
1150 removeRemovedObjects();
1154 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1156 core::map<u16, ServerActiveObject*>::Node *n;
1157 n = m_active_objects.find(id);
1160 return n->getValue();
1163 bool isFreeServerActiveObjectId(u16 id,
1164 core::map<u16, ServerActiveObject*> &objects)
1169 for(core::map<u16, ServerActiveObject*>::Iterator
1170 i = objects.getIterator();
1171 i.atEnd()==false; i++)
1173 if(i.getNode()->getKey() == id)
1179 u16 getFreeServerActiveObjectId(
1180 core::map<u16, ServerActiveObject*> &objects)
1185 if(isFreeServerActiveObjectId(new_id, objects))
1195 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1198 u16 id = addActiveObjectRaw(object, true);
1202 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1206 v3f objectpos = obj->getBasePosition();
1208 // The block in which the object resides in
1209 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1212 Update the static data
1215 // Create new static object
1216 std::string staticdata = obj->getStaticData();
1217 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1218 // Add to the block where the object is located in
1219 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1220 // Get or generate the block
1221 MapBlock *block = m_map->emergeBlock(blockpos);
1223 bool succeeded = false;
1227 block->m_static_objects.insert(0, s_obj);
1228 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1229 "addActiveObjectAsStatic");
1233 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1234 <<"Could not find or generate "
1235 <<"a block for storing static object"<<std::endl;
1239 if(obj->environmentDeletes())
1246 Finds out what new objects have been added to
1247 inside a radius around a position
1249 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1250 core::map<u16, bool> ¤t_objects,
1251 core::map<u16, bool> &added_objects)
1253 v3f pos_f = intToFloat(pos, BS);
1254 f32 radius_f = radius * BS;
1256 Go through the object list,
1257 - discard m_removed objects,
1258 - discard objects that are too far away,
1259 - discard objects that are found in current_objects.
1260 - add remaining objects to added_objects
1262 for(core::map<u16, ServerActiveObject*>::Iterator
1263 i = m_active_objects.getIterator();
1264 i.atEnd()==false; i++)
1266 u16 id = i.getNode()->getKey();
1268 ServerActiveObject *object = i.getNode()->getValue();
1271 // Discard if removed
1272 if(object->m_removed)
1274 if(object->unlimitedTransferDistance() == false){
1275 // Discard if too far
1276 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1277 if(distance_f > radius_f)
1280 // Discard if already on current_objects
1281 core::map<u16, bool>::Node *n;
1282 n = current_objects.find(id);
1285 // Add to added_objects
1286 added_objects.insert(id, false);
1291 Finds out what objects have been removed from
1292 inside a radius around a position
1294 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1295 core::map<u16, bool> ¤t_objects,
1296 core::map<u16, bool> &removed_objects)
1298 v3f pos_f = intToFloat(pos, BS);
1299 f32 radius_f = radius * BS;
1301 Go through current_objects; object is removed if:
1302 - object is not found in m_active_objects (this is actually an
1303 error condition; objects should be set m_removed=true and removed
1304 only after all clients have been informed about removal), or
1305 - object has m_removed=true, or
1306 - object is too far away
1308 for(core::map<u16, bool>::Iterator
1309 i = current_objects.getIterator();
1310 i.atEnd()==false; i++)
1312 u16 id = i.getNode()->getKey();
1313 ServerActiveObject *object = getActiveObject(id);
1316 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1317 <<" object in current_objects is NULL"<<std::endl;
1318 removed_objects.insert(id, false);
1322 if(object->m_removed)
1324 removed_objects.insert(id, false);
1328 // If transfer distance is unlimited, don't remove
1329 if(object->unlimitedTransferDistance())
1332 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1334 if(distance_f >= radius_f)
1336 removed_objects.insert(id, false);
1344 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1346 if(m_active_object_messages.size() == 0)
1347 return ActiveObjectMessage(0);
1349 return m_active_object_messages.pop_front();
1353 ************ Private methods *************
1356 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1360 if(object->getId() == 0){
1361 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1364 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1365 <<"no free ids available"<<std::endl;
1366 if(object->environmentDeletes())
1370 object->setId(new_id);
1373 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1374 <<"supplied with id "<<object->getId()<<std::endl;
1376 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1378 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1379 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1380 if(object->environmentDeletes())
1384 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1385 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1387 m_active_objects.insert(object->getId(), object);
1389 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1390 <<"Added id="<<object->getId()<<"; there are now "
1391 <<m_active_objects.size()<<" active objects."
1394 // Register reference in scripting api (must be done before post-init)
1395 scriptapi_add_object_reference(m_lua, object);
1396 // Post-initialize object
1397 object->addedToEnvironment();
1399 // Add static data to block
1400 if(object->isStaticAllowed())
1402 // Add static object to active static list of the block
1403 v3f objectpos = object->getBasePosition();
1404 std::string staticdata = object->getStaticData();
1405 StaticObject s_obj(object->getType(), objectpos, staticdata);
1406 // Add to the block where the object is located in
1407 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1408 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1411 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1412 object->m_static_exists = true;
1413 object->m_static_block = blockpos;
1416 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1417 "addActiveObjectRaw");
1420 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1421 <<"could not find block for storing id="<<object->getId()
1422 <<" statically"<<std::endl;
1426 return object->getId();
1430 Remove objects that satisfy (m_removed && m_known_by_count==0)
1432 void ServerEnvironment::removeRemovedObjects()
1434 core::list<u16> objects_to_remove;
1435 for(core::map<u16, ServerActiveObject*>::Iterator
1436 i = m_active_objects.getIterator();
1437 i.atEnd()==false; i++)
1439 u16 id = i.getNode()->getKey();
1440 ServerActiveObject* obj = i.getNode()->getValue();
1441 // This shouldn't happen but check it
1444 infostream<<"NULL object found in ServerEnvironment"
1445 <<" while finding removed objects. id="<<id<<std::endl;
1446 // Id to be removed from m_active_objects
1447 objects_to_remove.push_back(id);
1452 We will delete objects that are marked as removed or thatare
1453 waiting for deletion after deactivation
1455 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1459 Delete static data from block if is marked as removed
1461 if(obj->m_static_exists && obj->m_removed)
1463 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1466 block->m_static_objects.remove(id);
1467 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1468 "removeRemovedObjects");
1469 obj->m_static_exists = false;
1473 // If m_known_by_count > 0, don't actually remove.
1474 if(obj->m_known_by_count > 0)
1477 // Tell the object about removal
1478 obj->removingFromEnvironment();
1479 // Deregister in scripting api
1480 scriptapi_rm_object_reference(m_lua, obj);
1483 if(obj->environmentDeletes())
1485 // Id to be removed from m_active_objects
1486 objects_to_remove.push_back(id);
1488 // Remove references from m_active_objects
1489 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1490 i != objects_to_remove.end(); i++)
1492 m_active_objects.remove(*i);
1496 static void print_hexdump(std::ostream &o, const std::string &data)
1498 const int linelength = 16;
1499 for(int l=0; ; l++){
1500 int i0 = linelength * l;
1501 bool at_end = false;
1502 int thislinelength = linelength;
1503 if(i0 + thislinelength > (int)data.size()){
1504 thislinelength = data.size() - i0;
1507 for(int di=0; di<linelength; di++){
1510 if(di<thislinelength)
1511 snprintf(buf, 4, "%.2x ", data[i]);
1513 snprintf(buf, 4, " ");
1517 for(int di=0; di<thislinelength; di++){
1531 Convert stored objects from blocks near the players to active.
1533 void ServerEnvironment::activateObjects(MapBlock *block)
1537 // Ignore if no stored objects (to not set changed flag)
1538 if(block->m_static_objects.m_stored.size() == 0)
1540 verbosestream<<"ServerEnvironment::activateObjects(): "
1541 <<"activating objects of block "<<PP(block->getPos())
1542 <<" ("<<block->m_static_objects.m_stored.size()
1543 <<" objects)"<<std::endl;
1544 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1546 errorstream<<"suspiciously large amount of objects detected: "
1547 <<block->m_static_objects.m_stored.size()<<" in "
1548 <<PP(block->getPos())
1549 <<"; removing all of them."<<std::endl;
1550 // Clear stored list
1551 block->m_static_objects.m_stored.clear();
1552 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1553 "stored list cleared in activateObjects due to "
1554 "large amount of objects");
1557 // A list for objects that couldn't be converted to static for some
1558 // reason. They will be stored back.
1559 core::list<StaticObject> new_stored;
1560 // Loop through stored static objects
1561 for(core::list<StaticObject>::Iterator
1562 i = block->m_static_objects.m_stored.begin();
1563 i != block->m_static_objects.m_stored.end(); i++)
1565 /*infostream<<"Server: Creating an active object from "
1566 <<"static data"<<std::endl;*/
1567 StaticObject &s_obj = *i;
1568 // Create an active object from the data
1569 ServerActiveObject *obj = ServerActiveObject::create
1570 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1571 // If couldn't create object, store static data back.
1574 errorstream<<"ServerEnvironment::activateObjects(): "
1575 <<"failed to create active object from static object "
1576 <<"in block "<<PP(s_obj.pos/BS)
1577 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1578 print_hexdump(verbosestream, s_obj.data);
1580 new_stored.push_back(s_obj);
1583 verbosestream<<"ServerEnvironment::activateObjects(): "
1584 <<"activated static object pos="<<PP(s_obj.pos/BS)
1585 <<" type="<<(int)s_obj.type<<std::endl;
1586 // This will also add the object to the active static list
1587 addActiveObjectRaw(obj, false);
1589 // Clear stored list
1590 block->m_static_objects.m_stored.clear();
1591 // Add leftover failed stuff to stored list
1592 for(core::list<StaticObject>::Iterator
1593 i = new_stored.begin();
1594 i != new_stored.end(); i++)
1596 StaticObject &s_obj = *i;
1597 block->m_static_objects.m_stored.push_back(s_obj);
1600 Note: Block hasn't really been modified here.
1601 The objects have just been activated and moved from the stored
1602 static list to the active static list.
1603 As such, the block is essentially the same.
1604 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1605 Otherwise there would be a huge amount of unnecessary I/O.
1610 Convert objects that are not standing inside active blocks to static.
1612 If m_known_by_count != 0, active object is not deleted, but static
1613 data is still updated.
1615 If force_delete is set, active object is deleted nevertheless. It
1616 shall only be set so in the destructor of the environment.
1618 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1620 core::list<u16> objects_to_remove;
1621 for(core::map<u16, ServerActiveObject*>::Iterator
1622 i = m_active_objects.getIterator();
1623 i.atEnd()==false; i++)
1625 ServerActiveObject* obj = i.getNode()->getValue();
1628 // Do not deactivate if static data creation not allowed
1629 if(!force_delete && !obj->isStaticAllowed())
1632 // If pending deactivation, let removeRemovedObjects() do it
1633 if(!force_delete && obj->m_pending_deactivation)
1636 u16 id = i.getNode()->getKey();
1637 v3f objectpos = obj->getBasePosition();
1639 // The block in which the object resides in
1640 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1642 // If block is active, don't remove
1643 if(!force_delete && m_active_blocks.contains(blockpos_o))
1646 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1647 <<"deactivating object id="<<id<<" on inactive block "
1648 <<PP(blockpos_o)<<std::endl;
1650 // If known by some client, don't immediately delete.
1651 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1654 Update the static data
1657 if(obj->isStaticAllowed())
1659 // Create new static object
1660 std::string staticdata_new = obj->getStaticData();
1661 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1663 bool stays_in_same_block = false;
1664 bool data_changed = true;
1666 if(obj->m_static_exists){
1667 if(obj->m_static_block == blockpos_o)
1668 stays_in_same_block = true;
1670 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1672 core::map<u16, StaticObject>::Node *n =
1673 block->m_static_objects.m_active.find(id);
1675 StaticObject static_old = n->getValue();
1677 float save_movem = obj->getMinimumSavedMovement();
1679 if(static_old.data == staticdata_new &&
1680 (static_old.pos - objectpos).getLength() < save_movem)
1681 data_changed = false;
1683 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1684 <<"id="<<id<<" m_static_exists=true but "
1685 <<"static data doesn't actually exist in "
1686 <<PP(obj->m_static_block)<<std::endl;
1690 bool shall_be_written = (!stays_in_same_block || data_changed);
1692 // Delete old static object
1693 if(obj->m_static_exists)
1695 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1698 block->m_static_objects.remove(id);
1699 obj->m_static_exists = false;
1700 // Only mark block as modified if data changed considerably
1701 if(shall_be_written)
1702 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1703 "deactivateFarObjects: Static data "
1704 "changed considerably");
1708 // Add to the block where the object is located in
1709 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1710 // Get or generate the block
1711 MapBlock *block = m_map->emergeBlock(blockpos);
1715 if(block->m_static_objects.m_stored.size() >= 49){
1716 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1717 <<" statically but block "<<PP(blockpos)
1718 <<" already contains "
1719 <<block->m_static_objects.m_stored.size()
1720 <<" (over 49) objects."
1721 <<" Forcing delete."<<std::endl;
1722 force_delete = true;
1724 u16 new_id = pending_delete ? id : 0;
1725 block->m_static_objects.insert(new_id, s_obj);
1727 // Only mark block as modified if data changed considerably
1728 if(shall_be_written)
1729 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1730 "deactivateFarObjects: Static data "
1731 "changed considerably");
1733 obj->m_static_exists = true;
1734 obj->m_static_block = block->getPos();
1739 errorstream<<"ServerEnv: Could not find or generate "
1740 <<"a block for storing id="<<obj->getId()
1741 <<" statically"<<std::endl;
1748 If known by some client, set pending deactivation.
1749 Otherwise delete it immediately.
1752 if(pending_delete && !force_delete)
1754 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1755 <<"object id="<<id<<" is known by clients"
1756 <<"; not deleting yet"<<std::endl;
1758 obj->m_pending_deactivation = true;
1762 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1763 <<"object id="<<id<<" is not known by clients"
1764 <<"; deleting"<<std::endl;
1766 // Tell the object about removal
1767 obj->removingFromEnvironment();
1768 // Deregister in scripting api
1769 scriptapi_rm_object_reference(m_lua, obj);
1771 // Delete active object
1772 if(obj->environmentDeletes())
1774 // Id to be removed from m_active_objects
1775 objects_to_remove.push_back(id);
1778 // Remove references from m_active_objects
1779 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1780 i != objects_to_remove.end(); i++)
1782 m_active_objects.remove(*i);
1789 #include "clientsimpleobject.h"
1795 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1796 ITextureSource *texturesource, IGameDef *gamedef,
1797 IrrlichtDevice *irr):
1800 m_texturesource(texturesource),
1806 ClientEnvironment::~ClientEnvironment()
1808 // delete active objects
1809 for(core::map<u16, ClientActiveObject*>::Iterator
1810 i = m_active_objects.getIterator();
1811 i.atEnd()==false; i++)
1813 delete i.getNode()->getValue();
1816 for(core::list<ClientSimpleObject*>::Iterator
1817 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1826 Map & ClientEnvironment::getMap()
1831 ClientMap & ClientEnvironment::getClientMap()
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");
1868 LocalPlayer *lplayer = getLocalPlayer();
1870 // collision info queue
1871 core::list<CollisionInfo> player_collisions;
1874 Get the speed the player is going
1876 bool is_climbing = lplayer->is_climbing;
1878 f32 player_speed = lplayer->getSpeed().getLength();
1881 Maximum position increment
1883 //f32 position_max_increment = 0.05*BS;
1884 f32 position_max_increment = 0.1*BS;
1886 // Maximum time increment (for collision detection etc)
1887 // time = distance / speed
1888 f32 dtime_max_increment = 1;
1889 if(player_speed > 0.001)
1890 dtime_max_increment = position_max_increment / player_speed;
1892 // Maximum time increment is 10ms or lower
1893 if(dtime_max_increment > 0.01)
1894 dtime_max_increment = 0.01;
1896 // Don't allow overly huge dtime
1900 f32 dtime_downcount = dtime;
1903 Stuff that has a maximum time increment
1912 if(dtime_downcount > dtime_max_increment)
1914 dtime_part = dtime_max_increment;
1915 dtime_downcount -= dtime_part;
1919 dtime_part = dtime_downcount;
1921 Setting this to 0 (no -=dtime_part) disables an infinite loop
1922 when dtime_part is so small that dtime_downcount -= dtime_part
1925 dtime_downcount = 0;
1933 v3f lplayerpos = lplayer->getPosition();
1936 if(free_move == false && is_climbing == false)
1939 v3f speed = lplayer->getSpeed();
1940 if(lplayer->swimming_up == false)
1941 speed.Y -= 9.81 * BS * dtime_part * 2;
1944 if(lplayer->in_water_stable || lplayer->in_water)
1946 f32 max_down = 2.0*BS;
1947 if(speed.Y < -max_down) speed.Y = -max_down;
1950 if(speed.getLength() > max)
1952 speed = speed / speed.getLength() * max;
1956 lplayer->setSpeed(speed);
1961 This also does collision detection.
1963 lplayer->move(dtime_part, *m_map, position_max_increment,
1964 &player_collisions);
1967 while(dtime_downcount > 0.001);
1969 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1971 for(core::list<CollisionInfo>::Iterator
1972 i = player_collisions.begin();
1973 i != player_collisions.end(); i++)
1975 CollisionInfo &info = *i;
1976 if(info.t == COLLISION_FALL)
1978 //f32 tolerance = BS*10; // 2 without damage
1979 f32 tolerance = BS*12; // 3 without damage
1981 if(info.speed > tolerance)
1983 f32 damage_f = (info.speed - tolerance)/BS*factor;
1984 u16 damage = (u16)(damage_f+0.5);
1985 damageLocalPlayer(damage, true);
1991 A quick draft of lava damage
1993 if(m_lava_hurt_interval.step(dtime, 1.0))
1995 v3f pf = lplayer->getPosition();
1997 // Feet, middle and head
1998 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1999 MapNode n1 = m_map->getNodeNoEx(p1);
2000 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2001 MapNode n2 = m_map->getNodeNoEx(p2);
2002 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2003 MapNode n3 = m_map->getNodeNoEx(p2);
2005 u32 damage_per_second = 0;
2006 damage_per_second = MYMAX(damage_per_second,
2007 m_gamedef->ndef()->get(n1).damage_per_second);
2008 damage_per_second = MYMAX(damage_per_second,
2009 m_gamedef->ndef()->get(n2).damage_per_second);
2010 damage_per_second = MYMAX(damage_per_second,
2011 m_gamedef->ndef()->get(n3).damage_per_second);
2013 if(damage_per_second != 0)
2015 damageLocalPlayer(damage_per_second, true);
2020 Stuff that can be done in an arbitarily large dtime
2022 for(core::list<Player*>::Iterator i = m_players.begin();
2023 i != m_players.end(); i++)
2025 Player *player = *i;
2026 v3f playerpos = player->getPosition();
2029 Handle non-local players
2031 if(player->isLocal() == false)
2034 player->move(dtime, *m_map, 100*BS);
2038 // Update lighting on all players on client
2039 u8 light = LIGHT_MAX;
2042 v3s16 p = player->getLightPosition();
2043 MapNode n = m_map->getNode(p);
2044 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2046 catch(InvalidPositionException &e){
2047 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2049 player->updateLight(light);
2053 Step active objects and update lighting of them
2056 for(core::map<u16, ClientActiveObject*>::Iterator
2057 i = m_active_objects.getIterator();
2058 i.atEnd()==false; i++)
2060 ClientActiveObject* obj = i.getNode()->getValue();
2062 obj->step(dtime, this);
2064 if(m_active_object_light_update_interval.step(dtime, 0.21))
2070 v3s16 p = obj->getLightPosition();
2071 MapNode n = m_map->getNode(p);
2072 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2074 catch(InvalidPositionException &e){
2075 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2077 obj->updateLight(light);
2082 Step and handle simple objects
2084 for(core::list<ClientSimpleObject*>::Iterator
2085 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2087 ClientSimpleObject *simple = *i;
2088 core::list<ClientSimpleObject*>::Iterator cur = i;
2090 simple->step(dtime);
2091 if(simple->m_to_be_removed){
2093 m_simple_objects.erase(cur);
2098 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2100 m_simple_objects.push_back(simple);
2103 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2105 core::map<u16, ClientActiveObject*>::Node *n;
2106 n = m_active_objects.find(id);
2109 return n->getValue();
2112 bool isFreeClientActiveObjectId(u16 id,
2113 core::map<u16, ClientActiveObject*> &objects)
2118 for(core::map<u16, ClientActiveObject*>::Iterator
2119 i = objects.getIterator();
2120 i.atEnd()==false; i++)
2122 if(i.getNode()->getKey() == id)
2128 u16 getFreeClientActiveObjectId(
2129 core::map<u16, ClientActiveObject*> &objects)
2134 if(isFreeClientActiveObjectId(new_id, objects))
2144 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2147 if(object->getId() == 0)
2149 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2152 infostream<<"ClientEnvironment::addActiveObject(): "
2153 <<"no free ids available"<<std::endl;
2157 object->setId(new_id);
2159 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2161 infostream<<"ClientEnvironment::addActiveObject(): "
2162 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2166 infostream<<"ClientEnvironment::addActiveObject(): "
2167 <<"added (id="<<object->getId()<<")"<<std::endl;
2168 m_active_objects.insert(object->getId(), object);
2169 object->addToScene(m_smgr, m_texturesource, m_irr);
2170 { // Update lighting immediately
2174 v3s16 p = object->getLightPosition();
2175 MapNode n = m_map->getNode(p);
2176 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2178 catch(InvalidPositionException &e){
2179 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2181 object->updateLight(light);
2183 return object->getId();
2186 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2187 const std::string &init_data)
2189 ClientActiveObject* obj =
2190 ClientActiveObject::create(type, m_gamedef, this);
2193 infostream<<"ClientEnvironment::addActiveObject(): "
2194 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2201 obj->initialize(init_data);
2203 addActiveObject(obj);
2206 void ClientEnvironment::removeActiveObject(u16 id)
2208 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2209 <<"id="<<id<<std::endl;
2210 ClientActiveObject* obj = getActiveObject(id);
2213 infostream<<"ClientEnvironment::removeActiveObject(): "
2214 <<"id="<<id<<" not found"<<std::endl;
2217 obj->removeFromScene();
2219 m_active_objects.remove(id);
2222 void ClientEnvironment::processActiveObjectMessage(u16 id,
2223 const std::string &data)
2225 ClientActiveObject* obj = getActiveObject(id);
2228 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2229 <<" got message for id="<<id<<", which doesn't exist."
2233 obj->processMessage(data);
2237 Callbacks for activeobjects
2240 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2242 LocalPlayer *lplayer = getLocalPlayer();
2246 if(lplayer->hp > damage)
2247 lplayer->hp -= damage;
2252 ClientEnvEvent event;
2253 event.type = CEE_PLAYER_DAMAGE;
2254 event.player_damage.amount = damage;
2255 event.player_damage.send_to_server = handle_hp;
2256 m_client_event_queue.push_back(event);
2260 Client likes to call these
2263 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2264 core::array<DistanceSortedActiveObject> &dest)
2266 for(core::map<u16, ClientActiveObject*>::Iterator
2267 i = m_active_objects.getIterator();
2268 i.atEnd()==false; i++)
2270 ClientActiveObject* obj = i.getNode()->getValue();
2272 f32 d = (obj->getPosition() - origin).getLength();
2277 DistanceSortedActiveObject dso(obj, d);
2279 dest.push_back(dso);
2283 ClientEnvEvent ClientEnvironment::getClientEvent()
2285 if(m_client_event_queue.size() == 0)
2287 ClientEnvEvent event;
2288 event.type = CEE_NONE;
2291 return m_client_event_queue.pop_front();
2294 #endif // #ifndef SERVER