3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
40 #include "serverremoteplayer.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 Environment::Environment():
49 Environment::~Environment()
52 for(core::list<Player*>::Iterator i = m_players.begin();
53 i != m_players.end(); i++)
59 void Environment::addPlayer(Player *player)
61 DSTACK(__FUNCTION_NAME);
63 Check that peer_ids are unique.
64 Also check that names are unique.
65 Exception: there can be multiple players with peer_id=0
67 // If peer id is non-zero, it has to be unique.
68 if(player->peer_id != 0)
69 assert(getPlayer(player->peer_id) == NULL);
70 // Name has to be unique.
71 assert(getPlayer(player->getName()) == NULL);
73 m_players.push_back(player);
76 void Environment::removePlayer(u16 peer_id)
78 DSTACK(__FUNCTION_NAME);
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id != peer_id)
89 // See if there is an another one
90 // (shouldn't be, but just to be sure)
95 Player * Environment::getPlayer(u16 peer_id)
97 for(core::list<Player*>::Iterator i = m_players.begin();
98 i != m_players.end(); i++)
101 if(player->peer_id == peer_id)
107 Player * Environment::getPlayer(const char *name)
109 for(core::list<Player*>::Iterator i = m_players.begin();
110 i != m_players.end(); i++)
113 if(strcmp(player->getName(), name) == 0)
119 Player * Environment::getRandomConnectedPlayer()
121 core::list<Player*> connected_players = getPlayers(true);
122 u32 chosen_one = myrand() % connected_players.size();
124 for(core::list<Player*>::Iterator
125 i = connected_players.begin();
126 i != connected_players.end(); i++)
138 Player * Environment::getNearestConnectedPlayer(v3f pos)
140 core::list<Player*> connected_players = getPlayers(true);
142 Player *nearest_player = NULL;
143 for(core::list<Player*>::Iterator
144 i = connected_players.begin();
145 i != connected_players.end(); i++)
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL)
152 nearest_player = player;
155 return nearest_player;
158 core::list<Player*> Environment::getPlayers()
163 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
165 core::list<Player*> newlist;
166 for(core::list<Player*>::Iterator
167 i = m_players.begin();
168 i != m_players.end(); i++)
172 if(ignore_disconnected)
174 // Ignore disconnected players
175 if(player->peer_id == 0)
179 newlist.push_back(player);
184 void Environment::printPlayers(std::ostream &o)
186 o<<"Players in environment:"<<std::endl;
187 for(core::list<Player*>::Iterator i = m_players.begin();
188 i != m_players.end(); i++)
191 o<<"Player peer_id="<<player->peer_id<<std::endl;
195 /*void Environment::setDayNightRatio(u32 r)
197 getDayNightRatio() = r;
200 u32 Environment::getDayNightRatio()
202 //return getDayNightRatio();
203 return time_to_daynight_ratio(m_time_of_day);
210 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
213 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
214 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
215 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
222 void ActiveBlockList::update(core::list<v3s16> &active_positions,
224 core::map<v3s16, bool> &blocks_removed,
225 core::map<v3s16, bool> &blocks_added)
230 core::map<v3s16, bool> newlist;
231 for(core::list<v3s16>::Iterator i = active_positions.begin();
232 i != active_positions.end(); i++)
234 fillRadiusBlock(*i, radius, newlist);
238 Find out which blocks on the old list are not on the new list
240 // Go through old list
241 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
242 i.atEnd()==false; i++)
244 v3s16 p = i.getNode()->getKey();
245 // If not on new list, it's been removed
246 if(newlist.find(p) == NULL)
247 blocks_removed.insert(p, true);
251 Find out which blocks on the new list are not on the old list
253 // Go through new list
254 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
255 i.atEnd()==false; i++)
257 v3s16 p = i.getNode()->getKey();
258 // If not on old list, it's been added
259 if(m_list.find(p) == NULL)
260 blocks_added.insert(p, true);
267 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
268 i.atEnd()==false; i++)
270 v3s16 p = i.getNode()->getKey();
271 m_list.insert(p, true);
279 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
280 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
285 m_random_spawn_timer(3),
286 m_send_recommended_timer(0),
288 m_game_time_fraction_counter(0)
292 ServerEnvironment::~ServerEnvironment()
294 // Clear active block list.
295 // This makes the next one delete all active objects.
296 m_active_blocks.clear();
298 // Convert all objects to static and delete the active objects
299 deactivateFarObjects(true);
304 // Delete ActiveBlockModifiers
305 for(core::list<ABMWithState>::Iterator
306 i = m_abms.begin(); i != m_abms.end(); i++){
311 void ServerEnvironment::serializePlayers(const std::string &savedir)
313 std::string players_path = savedir + "/players";
314 fs::CreateDir(players_path);
316 core::map<Player*, bool> saved_players;
318 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
319 for(u32 i=0; i<player_files.size(); i++)
321 if(player_files[i].dir)
324 // Full path to this file
325 std::string path = players_path + "/" + player_files[i].name;
327 //infostream<<"Checking player file "<<path<<std::endl;
329 // Load player to see what is its name
330 ServerRemotePlayer testplayer(this);
332 // Open file and deserialize
333 std::ifstream is(path.c_str(), std::ios_base::binary);
334 if(is.good() == false)
336 infostream<<"Failed to read "<<path<<std::endl;
339 testplayer.deSerialize(is);
342 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
344 // Search for the player
345 std::string playername = testplayer.getName();
346 Player *player = getPlayer(playername.c_str());
349 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
353 //infostream<<"Found matching player, overwriting."<<std::endl;
355 // OK, found. Save player there.
357 // Open file and serialize
358 std::ofstream os(path.c_str(), std::ios_base::binary);
359 if(os.good() == false)
361 infostream<<"Failed to overwrite "<<path<<std::endl;
364 player->serialize(os);
365 saved_players.insert(player, true);
369 for(core::list<Player*>::Iterator i = m_players.begin();
370 i != m_players.end(); i++)
373 if(saved_players.find(player) != NULL)
375 /*infostream<<"Player "<<player->getName()
376 <<" was already saved."<<std::endl;*/
379 std::string playername = player->getName();
380 // Don't save unnamed player
383 //infostream<<"Not saving unnamed player."<<std::endl;
389 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
390 playername = "player";
391 std::string path = players_path + "/" + playername;
393 for(u32 i=0; i<1000; i++)
395 if(fs::PathExists(path) == false)
400 path = players_path + "/" + playername + itos(i);
404 infostream<<"Didn't find free file for player"<<std::endl;
409 /*infostream<<"Saving player "<<player->getName()<<" to "
411 // Open file and serialize
412 std::ofstream os(path.c_str(), std::ios_base::binary);
413 if(os.good() == false)
415 infostream<<"Failed to overwrite "<<path<<std::endl;
418 player->serialize(os);
419 saved_players.insert(player, true);
423 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
426 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
428 std::string players_path = savedir + "/players";
430 core::map<Player*, bool> saved_players;
432 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
433 for(u32 i=0; i<player_files.size(); i++)
435 if(player_files[i].dir)
438 // Full path to this file
439 std::string path = players_path + "/" + player_files[i].name;
441 infostream<<"Checking player file "<<path<<std::endl;
443 // Load player to see what is its name
444 ServerRemotePlayer testplayer(this);
446 // Open file and deserialize
447 std::ifstream is(path.c_str(), std::ios_base::binary);
448 if(is.good() == false)
450 infostream<<"Failed to read "<<path<<std::endl;
453 testplayer.deSerialize(is);
456 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
458 infostream<<"Not loading player with invalid name: "
459 <<testplayer.getName()<<std::endl;
462 infostream<<"Loaded test player with name "<<testplayer.getName()
465 // Search for the player
466 std::string playername = testplayer.getName();
467 Player *player = getPlayer(playername.c_str());
468 bool newplayer = false;
471 infostream<<"Is a new player"<<std::endl;
472 player = new ServerRemotePlayer(this);
476 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
480 infostream<<"Reading player "<<testplayer.getName()<<" from "
482 // Open file and deserialize
483 std::ifstream is(path.c_str(), std::ios_base::binary);
484 if(is.good() == false)
486 infostream<<"Failed to read "<<path<<std::endl;
489 srp->deSerialize(is);
490 srp->m_last_good_position = srp->getBasePosition();
491 srp->m_last_good_position_age = 0;
501 void ServerEnvironment::saveMeta(const std::string &savedir)
503 std::string path = savedir + "/env_meta.txt";
505 // Open file and serialize
506 std::ofstream os(path.c_str(), std::ios_base::binary);
507 if(os.good() == false)
509 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
511 throw SerializationError("Couldn't save env meta");
515 args.setU64("game_time", m_game_time);
516 args.setU64("time_of_day", getTimeOfDay());
521 void ServerEnvironment::loadMeta(const std::string &savedir)
523 std::string path = savedir + "/env_meta.txt";
525 // Open file and deserialize
526 std::ifstream is(path.c_str(), std::ios_base::binary);
527 if(is.good() == false)
529 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
531 throw SerializationError("Couldn't load env meta");
539 throw SerializationError
540 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
542 std::getline(is, line);
543 std::string trimmedline = trim(line);
544 if(trimmedline == "EnvArgsEnd")
546 args.parseConfigLine(line);
550 m_game_time = args.getU64("game_time");
551 }catch(SettingNotFoundException &e){
552 // Getting this is crucial, otherwise timestamps are useless
553 throw SerializationError("Couldn't load env meta game_time");
557 m_time_of_day = args.getU64("time_of_day");
558 }catch(SettingNotFoundException &e){
559 // This is not as important
560 m_time_of_day = 9000;
566 ActiveBlockModifier *abm;
573 ServerEnvironment *m_env;
574 std::map<content_t, std::list<ActiveABM> > m_aabms;
576 ABMHandler(core::list<ABMWithState> &abms,
577 float dtime_s, ServerEnvironment *env,
583 INodeDefManager *ndef = env->getGameDef()->ndef();
584 for(core::list<ABMWithState>::Iterator
585 i = abms.begin(); i != abms.end(); i++){
586 ActiveBlockModifier *abm = i->abm;
587 float trigger_interval = abm->getTriggerInterval();
588 if(trigger_interval < 0.001)
589 trigger_interval = 0.001;
592 if(i->timer < trigger_interval)
594 i->timer -= trigger_interval;
598 float intervals = dtime_s / trigger_interval;
599 float chance = abm->getTriggerChance();
602 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
605 std::set<std::string> contents_s = abm->getTriggerContents();
606 for(std::set<std::string>::iterator
607 i = contents_s.begin(); i != contents_s.end(); i++){
608 content_t c = ndef->getId(*i);
609 if(c == CONTENT_IGNORE)
611 std::map<content_t, std::list<ActiveABM> >::iterator j;
613 if(j == m_aabms.end()){
614 std::list<ActiveABM> aabmlist;
615 m_aabms[c] = aabmlist;
618 j->second.push_back(aabm);
622 void apply(MapBlock *block)
627 ServerMap *map = &m_env->getServerMap();
630 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
631 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
632 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
634 MapNode n = block->getNodeNoEx(p0);
635 content_t c = n.getContent();
636 v3s16 p = p0 + block->getPosRelative();
638 std::map<content_t, std::list<ActiveABM> >::iterator j;
640 if(j == m_aabms.end())
643 for(std::list<ActiveABM>::iterator
644 i = j->second.begin(); i != j->second.end(); i++)
646 if(myrand() % i->chance != 0)
649 // Find out how many objects the block contains
650 u32 active_object_count = block->m_static_objects.m_active.size();
651 // Find out how many objects this and all the neighbors contain
652 u32 active_object_count_wider = 0;
653 for(s16 x=-1; x<=1; x++)
654 for(s16 y=-1; y<=1; y++)
655 for(s16 z=-1; z<=1; z++)
657 MapBlock *block2 = map->getBlockNoCreateNoEx(
658 block->getPos() + v3s16(x,y,z));
661 active_object_count_wider +=
662 block2->m_static_objects.m_active.size()
663 + block2->m_static_objects.m_stored.size();
666 // Call all the trigger variations
667 i->abm->trigger(m_env, p, n);
668 i->abm->trigger(m_env, p, n,
669 active_object_count, active_object_count_wider);
675 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
677 // Get time difference
679 u32 stamp = block->getTimestamp();
680 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
681 dtime_s = m_game_time - block->getTimestamp();
682 dtime_s += additional_dtime;
684 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
685 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
687 // Set current time as timestamp
688 block->setTimestampNoChangedFlag(m_game_time);
690 /*infostream<<"ServerEnvironment::activateBlock(): block is "
691 <<dtime_s<<" seconds old."<<std::endl;*/
693 // Activate stored objects
694 activateObjects(block);
697 bool changed = block->m_node_metadata->step((float)dtime_s);
701 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
702 event.p = block->getPos();
703 m_map->dispatchEvent(&event);
705 block->raiseModified(MOD_STATE_WRITE_NEEDED,
706 "node metadata modified in activateBlock");
709 /* Handle ActiveBlockModifiers */
710 ABMHandler abmhandler(m_abms, dtime_s, this, false);
711 abmhandler.apply(block);
714 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
716 m_abms.push_back(ABMWithState(abm));
719 void ServerEnvironment::clearAllObjects()
721 infostream<<"ServerEnvironment::clearAllObjects(): "
722 <<"Removing all active objects"<<std::endl;
723 core::list<u16> objects_to_remove;
724 for(core::map<u16, ServerActiveObject*>::Iterator
725 i = m_active_objects.getIterator();
726 i.atEnd()==false; i++)
728 ServerActiveObject* obj = i.getNode()->getValue();
729 u16 id = i.getNode()->getKey();
730 v3f objectpos = obj->getBasePosition();
731 // Delete static object if block is loaded
732 if(obj->m_static_exists){
733 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
735 block->m_static_objects.remove(id);
736 block->raiseModified(MOD_STATE_WRITE_NEEDED,
738 obj->m_static_exists = false;
741 // If known by some client, don't delete immediately
742 if(obj->m_known_by_count > 0){
743 obj->m_pending_deactivation = true;
744 obj->m_removed = true;
748 // Tell the object about removal
749 obj->removingFromEnvironment();
750 // Deregister in scripting api
751 scriptapi_rm_object_reference(m_lua, obj);
753 // Delete active object
754 if(obj->environmentDeletes())
756 // Id to be removed from m_active_objects
757 objects_to_remove.push_back(id);
759 // Remove references from m_active_objects
760 for(core::list<u16>::Iterator i = objects_to_remove.begin();
761 i != objects_to_remove.end(); i++)
763 m_active_objects.remove(*i);
766 core::list<v3s16> loadable_blocks;
767 infostream<<"ServerEnvironment::clearAllObjects(): "
768 <<"Listing all loadable blocks"<<std::endl;
769 m_map->listAllLoadableBlocks(loadable_blocks);
770 infostream<<"ServerEnvironment::clearAllObjects(): "
771 <<"Done listing all loadable blocks: "
772 <<loadable_blocks.size()
773 <<", now clearing"<<std::endl;
774 u32 report_interval = loadable_blocks.size() / 10;
775 u32 num_blocks_checked = 0;
776 u32 num_blocks_cleared = 0;
777 u32 num_objs_cleared = 0;
778 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
779 i != loadable_blocks.end(); i++)
782 MapBlock *block = m_map->emergeBlock(p, false);
784 errorstream<<"ServerEnvironment::clearAllObjects(): "
785 <<"Failed to emerge block "<<PP(p)<<std::endl;
788 u32 num_stored = block->m_static_objects.m_stored.size();
789 u32 num_active = block->m_static_objects.m_active.size();
790 if(num_stored != 0 || num_active != 0){
791 block->m_static_objects.m_stored.clear();
792 block->m_static_objects.m_active.clear();
793 block->raiseModified(MOD_STATE_WRITE_NEEDED,
795 num_objs_cleared += num_stored + num_active;
796 num_blocks_cleared++;
798 num_blocks_checked++;
800 if(num_blocks_checked % report_interval == 0){
801 float percent = 100.0 * (float)num_blocks_checked /
802 loadable_blocks.size();
803 infostream<<"ServerEnvironment::clearAllObjects(): "
804 <<"Cleared "<<num_objs_cleared<<" objects"
805 <<" in "<<num_blocks_cleared<<" blocks ("
806 <<percent<<"%)"<<std::endl;
809 infostream<<"ServerEnvironment::clearAllObjects(): "
810 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
811 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
814 void ServerEnvironment::step(float dtime)
816 DSTACK(__FUNCTION_NAME);
818 //TimeTaker timer("ServerEnv step");
821 bool footprints = g_settings->getBool("footprints");
827 m_game_time_fraction_counter += dtime;
828 u32 inc_i = (u32)m_game_time_fraction_counter;
829 m_game_time += inc_i;
830 m_game_time_fraction_counter -= (float)inc_i;
837 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
838 for(core::list<Player*>::Iterator i = m_players.begin();
839 i != m_players.end(); i++)
843 // Ignore disconnected players
844 if(player->peer_id == 0)
847 v3f playerpos = player->getPosition();
850 player->move(dtime, *m_map, 100*BS);
853 Add footsteps to grass
857 // Get node that is at BS/4 under player
858 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
860 MapNode n = m_map->getNode(bottompos);
861 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
863 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
864 m_map->setNode(bottompos, n);
867 catch(InvalidPositionException &e)
875 Manage active block list
877 if(m_active_blocks_management_interval.step(dtime, 2.0))
879 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
881 Get player block positions
883 core::list<v3s16> players_blockpos;
884 for(core::list<Player*>::Iterator
885 i = m_players.begin();
886 i != m_players.end(); i++)
889 // Ignore disconnected players
890 if(player->peer_id == 0)
892 v3s16 blockpos = getNodeBlockPos(
893 floatToInt(player->getPosition(), BS));
894 players_blockpos.push_back(blockpos);
898 Update list of active blocks, collecting changes
900 const s16 active_block_range = g_settings->getS16("active_block_range");
901 core::map<v3s16, bool> blocks_removed;
902 core::map<v3s16, bool> blocks_added;
903 m_active_blocks.update(players_blockpos, active_block_range,
904 blocks_removed, blocks_added);
907 Handle removed blocks
910 // Convert active objects that are no more in active blocks to static
911 deactivateFarObjects(false);
913 for(core::map<v3s16, bool>::Iterator
914 i = blocks_removed.getIterator();
915 i.atEnd()==false; i++)
917 v3s16 p = i.getNode()->getKey();
919 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
920 <<") became inactive"<<std::endl;*/
922 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
926 // Set current time as timestamp (and let it set ChangedFlag)
927 block->setTimestamp(m_game_time);
934 for(core::map<v3s16, bool>::Iterator
935 i = blocks_added.getIterator();
936 i.atEnd()==false; i++)
938 v3s16 p = i.getNode()->getKey();
940 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
941 <<") became active"<<std::endl;*/
943 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
945 // Block needs to be fetched first
946 m_emerger->queueBlockEmerge(p, false);
947 m_active_blocks.m_list.remove(p);
951 activateBlock(block);
956 Mess around in active blocks
958 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
960 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
964 for(core::map<v3s16, bool>::Iterator
965 i = m_active_blocks.m_list.getIterator();
966 i.atEnd()==false; i++)
968 v3s16 p = i.getNode()->getKey();
970 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
971 <<") being handled"<<std::endl;*/
973 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
977 // Reset block usage timer
978 block->resetUsageTimer();
980 // Set current time as timestamp
981 block->setTimestampNoChangedFlag(m_game_time);
982 // If time has changed much from the one on disk,
983 // set block to be saved when it is unloaded
984 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
985 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
986 "Timestamp older than 60s (step)");
989 bool changed = block->m_node_metadata->step(dtime);
993 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
995 m_map->dispatchEvent(&event);
997 block->raiseModified(MOD_STATE_WRITE_NEEDED,
998 "node metadata modified in step");
1003 const float abm_interval = 1.0;
1004 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1006 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1007 TimeTaker timer("modify in active blocks");
1009 // Initialize handling of ActiveBlockModifiers
1010 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1012 for(core::map<v3s16, bool>::Iterator
1013 i = m_active_blocks.m_list.getIterator();
1014 i.atEnd()==false; i++)
1016 v3s16 p = i.getNode()->getKey();
1018 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1019 <<") being handled"<<std::endl;*/
1021 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1025 // Set current time as timestamp
1026 block->setTimestampNoChangedFlag(m_game_time);
1028 /* Handle ActiveBlockModifiers */
1029 abmhandler.apply(block);
1032 u32 time_ms = timer.stop(true);
1033 u32 max_time_ms = 200;
1034 if(time_ms > max_time_ms){
1035 infostream<<"WARNING: active block modifiers took "
1036 <<time_ms<<"ms (longer than "
1037 <<max_time_ms<<"ms)"<<std::endl;
1042 Step script environment (run global on_step())
1044 scriptapi_environment_step(m_lua, dtime);
1050 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1051 //TimeTaker timer("Step active objects");
1053 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1055 // This helps the objects to send data at the same time
1056 bool send_recommended = false;
1057 m_send_recommended_timer += dtime;
1058 if(m_send_recommended_timer > getSendRecommendedInterval())
1060 m_send_recommended_timer -= getSendRecommendedInterval();
1061 send_recommended = true;
1064 for(core::map<u16, ServerActiveObject*>::Iterator
1065 i = m_active_objects.getIterator();
1066 i.atEnd()==false; i++)
1068 ServerActiveObject* obj = i.getNode()->getValue();
1069 // Remove non-peaceful mobs on peaceful mode
1070 if(g_settings->getBool("only_peaceful_mobs")){
1071 if(!obj->isPeaceful())
1072 obj->m_removed = true;
1074 // Don't step if is to be removed or stored statically
1075 if(obj->m_removed || obj->m_pending_deactivation)
1078 obj->step(dtime, send_recommended);
1079 // Read messages from object
1080 while(obj->m_messages_out.size() > 0)
1082 m_active_object_messages.push_back(
1083 obj->m_messages_out.pop_front());
1089 Manage active objects
1091 if(m_object_management_interval.step(dtime, 0.5))
1093 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1095 Remove objects that satisfy (m_removed && m_known_by_count==0)
1097 removeRemovedObjects();
1101 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1103 core::map<u16, ServerActiveObject*>::Node *n;
1104 n = m_active_objects.find(id);
1107 return n->getValue();
1110 bool isFreeServerActiveObjectId(u16 id,
1111 core::map<u16, ServerActiveObject*> &objects)
1116 for(core::map<u16, ServerActiveObject*>::Iterator
1117 i = objects.getIterator();
1118 i.atEnd()==false; i++)
1120 if(i.getNode()->getKey() == id)
1126 u16 getFreeServerActiveObjectId(
1127 core::map<u16, ServerActiveObject*> &objects)
1132 if(isFreeServerActiveObjectId(new_id, objects))
1142 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1145 u16 id = addActiveObjectRaw(object, true);
1149 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1153 v3f objectpos = obj->getBasePosition();
1155 // The block in which the object resides in
1156 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1159 Update the static data
1162 // Create new static object
1163 std::string staticdata = obj->getStaticData();
1164 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1165 // Add to the block where the object is located in
1166 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1167 // Get or generate the block
1168 MapBlock *block = m_map->emergeBlock(blockpos);
1170 bool succeeded = false;
1174 block->m_static_objects.insert(0, s_obj);
1175 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1176 "addActiveObjectAsStatic");
1180 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1181 <<"Could not find or generate "
1182 <<"a block for storing static object"<<std::endl;
1186 if(obj->environmentDeletes())
1193 Finds out what new objects have been added to
1194 inside a radius around a position
1196 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1197 core::map<u16, bool> ¤t_objects,
1198 core::map<u16, bool> &added_objects)
1200 v3f pos_f = intToFloat(pos, BS);
1201 f32 radius_f = radius * BS;
1203 Go through the object list,
1204 - discard m_removed objects,
1205 - discard objects that are too far away,
1206 - discard objects that are found in current_objects.
1207 - add remaining objects to added_objects
1209 for(core::map<u16, ServerActiveObject*>::Iterator
1210 i = m_active_objects.getIterator();
1211 i.atEnd()==false; i++)
1213 u16 id = i.getNode()->getKey();
1215 ServerActiveObject *object = i.getNode()->getValue();
1218 // Discard if removed
1219 if(object->m_removed)
1221 if(object->unlimitedTransferDistance() == false){
1222 // Discard if too far
1223 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1224 if(distance_f > radius_f)
1227 // Discard if already on current_objects
1228 core::map<u16, bool>::Node *n;
1229 n = current_objects.find(id);
1232 // Add to added_objects
1233 added_objects.insert(id, false);
1238 Finds out what objects have been removed from
1239 inside a radius around a position
1241 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1242 core::map<u16, bool> ¤t_objects,
1243 core::map<u16, bool> &removed_objects)
1245 v3f pos_f = intToFloat(pos, BS);
1246 f32 radius_f = radius * BS;
1248 Go through current_objects; object is removed if:
1249 - object is not found in m_active_objects (this is actually an
1250 error condition; objects should be set m_removed=true and removed
1251 only after all clients have been informed about removal), or
1252 - object has m_removed=true, or
1253 - object is too far away
1255 for(core::map<u16, bool>::Iterator
1256 i = current_objects.getIterator();
1257 i.atEnd()==false; i++)
1259 u16 id = i.getNode()->getKey();
1260 ServerActiveObject *object = getActiveObject(id);
1263 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1264 <<" object in current_objects is NULL"<<std::endl;
1265 removed_objects.insert(id, false);
1269 if(object->m_removed)
1271 removed_objects.insert(id, false);
1275 // If transfer distance is unlimited, don't remove
1276 if(object->unlimitedTransferDistance())
1279 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1281 if(distance_f >= radius_f)
1283 removed_objects.insert(id, false);
1291 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1293 if(m_active_object_messages.size() == 0)
1294 return ActiveObjectMessage(0);
1296 return m_active_object_messages.pop_front();
1300 ************ Private methods *************
1303 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1307 if(object->getId() == 0){
1308 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1311 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1312 <<"no free ids available"<<std::endl;
1313 if(object->environmentDeletes())
1317 object->setId(new_id);
1320 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1321 <<"supplied with id "<<object->getId()<<std::endl;
1323 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1325 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1326 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1327 if(object->environmentDeletes())
1331 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1332 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1334 m_active_objects.insert(object->getId(), object);
1336 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1337 <<"Added id="<<object->getId()<<"; there are now "
1338 <<m_active_objects.size()<<" active objects."
1341 // Register reference in scripting api (must be done before post-init)
1342 scriptapi_add_object_reference(m_lua, object);
1343 // Post-initialize object
1344 object->addedToEnvironment();
1346 // Add static data to block
1347 if(object->isStaticAllowed())
1349 // Add static object to active static list of the block
1350 v3f objectpos = object->getBasePosition();
1351 std::string staticdata = object->getStaticData();
1352 StaticObject s_obj(object->getType(), objectpos, staticdata);
1353 // Add to the block where the object is located in
1354 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1355 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1358 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1359 object->m_static_exists = true;
1360 object->m_static_block = blockpos;
1363 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1364 "addActiveObjectRaw");
1367 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1368 <<"could not find block for storing id="<<object->getId()
1369 <<" statically"<<std::endl;
1373 return object->getId();
1377 Remove objects that satisfy (m_removed && m_known_by_count==0)
1379 void ServerEnvironment::removeRemovedObjects()
1381 core::list<u16> objects_to_remove;
1382 for(core::map<u16, ServerActiveObject*>::Iterator
1383 i = m_active_objects.getIterator();
1384 i.atEnd()==false; i++)
1386 u16 id = i.getNode()->getKey();
1387 ServerActiveObject* obj = i.getNode()->getValue();
1388 // This shouldn't happen but check it
1391 infostream<<"NULL object found in ServerEnvironment"
1392 <<" while finding removed objects. id="<<id<<std::endl;
1393 // Id to be removed from m_active_objects
1394 objects_to_remove.push_back(id);
1399 We will delete objects that are marked as removed or thatare
1400 waiting for deletion after deactivation
1402 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1406 Delete static data from block if is marked as removed
1408 if(obj->m_static_exists && obj->m_removed)
1410 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1413 block->m_static_objects.remove(id);
1414 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1415 "removeRemovedObjects");
1416 obj->m_static_exists = false;
1420 // If m_known_by_count > 0, don't actually remove.
1421 if(obj->m_known_by_count > 0)
1424 // Tell the object about removal
1425 obj->removingFromEnvironment();
1426 // Deregister in scripting api
1427 scriptapi_rm_object_reference(m_lua, obj);
1430 if(obj->environmentDeletes())
1432 // Id to be removed from m_active_objects
1433 objects_to_remove.push_back(id);
1435 // Remove references from m_active_objects
1436 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1437 i != objects_to_remove.end(); i++)
1439 m_active_objects.remove(*i);
1443 static void print_hexdump(std::ostream &o, const std::string &data)
1445 const int linelength = 16;
1446 for(int l=0; ; l++){
1447 int i0 = linelength * l;
1448 bool at_end = false;
1449 int thislinelength = linelength;
1450 if(i0 + thislinelength > (int)data.size()){
1451 thislinelength = data.size() - i0;
1454 for(int di=0; di<linelength; di++){
1457 if(di<thislinelength)
1458 snprintf(buf, 4, "%.2x ", data[i]);
1460 snprintf(buf, 4, " ");
1464 for(int di=0; di<thislinelength; di++){
1478 Convert stored objects from blocks near the players to active.
1480 void ServerEnvironment::activateObjects(MapBlock *block)
1484 // Ignore if no stored objects (to not set changed flag)
1485 if(block->m_static_objects.m_stored.size() == 0)
1487 verbosestream<<"ServerEnvironment::activateObjects(): "
1488 <<"activating objects of block "<<PP(block->getPos())
1489 <<" ("<<block->m_static_objects.m_stored.size()
1490 <<" objects)"<<std::endl;
1491 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1493 errorstream<<"suspiciously large amount of objects detected: "
1494 <<block->m_static_objects.m_stored.size()<<" in "
1495 <<PP(block->getPos())
1496 <<"; removing all of them."<<std::endl;
1497 // Clear stored list
1498 block->m_static_objects.m_stored.clear();
1499 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1500 "stored list cleared in activateObjects due to "
1501 "large amount of objects");
1504 // A list for objects that couldn't be converted to static for some
1505 // reason. They will be stored back.
1506 core::list<StaticObject> new_stored;
1507 // Loop through stored static objects
1508 for(core::list<StaticObject>::Iterator
1509 i = block->m_static_objects.m_stored.begin();
1510 i != block->m_static_objects.m_stored.end(); i++)
1512 /*infostream<<"Server: Creating an active object from "
1513 <<"static data"<<std::endl;*/
1514 StaticObject &s_obj = *i;
1515 // Create an active object from the data
1516 ServerActiveObject *obj = ServerActiveObject::create
1517 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1518 // If couldn't create object, store static data back.
1521 errorstream<<"ServerEnvironment::activateObjects(): "
1522 <<"failed to create active object from static object "
1523 <<"in block "<<PP(s_obj.pos/BS)
1524 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1525 print_hexdump(verbosestream, s_obj.data);
1527 new_stored.push_back(s_obj);
1530 verbosestream<<"ServerEnvironment::activateObjects(): "
1531 <<"activated static object pos="<<PP(s_obj.pos/BS)
1532 <<" type="<<(int)s_obj.type<<std::endl;
1533 // This will also add the object to the active static list
1534 addActiveObjectRaw(obj, false);
1536 // Clear stored list
1537 block->m_static_objects.m_stored.clear();
1538 // Add leftover failed stuff to stored list
1539 for(core::list<StaticObject>::Iterator
1540 i = new_stored.begin();
1541 i != new_stored.end(); i++)
1543 StaticObject &s_obj = *i;
1544 block->m_static_objects.m_stored.push_back(s_obj);
1547 Note: Block hasn't really been modified here.
1548 The objects have just been activated and moved from the stored
1549 static list to the active static list.
1550 As such, the block is essentially the same.
1551 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1552 Otherwise there would be a huge amount of unnecessary I/O.
1557 Convert objects that are not standing inside active blocks to static.
1559 If m_known_by_count != 0, active object is not deleted, but static
1560 data is still updated.
1562 If force_delete is set, active object is deleted nevertheless. It
1563 shall only be set so in the destructor of the environment.
1565 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1567 core::list<u16> objects_to_remove;
1568 for(core::map<u16, ServerActiveObject*>::Iterator
1569 i = m_active_objects.getIterator();
1570 i.atEnd()==false; i++)
1572 ServerActiveObject* obj = i.getNode()->getValue();
1575 // Do not deactivate if static data creation not allowed
1576 if(!force_delete && !obj->isStaticAllowed())
1579 // If pending deactivation, let removeRemovedObjects() do it
1580 if(!force_delete && obj->m_pending_deactivation)
1583 u16 id = i.getNode()->getKey();
1584 v3f objectpos = obj->getBasePosition();
1586 // The block in which the object resides in
1587 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1589 // If block is active, don't remove
1590 if(!force_delete && m_active_blocks.contains(blockpos_o))
1593 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1594 <<"deactivating object id="<<id<<" on inactive block "
1595 <<PP(blockpos_o)<<std::endl;
1597 // If known by some client, don't immediately delete.
1598 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1601 Update the static data
1604 if(obj->isStaticAllowed())
1606 // Create new static object
1607 std::string staticdata_new = obj->getStaticData();
1608 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1610 bool stays_in_same_block = false;
1611 bool data_changed = true;
1613 if(obj->m_static_exists){
1614 if(obj->m_static_block == blockpos_o)
1615 stays_in_same_block = true;
1617 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1619 core::map<u16, StaticObject>::Node *n =
1620 block->m_static_objects.m_active.find(id);
1622 StaticObject static_old = n->getValue();
1624 float save_movem = obj->getMinimumSavedMovement();
1626 if(static_old.data == staticdata_new &&
1627 (static_old.pos - objectpos).getLength() < save_movem)
1628 data_changed = false;
1630 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1631 <<"id="<<id<<" m_static_exists=true but "
1632 <<"static data doesn't actually exist in "
1633 <<PP(obj->m_static_block)<<std::endl;
1637 bool shall_be_written = (!stays_in_same_block || data_changed);
1639 // Delete old static object
1640 if(obj->m_static_exists)
1642 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1645 block->m_static_objects.remove(id);
1646 obj->m_static_exists = false;
1647 // Only mark block as modified if data changed considerably
1648 if(shall_be_written)
1649 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1650 "deactivateFarObjects: Static data "
1651 "changed considerably");
1655 // Add to the block where the object is located in
1656 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1657 // Get or generate the block
1658 MapBlock *block = m_map->emergeBlock(blockpos);
1662 if(block->m_static_objects.m_stored.size() >= 49){
1663 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1664 <<" statically but block "<<PP(blockpos)
1665 <<" already contains "
1666 <<block->m_static_objects.m_stored.size()
1667 <<" (over 49) objects."
1668 <<" Forcing delete."<<std::endl;
1669 force_delete = true;
1671 u16 new_id = pending_delete ? id : 0;
1672 block->m_static_objects.insert(new_id, s_obj);
1674 // Only mark block as modified if data changed considerably
1675 if(shall_be_written)
1676 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1677 "deactivateFarObjects: Static data "
1678 "changed considerably");
1680 obj->m_static_exists = true;
1681 obj->m_static_block = block->getPos();
1686 errorstream<<"ServerEnv: Could not find or generate "
1687 <<"a block for storing id="<<obj->getId()
1688 <<" statically"<<std::endl;
1695 If known by some client, set pending deactivation.
1696 Otherwise delete it immediately.
1699 if(pending_delete && !force_delete)
1701 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1702 <<"object id="<<id<<" is known by clients"
1703 <<"; not deleting yet"<<std::endl;
1705 obj->m_pending_deactivation = true;
1709 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1710 <<"object id="<<id<<" is not known by clients"
1711 <<"; deleting"<<std::endl;
1713 // Tell the object about removal
1714 obj->removingFromEnvironment();
1715 // Deregister in scripting api
1716 scriptapi_rm_object_reference(m_lua, obj);
1718 // Delete active object
1719 if(obj->environmentDeletes())
1721 // Id to be removed from m_active_objects
1722 objects_to_remove.push_back(id);
1725 // Remove references from m_active_objects
1726 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1727 i != objects_to_remove.end(); i++)
1729 m_active_objects.remove(*i);
1740 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1741 ITextureSource *texturesource, IGameDef *gamedef,
1742 IrrlichtDevice *irr):
1745 m_texturesource(texturesource),
1751 ClientEnvironment::~ClientEnvironment()
1753 // delete active objects
1754 for(core::map<u16, ClientActiveObject*>::Iterator
1755 i = m_active_objects.getIterator();
1756 i.atEnd()==false; i++)
1758 delete i.getNode()->getValue();
1765 void ClientEnvironment::addPlayer(Player *player)
1767 DSTACK(__FUNCTION_NAME);
1769 It is a failure if player is local and there already is a local
1772 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1774 Environment::addPlayer(player);
1777 LocalPlayer * ClientEnvironment::getLocalPlayer()
1779 for(core::list<Player*>::Iterator i = m_players.begin();
1780 i != m_players.end(); i++)
1782 Player *player = *i;
1783 if(player->isLocal())
1784 return (LocalPlayer*)player;
1789 void ClientEnvironment::step(float dtime)
1791 DSTACK(__FUNCTION_NAME);
1793 // Get some settings
1794 bool free_move = g_settings->getBool("free_move");
1795 bool footprints = g_settings->getBool("footprints");
1798 LocalPlayer *lplayer = getLocalPlayer();
1800 // collision info queue
1801 core::list<CollisionInfo> player_collisions;
1804 Get the speed the player is going
1806 bool is_climbing = lplayer->is_climbing;
1808 f32 player_speed = lplayer->getSpeed().getLength();
1811 Maximum position increment
1813 //f32 position_max_increment = 0.05*BS;
1814 f32 position_max_increment = 0.1*BS;
1816 // Maximum time increment (for collision detection etc)
1817 // time = distance / speed
1818 f32 dtime_max_increment = 1;
1819 if(player_speed > 0.001)
1820 dtime_max_increment = position_max_increment / player_speed;
1822 // Maximum time increment is 10ms or lower
1823 if(dtime_max_increment > 0.01)
1824 dtime_max_increment = 0.01;
1826 // Don't allow overly huge dtime
1830 f32 dtime_downcount = dtime;
1833 Stuff that has a maximum time increment
1842 if(dtime_downcount > dtime_max_increment)
1844 dtime_part = dtime_max_increment;
1845 dtime_downcount -= dtime_part;
1849 dtime_part = dtime_downcount;
1851 Setting this to 0 (no -=dtime_part) disables an infinite loop
1852 when dtime_part is so small that dtime_downcount -= dtime_part
1855 dtime_downcount = 0;
1863 v3f lplayerpos = lplayer->getPosition();
1866 if(free_move == false && is_climbing == false)
1869 v3f speed = lplayer->getSpeed();
1870 if(lplayer->swimming_up == false)
1871 speed.Y -= 9.81 * BS * dtime_part * 2;
1874 if(lplayer->in_water_stable || lplayer->in_water)
1876 f32 max_down = 2.0*BS;
1877 if(speed.Y < -max_down) speed.Y = -max_down;
1880 if(speed.getLength() > max)
1882 speed = speed / speed.getLength() * max;
1886 lplayer->setSpeed(speed);
1891 This also does collision detection.
1893 lplayer->move(dtime_part, *m_map, position_max_increment,
1894 &player_collisions);
1897 while(dtime_downcount > 0.001);
1899 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1901 for(core::list<CollisionInfo>::Iterator
1902 i = player_collisions.begin();
1903 i != player_collisions.end(); i++)
1905 CollisionInfo &info = *i;
1906 if(info.t == COLLISION_FALL)
1908 //f32 tolerance = BS*10; // 2 without damage
1909 f32 tolerance = BS*12; // 3 without damage
1911 if(info.speed > tolerance)
1913 f32 damage_f = (info.speed - tolerance)/BS*factor;
1914 u16 damage = (u16)(damage_f+0.5);
1915 if(lplayer->hp > damage)
1916 lplayer->hp -= damage;
1920 ClientEnvEvent event;
1921 event.type = CEE_PLAYER_DAMAGE;
1922 event.player_damage.amount = damage;
1923 m_client_event_queue.push_back(event);
1929 A quick draft of lava damage
1931 if(m_lava_hurt_interval.step(dtime, 1.0))
1933 v3f pf = lplayer->getPosition();
1935 // Feet, middle and head
1936 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1937 MapNode n1 = m_map->getNodeNoEx(p1);
1938 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1939 MapNode n2 = m_map->getNodeNoEx(p2);
1940 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1941 MapNode n3 = m_map->getNodeNoEx(p2);
1943 u32 damage_per_second = 0;
1944 damage_per_second = MYMAX(damage_per_second,
1945 m_gamedef->ndef()->get(n1).damage_per_second);
1946 damage_per_second = MYMAX(damage_per_second,
1947 m_gamedef->ndef()->get(n2).damage_per_second);
1948 damage_per_second = MYMAX(damage_per_second,
1949 m_gamedef->ndef()->get(n3).damage_per_second);
1951 if(damage_per_second != 0)
1953 ClientEnvEvent event;
1954 event.type = CEE_PLAYER_DAMAGE;
1955 event.player_damage.amount = damage_per_second;
1956 m_client_event_queue.push_back(event);
1961 Stuff that can be done in an arbitarily large dtime
1963 for(core::list<Player*>::Iterator i = m_players.begin();
1964 i != m_players.end(); i++)
1966 Player *player = *i;
1967 v3f playerpos = player->getPosition();
1970 Handle non-local players
1972 if(player->isLocal() == false)
1975 player->move(dtime, *m_map, 100*BS);
1979 // Update lighting on all players on client
1980 u8 light = LIGHT_MAX;
1983 v3s16 p = player->getLightPosition();
1984 MapNode n = m_map->getNode(p);
1985 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
1987 catch(InvalidPositionException &e) {}
1988 player->updateLight(light);
1991 Add footsteps to grass
1995 // Get node that is at BS/4 under player
1996 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1998 MapNode n = m_map->getNode(bottompos);
1999 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2001 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2002 m_map->setNode(bottompos, n);
2003 // Update mesh on client
2004 if(m_map->mapType() == MAPTYPE_CLIENT)
2006 v3s16 p_blocks = getNodeBlockPos(bottompos);
2007 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2008 //b->updateMesh(getDayNightRatio());
2009 b->setMeshExpired(true);
2013 catch(InvalidPositionException &e)
2020 Step active objects and update lighting of them
2023 for(core::map<u16, ClientActiveObject*>::Iterator
2024 i = m_active_objects.getIterator();
2025 i.atEnd()==false; i++)
2027 ClientActiveObject* obj = i.getNode()->getValue();
2029 obj->step(dtime, this);
2031 if(m_active_object_light_update_interval.step(dtime, 0.21))
2034 //u8 light = LIGHT_MAX;
2038 v3s16 p = obj->getLightPosition();
2039 MapNode n = m_map->getNode(p);
2040 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2042 catch(InvalidPositionException &e) {}
2043 obj->updateLight(light);
2048 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2050 m_map->updateMeshes(blockpos, getDayNightRatio());
2053 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2055 m_map->expireMeshes(only_daynight_diffed);
2058 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2060 core::map<u16, ClientActiveObject*>::Node *n;
2061 n = m_active_objects.find(id);
2064 return n->getValue();
2067 bool isFreeClientActiveObjectId(u16 id,
2068 core::map<u16, ClientActiveObject*> &objects)
2073 for(core::map<u16, ClientActiveObject*>::Iterator
2074 i = objects.getIterator();
2075 i.atEnd()==false; i++)
2077 if(i.getNode()->getKey() == id)
2083 u16 getFreeClientActiveObjectId(
2084 core::map<u16, ClientActiveObject*> &objects)
2089 if(isFreeClientActiveObjectId(new_id, objects))
2099 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2102 if(object->getId() == 0)
2104 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2107 infostream<<"ClientEnvironment::addActiveObject(): "
2108 <<"no free ids available"<<std::endl;
2112 object->setId(new_id);
2114 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2116 infostream<<"ClientEnvironment::addActiveObject(): "
2117 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2121 infostream<<"ClientEnvironment::addActiveObject(): "
2122 <<"added (id="<<object->getId()<<")"<<std::endl;
2123 m_active_objects.insert(object->getId(), object);
2124 object->addToScene(m_smgr, m_texturesource, m_irr);
2125 { // Update lighting immediately
2129 v3s16 p = object->getLightPosition();
2130 MapNode n = m_map->getNode(p);
2131 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2133 catch(InvalidPositionException &e) {}
2134 object->updateLight(light);
2136 return object->getId();
2139 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2140 const std::string &init_data)
2142 ClientActiveObject* obj =
2143 ClientActiveObject::create(type, m_gamedef, this);
2146 infostream<<"ClientEnvironment::addActiveObject(): "
2147 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2154 obj->initialize(init_data);
2156 addActiveObject(obj);
2159 void ClientEnvironment::removeActiveObject(u16 id)
2161 infostream<<"ClientEnvironment::removeActiveObject(): "
2162 <<"id="<<id<<std::endl;
2163 ClientActiveObject* obj = getActiveObject(id);
2166 infostream<<"ClientEnvironment::removeActiveObject(): "
2167 <<"id="<<id<<" not found"<<std::endl;
2170 obj->removeFromScene();
2172 m_active_objects.remove(id);
2175 void ClientEnvironment::processActiveObjectMessage(u16 id,
2176 const std::string &data)
2178 ClientActiveObject* obj = getActiveObject(id);
2181 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2182 <<" got message for id="<<id<<", which doesn't exist."
2186 obj->processMessage(data);
2190 Callbacks for activeobjects
2193 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2195 LocalPlayer *lplayer = getLocalPlayer();
2199 if(lplayer->hp > damage)
2200 lplayer->hp -= damage;
2205 ClientEnvEvent event;
2206 event.type = CEE_PLAYER_DAMAGE;
2207 event.player_damage.amount = damage;
2208 m_client_event_queue.push_back(event);
2212 Client likes to call these
2215 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2216 core::array<DistanceSortedActiveObject> &dest)
2218 for(core::map<u16, ClientActiveObject*>::Iterator
2219 i = m_active_objects.getIterator();
2220 i.atEnd()==false; i++)
2222 ClientActiveObject* obj = i.getNode()->getValue();
2224 f32 d = (obj->getPosition() - origin).getLength();
2229 DistanceSortedActiveObject dso(obj, d);
2231 dest.push_back(dso);
2235 ClientEnvEvent ClientEnvironment::getClientEvent()
2237 if(m_client_event_queue.size() == 0)
2239 ClientEnvEvent event;
2240 event.type = CEE_NONE;
2243 return m_client_event_queue.pop_front();
2246 #endif // #ifndef SERVER