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
41 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
43 Environment::Environment():
48 Environment::~Environment()
51 for(core::list<Player*>::Iterator i = m_players.begin();
52 i != m_players.end(); i++)
58 void Environment::addPlayer(Player *player)
60 DSTACK(__FUNCTION_NAME);
62 Check that peer_ids are unique.
63 Also check that names are unique.
64 Exception: there can be multiple players with peer_id=0
66 // If peer id is non-zero, it has to be unique.
67 if(player->peer_id != 0)
68 assert(getPlayer(player->peer_id) == NULL);
69 // Name has to be unique.
70 assert(getPlayer(player->getName()) == NULL);
72 m_players.push_back(player);
75 void Environment::removePlayer(u16 peer_id)
77 DSTACK(__FUNCTION_NAME);
79 for(core::list<Player*>::Iterator i = m_players.begin();
80 i != m_players.end(); i++)
83 if(player->peer_id != peer_id)
88 // See if there is an another one
89 // (shouldn't be, but just to be sure)
94 Player * Environment::getPlayer(u16 peer_id)
96 for(core::list<Player*>::Iterator i = m_players.begin();
97 i != m_players.end(); i++)
100 if(player->peer_id == peer_id)
106 Player * Environment::getPlayer(const char *name)
108 for(core::list<Player*>::Iterator i = m_players.begin();
109 i != m_players.end(); i++)
112 if(strcmp(player->getName(), name) == 0)
118 Player * Environment::getRandomConnectedPlayer()
120 core::list<Player*> connected_players = getPlayers(true);
121 u32 chosen_one = myrand() % connected_players.size();
123 for(core::list<Player*>::Iterator
124 i = connected_players.begin();
125 i != connected_players.end(); i++)
137 Player * Environment::getNearestConnectedPlayer(v3f pos)
139 core::list<Player*> connected_players = getPlayers(true);
141 Player *nearest_player = NULL;
142 for(core::list<Player*>::Iterator
143 i = connected_players.begin();
144 i != connected_players.end(); i++)
147 f32 d = player->getPosition().getDistanceFrom(pos);
148 if(d < nearest_d || nearest_player == NULL)
151 nearest_player = player;
154 return nearest_player;
157 core::list<Player*> Environment::getPlayers()
162 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
164 core::list<Player*> newlist;
165 for(core::list<Player*>::Iterator
166 i = m_players.begin();
167 i != m_players.end(); i++)
171 if(ignore_disconnected)
173 // Ignore disconnected players
174 if(player->peer_id == 0)
178 newlist.push_back(player);
183 void Environment::printPlayers(std::ostream &o)
185 o<<"Players in environment:"<<std::endl;
186 for(core::list<Player*>::Iterator i = m_players.begin();
187 i != m_players.end(); i++)
190 o<<"Player peer_id="<<player->peer_id<<std::endl;
194 /*void Environment::setDayNightRatio(u32 r)
196 getDayNightRatio() = r;
199 u32 Environment::getDayNightRatio()
201 //return getDayNightRatio();
202 return time_to_daynight_ratio(m_time_of_day);
209 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
212 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
213 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
214 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
221 void ActiveBlockList::update(core::list<v3s16> &active_positions,
223 core::map<v3s16, bool> &blocks_removed,
224 core::map<v3s16, bool> &blocks_added)
229 core::map<v3s16, bool> newlist;
230 for(core::list<v3s16>::Iterator i = active_positions.begin();
231 i != active_positions.end(); i++)
233 fillRadiusBlock(*i, radius, newlist);
237 Find out which blocks on the old list are not on the new list
239 // Go through old list
240 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
241 i.atEnd()==false; i++)
243 v3s16 p = i.getNode()->getKey();
244 // If not on new list, it's been removed
245 if(newlist.find(p) == NULL)
246 blocks_removed.insert(p, true);
250 Find out which blocks on the new list are not on the old list
252 // Go through new list
253 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
254 i.atEnd()==false; i++)
256 v3s16 p = i.getNode()->getKey();
257 // If not on old list, it's been added
258 if(m_list.find(p) == NULL)
259 blocks_added.insert(p, true);
266 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
267 i.atEnd()==false; i++)
269 v3s16 p = i.getNode()->getKey();
270 m_list.insert(p, true);
278 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
279 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
284 m_random_spawn_timer(3),
285 m_send_recommended_timer(0),
287 m_game_time_fraction_counter(0)
291 ServerEnvironment::~ServerEnvironment()
293 // Clear active block list.
294 // This makes the next one delete all active objects.
295 m_active_blocks.clear();
297 // Convert all objects to static and delete the active objects
298 deactivateFarObjects(true);
303 // Delete ActiveBlockModifiers
304 for(core::list<ABMWithState>::Iterator
305 i = m_abms.begin(); i != m_abms.end(); i++){
310 void ServerEnvironment::serializePlayers(const std::string &savedir)
312 std::string players_path = savedir + "/players";
313 fs::CreateDir(players_path);
315 core::map<Player*, bool> saved_players;
317 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
318 for(u32 i=0; i<player_files.size(); i++)
320 if(player_files[i].dir)
323 // Full path to this file
324 std::string path = players_path + "/" + player_files[i].name;
326 //infostream<<"Checking player file "<<path<<std::endl;
328 // Load player to see what is its name
329 ServerRemotePlayer testplayer(this);
331 // Open file and deserialize
332 std::ifstream is(path.c_str(), std::ios_base::binary);
333 if(is.good() == false)
335 infostream<<"Failed to read "<<path<<std::endl;
338 testplayer.deSerialize(is);
341 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
343 // Search for the player
344 std::string playername = testplayer.getName();
345 Player *player = getPlayer(playername.c_str());
348 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
352 //infostream<<"Found matching player, overwriting."<<std::endl;
354 // OK, found. Save player there.
356 // Open file and serialize
357 std::ofstream os(path.c_str(), std::ios_base::binary);
358 if(os.good() == false)
360 infostream<<"Failed to overwrite "<<path<<std::endl;
363 player->serialize(os);
364 saved_players.insert(player, true);
368 for(core::list<Player*>::Iterator i = m_players.begin();
369 i != m_players.end(); i++)
372 if(saved_players.find(player) != NULL)
374 /*infostream<<"Player "<<player->getName()
375 <<" was already saved."<<std::endl;*/
378 std::string playername = player->getName();
379 // Don't save unnamed player
382 //infostream<<"Not saving unnamed player."<<std::endl;
388 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
389 playername = "player";
390 std::string path = players_path + "/" + playername;
392 for(u32 i=0; i<1000; i++)
394 if(fs::PathExists(path) == false)
399 path = players_path + "/" + playername + itos(i);
403 infostream<<"Didn't find free file for player"<<std::endl;
408 /*infostream<<"Saving player "<<player->getName()<<" to "
410 // Open file and serialize
411 std::ofstream os(path.c_str(), std::ios_base::binary);
412 if(os.good() == false)
414 infostream<<"Failed to overwrite "<<path<<std::endl;
417 player->serialize(os);
418 saved_players.insert(player, true);
422 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
425 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
427 std::string players_path = savedir + "/players";
429 core::map<Player*, bool> saved_players;
431 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
432 for(u32 i=0; i<player_files.size(); i++)
434 if(player_files[i].dir)
437 // Full path to this file
438 std::string path = players_path + "/" + player_files[i].name;
440 infostream<<"Checking player file "<<path<<std::endl;
442 // Load player to see what is its name
443 ServerRemotePlayer testplayer(this);
445 // Open file and deserialize
446 std::ifstream is(path.c_str(), std::ios_base::binary);
447 if(is.good() == false)
449 infostream<<"Failed to read "<<path<<std::endl;
452 testplayer.deSerialize(is);
455 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
457 infostream<<"Not loading player with invalid name: "
458 <<testplayer.getName()<<std::endl;
461 infostream<<"Loaded test player with name "<<testplayer.getName()
464 // Search for the player
465 std::string playername = testplayer.getName();
466 Player *player = getPlayer(playername.c_str());
467 bool newplayer = false;
470 infostream<<"Is a new player"<<std::endl;
471 player = new ServerRemotePlayer(this);
475 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
479 infostream<<"Reading player "<<testplayer.getName()<<" from "
481 // Open file and deserialize
482 std::ifstream is(path.c_str(), std::ios_base::binary);
483 if(is.good() == false)
485 infostream<<"Failed to read "<<path<<std::endl;
488 srp->deSerialize(is);
489 srp->m_last_good_position = srp->getBasePosition();
490 srp->m_last_good_position_age = 0;
498 void ServerEnvironment::saveMeta(const std::string &savedir)
500 std::string path = savedir + "/env_meta.txt";
502 // Open file and serialize
503 std::ofstream os(path.c_str(), std::ios_base::binary);
504 if(os.good() == false)
506 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
508 throw SerializationError("Couldn't save env meta");
512 args.setU64("game_time", m_game_time);
513 args.setU64("time_of_day", getTimeOfDay());
518 void ServerEnvironment::loadMeta(const std::string &savedir)
520 std::string path = savedir + "/env_meta.txt";
522 // Open file and deserialize
523 std::ifstream is(path.c_str(), std::ios_base::binary);
524 if(is.good() == false)
526 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
528 throw SerializationError("Couldn't load env meta");
536 throw SerializationError
537 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
539 std::getline(is, line);
540 std::string trimmedline = trim(line);
541 if(trimmedline == "EnvArgsEnd")
543 args.parseConfigLine(line);
547 m_game_time = args.getU64("game_time");
548 }catch(SettingNotFoundException &e){
549 // Getting this is crucial, otherwise timestamps are useless
550 throw SerializationError("Couldn't load env meta game_time");
554 m_time_of_day = args.getU64("time_of_day");
555 }catch(SettingNotFoundException &e){
556 // This is not as important
557 m_time_of_day = 9000;
563 ActiveBlockModifier *abm;
570 ServerEnvironment *m_env;
571 std::map<content_t, std::list<ActiveABM> > m_aabms;
573 ABMHandler(core::list<ABMWithState> &abms,
574 float dtime_s, ServerEnvironment *env,
580 INodeDefManager *ndef = env->getGameDef()->ndef();
581 for(core::list<ABMWithState>::Iterator
582 i = abms.begin(); i != abms.end(); i++){
583 ActiveBlockModifier *abm = i->abm;
584 float trigger_interval = abm->getTriggerInterval();
585 if(trigger_interval < 0.001)
586 trigger_interval = 0.001;
589 if(i->timer < trigger_interval)
591 i->timer -= trigger_interval;
595 float intervals = dtime_s / trigger_interval;
596 float chance = abm->getTriggerChance();
599 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
602 std::set<std::string> contents_s = abm->getTriggerContents();
603 for(std::set<std::string>::iterator
604 i = contents_s.begin(); i != contents_s.end(); i++){
605 content_t c = ndef->getId(*i);
606 if(c == CONTENT_IGNORE)
608 std::map<content_t, std::list<ActiveABM> >::iterator j;
610 if(j == m_aabms.end()){
611 std::list<ActiveABM> aabmlist;
612 m_aabms[c] = aabmlist;
615 j->second.push_back(aabm);
619 void apply(MapBlock *block)
624 ServerMap *map = &m_env->getServerMap();
627 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
628 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
629 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
631 MapNode n = block->getNodeNoEx(p0);
632 content_t c = n.getContent();
633 v3s16 p = p0 + block->getPosRelative();
635 std::map<content_t, std::list<ActiveABM> >::iterator j;
637 if(j == m_aabms.end())
640 for(std::list<ActiveABM>::iterator
641 i = j->second.begin(); i != j->second.end(); i++)
643 if(myrand() % i->chance != 0)
646 // Find out how many objects the block contains
647 u32 active_object_count = block->m_static_objects.m_active.size();
648 // Find out how many objects this and all the neighbors contain
649 u32 active_object_count_wider = 0;
650 for(s16 x=-1; x<=1; x++)
651 for(s16 y=-1; y<=1; y++)
652 for(s16 z=-1; z<=1; z++)
654 MapBlock *block2 = map->getBlockNoCreateNoEx(
655 block->getPos() + v3s16(x,y,z));
658 active_object_count_wider +=
659 block2->m_static_objects.m_active.size()
660 + block2->m_static_objects.m_stored.size();
663 // Call all the trigger variations
664 i->abm->trigger(m_env, p, n);
665 i->abm->trigger(m_env, p, n,
666 active_object_count, active_object_count_wider);
672 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
674 // Get time difference
676 u32 stamp = block->getTimestamp();
677 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
678 dtime_s = m_game_time - block->getTimestamp();
679 dtime_s += additional_dtime;
681 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
682 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
684 // Set current time as timestamp
685 block->setTimestampNoChangedFlag(m_game_time);
687 /*infostream<<"ServerEnvironment::activateBlock(): block is "
688 <<dtime_s<<" seconds old."<<std::endl;*/
690 // Activate stored objects
691 activateObjects(block);
694 bool changed = block->m_node_metadata->step((float)dtime_s);
698 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
699 event.p = block->getPos();
700 m_map->dispatchEvent(&event);
702 block->raiseModified(MOD_STATE_WRITE_NEEDED,
703 "node metadata modified in activateBlock");
706 /* Handle ActiveBlockModifiers */
707 ABMHandler abmhandler(m_abms, dtime_s, this, false);
708 abmhandler.apply(block);
711 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
713 m_abms.push_back(ABMWithState(abm));
716 void ServerEnvironment::clearAllObjects()
718 infostream<<"ServerEnvironment::clearAllObjects(): "
719 <<"Removing all active objects"<<std::endl;
720 core::list<u16> objects_to_remove;
721 for(core::map<u16, ServerActiveObject*>::Iterator
722 i = m_active_objects.getIterator();
723 i.atEnd()==false; i++)
725 ServerActiveObject* obj = i.getNode()->getValue();
726 u16 id = i.getNode()->getKey();
727 v3f objectpos = obj->getBasePosition();
728 // Delete static object if block is loaded
729 if(obj->m_static_exists){
730 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
732 block->m_static_objects.remove(id);
733 block->raiseModified(MOD_STATE_WRITE_NEEDED,
735 obj->m_static_exists = false;
738 // If known by some client, don't delete immediately
739 if(obj->m_known_by_count > 0){
740 obj->m_pending_deactivation = true;
741 obj->m_removed = true;
744 // Deregister in scripting api
745 scriptapi_rm_object_reference(m_lua, obj);
746 // Delete active object
748 // Id to be removed from m_active_objects
749 objects_to_remove.push_back(id);
751 // Remove references from m_active_objects
752 for(core::list<u16>::Iterator i = objects_to_remove.begin();
753 i != objects_to_remove.end(); i++)
755 m_active_objects.remove(*i);
758 core::list<v3s16> loadable_blocks;
759 infostream<<"ServerEnvironment::clearAllObjects(): "
760 <<"Listing all loadable blocks"<<std::endl;
761 m_map->listAllLoadableBlocks(loadable_blocks);
762 infostream<<"ServerEnvironment::clearAllObjects(): "
763 <<"Done listing all loadable blocks: "
764 <<loadable_blocks.size()
765 <<", now clearing"<<std::endl;
766 u32 report_interval = loadable_blocks.size() / 10;
767 u32 num_blocks_checked = 0;
768 u32 num_blocks_cleared = 0;
769 u32 num_objs_cleared = 0;
770 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
771 i != loadable_blocks.end(); i++)
774 MapBlock *block = m_map->emergeBlock(p, false);
776 errorstream<<"ServerEnvironment::clearAllObjects(): "
777 <<"Failed to emerge block "<<PP(p)<<std::endl;
780 u32 num_stored = block->m_static_objects.m_stored.size();
781 u32 num_active = block->m_static_objects.m_active.size();
782 if(num_stored != 0 || num_active != 0){
783 block->m_static_objects.m_stored.clear();
784 block->m_static_objects.m_active.clear();
785 block->raiseModified(MOD_STATE_WRITE_NEEDED,
787 num_objs_cleared += num_stored + num_active;
788 num_blocks_cleared++;
790 num_blocks_checked++;
792 if(num_blocks_checked % report_interval == 0){
793 float percent = 100.0 * (float)num_blocks_checked /
794 loadable_blocks.size();
795 infostream<<"ServerEnvironment::clearAllObjects(): "
796 <<"Cleared "<<num_objs_cleared<<" objects"
797 <<" in "<<num_blocks_cleared<<" blocks ("
798 <<percent<<"%)"<<std::endl;
801 infostream<<"ServerEnvironment::clearAllObjects(): "
802 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
803 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
806 void ServerEnvironment::step(float dtime)
808 DSTACK(__FUNCTION_NAME);
810 //TimeTaker timer("ServerEnv step");
813 bool footprints = g_settings->getBool("footprints");
819 m_game_time_fraction_counter += dtime;
820 u32 inc_i = (u32)m_game_time_fraction_counter;
821 m_game_time += inc_i;
822 m_game_time_fraction_counter -= (float)inc_i;
829 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
830 for(core::list<Player*>::Iterator i = m_players.begin();
831 i != m_players.end(); i++)
835 // Ignore disconnected players
836 if(player->peer_id == 0)
839 v3f playerpos = player->getPosition();
842 player->move(dtime, *m_map, 100*BS);
845 Add footsteps to grass
849 // Get node that is at BS/4 under player
850 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
852 MapNode n = m_map->getNode(bottompos);
853 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
855 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
856 m_map->setNode(bottompos, n);
859 catch(InvalidPositionException &e)
867 Manage active block list
869 if(m_active_blocks_management_interval.step(dtime, 2.0))
871 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
873 Get player block positions
875 core::list<v3s16> players_blockpos;
876 for(core::list<Player*>::Iterator
877 i = m_players.begin();
878 i != m_players.end(); i++)
881 // Ignore disconnected players
882 if(player->peer_id == 0)
884 v3s16 blockpos = getNodeBlockPos(
885 floatToInt(player->getPosition(), BS));
886 players_blockpos.push_back(blockpos);
890 Update list of active blocks, collecting changes
892 const s16 active_block_range = g_settings->getS16("active_block_range");
893 core::map<v3s16, bool> blocks_removed;
894 core::map<v3s16, bool> blocks_added;
895 m_active_blocks.update(players_blockpos, active_block_range,
896 blocks_removed, blocks_added);
899 Handle removed blocks
902 // Convert active objects that are no more in active blocks to static
903 deactivateFarObjects(false);
905 for(core::map<v3s16, bool>::Iterator
906 i = blocks_removed.getIterator();
907 i.atEnd()==false; i++)
909 v3s16 p = i.getNode()->getKey();
911 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
912 <<") became inactive"<<std::endl;*/
914 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
918 // Set current time as timestamp (and let it set ChangedFlag)
919 block->setTimestamp(m_game_time);
926 for(core::map<v3s16, bool>::Iterator
927 i = blocks_added.getIterator();
928 i.atEnd()==false; i++)
930 v3s16 p = i.getNode()->getKey();
932 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
933 <<") became active"<<std::endl;*/
935 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
937 // Block needs to be fetched first
938 m_emerger->queueBlockEmerge(p, false);
939 m_active_blocks.m_list.remove(p);
943 activateBlock(block);
948 Mess around in active blocks
950 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
952 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
956 for(core::map<v3s16, bool>::Iterator
957 i = m_active_blocks.m_list.getIterator();
958 i.atEnd()==false; i++)
960 v3s16 p = i.getNode()->getKey();
962 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
963 <<") being handled"<<std::endl;*/
965 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
969 // Reset block usage timer
970 block->resetUsageTimer();
972 // Set current time as timestamp
973 block->setTimestampNoChangedFlag(m_game_time);
974 // If time has changed much from the one on disk,
975 // set block to be saved when it is unloaded
976 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
977 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
978 "Timestamp older than 60s (step)");
981 bool changed = block->m_node_metadata->step(dtime);
985 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
987 m_map->dispatchEvent(&event);
989 block->raiseModified(MOD_STATE_WRITE_NEEDED,
990 "node metadata modified in step");
995 const float abm_interval = 1.0;
996 if(m_active_block_modifier_interval.step(dtime, abm_interval))
998 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
999 TimeTaker timer("modify in active blocks");
1001 // Initialize handling of ActiveBlockModifiers
1002 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1004 for(core::map<v3s16, bool>::Iterator
1005 i = m_active_blocks.m_list.getIterator();
1006 i.atEnd()==false; i++)
1008 v3s16 p = i.getNode()->getKey();
1010 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1011 <<") being handled"<<std::endl;*/
1013 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1017 // Set current time as timestamp
1018 block->setTimestampNoChangedFlag(m_game_time);
1020 /* Handle ActiveBlockModifiers */
1021 abmhandler.apply(block);
1024 u32 time_ms = timer.stop(true);
1025 u32 max_time_ms = 200;
1026 if(time_ms > max_time_ms){
1027 infostream<<"WARNING: active block modifiers took "
1028 <<time_ms<<"ms (longer than "
1029 <<max_time_ms<<"ms)"<<std::endl;
1034 Step script environment (run global on_step())
1036 scriptapi_environment_step(m_lua, dtime);
1042 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1043 //TimeTaker timer("Step active objects");
1045 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1047 // This helps the objects to send data at the same time
1048 bool send_recommended = false;
1049 m_send_recommended_timer += dtime;
1050 if(m_send_recommended_timer > getSendRecommendedInterval())
1052 m_send_recommended_timer -= getSendRecommendedInterval();
1053 send_recommended = true;
1056 for(core::map<u16, ServerActiveObject*>::Iterator
1057 i = m_active_objects.getIterator();
1058 i.atEnd()==false; i++)
1060 ServerActiveObject* obj = i.getNode()->getValue();
1061 // Remove non-peaceful mobs on peaceful mode
1062 if(g_settings->getBool("only_peaceful_mobs")){
1063 if(!obj->isPeaceful())
1064 obj->m_removed = true;
1066 // Don't step if is to be removed or stored statically
1067 if(obj->m_removed || obj->m_pending_deactivation)
1070 obj->step(dtime, send_recommended);
1071 // Read messages from object
1072 while(obj->m_messages_out.size() > 0)
1074 m_active_object_messages.push_back(
1075 obj->m_messages_out.pop_front());
1081 Manage active objects
1083 if(m_object_management_interval.step(dtime, 0.5))
1085 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1087 Remove objects that satisfy (m_removed && m_known_by_count==0)
1089 removeRemovedObjects();
1093 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1095 core::map<u16, ServerActiveObject*>::Node *n;
1096 n = m_active_objects.find(id);
1099 return n->getValue();
1102 bool isFreeServerActiveObjectId(u16 id,
1103 core::map<u16, ServerActiveObject*> &objects)
1108 for(core::map<u16, ServerActiveObject*>::Iterator
1109 i = objects.getIterator();
1110 i.atEnd()==false; i++)
1112 if(i.getNode()->getKey() == id)
1118 u16 getFreeServerActiveObjectId(
1119 core::map<u16, ServerActiveObject*> &objects)
1124 if(isFreeServerActiveObjectId(new_id, objects))
1134 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1137 u16 id = addActiveObjectRaw(object, true);
1141 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1145 v3f objectpos = obj->getBasePosition();
1147 // The block in which the object resides in
1148 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1151 Update the static data
1154 // Create new static object
1155 std::string staticdata = obj->getStaticData();
1156 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1157 // Add to the block where the object is located in
1158 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1159 // Get or generate the block
1160 MapBlock *block = m_map->emergeBlock(blockpos);
1162 bool succeeded = false;
1166 block->m_static_objects.insert(0, s_obj);
1167 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1168 "addActiveObjectAsStatic");
1172 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1173 <<"Could not find or generate "
1174 <<"a block for storing static object"<<std::endl;
1184 Finds out what new objects have been added to
1185 inside a radius around a position
1187 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1188 core::map<u16, bool> ¤t_objects,
1189 core::map<u16, bool> &added_objects)
1191 v3f pos_f = intToFloat(pos, BS);
1192 f32 radius_f = radius * BS;
1194 Go through the object list,
1195 - discard m_removed objects,
1196 - discard objects that are too far away,
1197 - discard objects that are found in current_objects.
1198 - add remaining objects to added_objects
1200 for(core::map<u16, ServerActiveObject*>::Iterator
1201 i = m_active_objects.getIterator();
1202 i.atEnd()==false; i++)
1204 u16 id = i.getNode()->getKey();
1206 ServerActiveObject *object = i.getNode()->getValue();
1209 // Discard if removed
1210 if(object->m_removed)
1212 // Discard if too far
1213 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1214 if(distance_f > radius_f)
1216 // Discard if already on current_objects
1217 core::map<u16, bool>::Node *n;
1218 n = current_objects.find(id);
1221 // Add to added_objects
1222 added_objects.insert(id, false);
1227 Finds out what objects have been removed from
1228 inside a radius around a position
1230 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1231 core::map<u16, bool> ¤t_objects,
1232 core::map<u16, bool> &removed_objects)
1234 v3f pos_f = intToFloat(pos, BS);
1235 f32 radius_f = radius * BS;
1237 Go through current_objects; object is removed if:
1238 - object is not found in m_active_objects (this is actually an
1239 error condition; objects should be set m_removed=true and removed
1240 only after all clients have been informed about removal), or
1241 - object has m_removed=true, or
1242 - object is too far away
1244 for(core::map<u16, bool>::Iterator
1245 i = current_objects.getIterator();
1246 i.atEnd()==false; i++)
1248 u16 id = i.getNode()->getKey();
1249 ServerActiveObject *object = getActiveObject(id);
1252 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1253 <<" object in current_objects is NULL"<<std::endl;
1255 else if(object->m_removed == false)
1257 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1258 /*infostream<<"removed == false"
1259 <<"distance_f = "<<distance_f
1260 <<", radius_f = "<<radius_f<<std::endl;*/
1261 if(distance_f < radius_f)
1267 removed_objects.insert(id, false);
1271 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1273 if(m_active_object_messages.size() == 0)
1274 return ActiveObjectMessage(0);
1276 return m_active_object_messages.pop_front();
1280 ************ Private methods *************
1283 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1287 if(object->getId() == 0){
1288 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1291 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1292 <<"no free ids available"<<std::endl;
1296 object->setId(new_id);
1299 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1300 <<"supplied with id "<<object->getId()<<std::endl;
1302 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1304 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1305 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1309 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1310 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1312 m_active_objects.insert(object->getId(), object);
1314 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1315 <<"Added id="<<object->getId()<<"; there are now "
1316 <<m_active_objects.size()<<" active objects."
1319 // Add static object to active static list of the block
1320 v3f objectpos = object->getBasePosition();
1321 std::string staticdata = object->getStaticData();
1322 StaticObject s_obj(object->getType(), objectpos, staticdata);
1323 // Add to the block where the object is located in
1324 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1325 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1328 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1329 object->m_static_exists = true;
1330 object->m_static_block = blockpos;
1333 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1334 "addActiveObjectRaw");
1337 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1338 <<"could not find block for storing id="<<object->getId()
1339 <<" statically"<<std::endl;
1342 // Register reference in scripting api (must be done before post-init)
1343 scriptapi_add_object_reference(m_lua, object);
1344 // Post-initialize object
1345 object->addedToEnvironment();
1347 return object->getId();
1351 Remove objects that satisfy (m_removed && m_known_by_count==0)
1353 void ServerEnvironment::removeRemovedObjects()
1355 core::list<u16> objects_to_remove;
1356 for(core::map<u16, ServerActiveObject*>::Iterator
1357 i = m_active_objects.getIterator();
1358 i.atEnd()==false; i++)
1360 u16 id = i.getNode()->getKey();
1361 ServerActiveObject* obj = i.getNode()->getValue();
1362 // This shouldn't happen but check it
1365 infostream<<"NULL object found in ServerEnvironment"
1366 <<" while finding removed objects. id="<<id<<std::endl;
1367 // Id to be removed from m_active_objects
1368 objects_to_remove.push_back(id);
1373 We will delete objects that are marked as removed or thatare
1374 waiting for deletion after deactivation
1376 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1380 Delete static data from block if is marked as removed
1382 if(obj->m_static_exists && obj->m_removed)
1384 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1387 block->m_static_objects.remove(id);
1388 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1389 "removeRemovedObjects");
1390 obj->m_static_exists = false;
1394 // If m_known_by_count > 0, don't actually remove.
1395 if(obj->m_known_by_count > 0)
1398 // Deregister in scripting api
1399 scriptapi_rm_object_reference(m_lua, obj);
1403 // Id to be removed from m_active_objects
1404 objects_to_remove.push_back(id);
1406 // Remove references from m_active_objects
1407 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1408 i != objects_to_remove.end(); i++)
1410 m_active_objects.remove(*i);
1414 static void print_hexdump(std::ostream &o, const std::string &data)
1416 const int linelength = 16;
1417 for(int l=0; ; l++){
1418 int i0 = linelength * l;
1419 bool at_end = false;
1420 int thislinelength = linelength;
1421 if(i0 + thislinelength > (int)data.size()){
1422 thislinelength = data.size() - i0;
1425 for(int di=0; di<linelength; di++){
1428 if(di<thislinelength)
1429 snprintf(buf, 4, "%.2x ", data[i]);
1431 snprintf(buf, 4, " ");
1435 for(int di=0; di<thislinelength; di++){
1449 Convert stored objects from blocks near the players to active.
1451 void ServerEnvironment::activateObjects(MapBlock *block)
1455 // Ignore if no stored objects (to not set changed flag)
1456 if(block->m_static_objects.m_stored.size() == 0)
1458 verbosestream<<"ServerEnvironment::activateObjects(): "
1459 <<"activating objects of block "<<PP(block->getPos())
1460 <<" ("<<block->m_static_objects.m_stored.size()
1461 <<" objects)"<<std::endl;
1462 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1464 errorstream<<"suspiciously large amount of objects detected: "
1465 <<block->m_static_objects.m_stored.size()<<" in "
1466 <<PP(block->getPos())
1467 <<"; removing all of them."<<std::endl;
1468 // Clear stored list
1469 block->m_static_objects.m_stored.clear();
1470 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1471 "stored list cleared in activateObjects due to "
1472 "large amount of objects");
1475 // A list for objects that couldn't be converted to static for some
1476 // reason. They will be stored back.
1477 core::list<StaticObject> new_stored;
1478 // Loop through stored static objects
1479 for(core::list<StaticObject>::Iterator
1480 i = block->m_static_objects.m_stored.begin();
1481 i != block->m_static_objects.m_stored.end(); i++)
1483 /*infostream<<"Server: Creating an active object from "
1484 <<"static data"<<std::endl;*/
1485 StaticObject &s_obj = *i;
1486 // Create an active object from the data
1487 ServerActiveObject *obj = ServerActiveObject::create
1488 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1489 // If couldn't create object, store static data back.
1492 errorstream<<"ServerEnvironment::activateObjects(): "
1493 <<"failed to create active object from static object "
1494 <<"in block "<<PP(s_obj.pos/BS)
1495 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1496 print_hexdump(verbosestream, s_obj.data);
1498 new_stored.push_back(s_obj);
1501 verbosestream<<"ServerEnvironment::activateObjects(): "
1502 <<"activated static object pos="<<PP(s_obj.pos/BS)
1503 <<" type="<<(int)s_obj.type<<std::endl;
1504 // This will also add the object to the active static list
1505 addActiveObjectRaw(obj, false);
1507 // Clear stored list
1508 block->m_static_objects.m_stored.clear();
1509 // Add leftover failed stuff to stored list
1510 for(core::list<StaticObject>::Iterator
1511 i = new_stored.begin();
1512 i != new_stored.end(); i++)
1514 StaticObject &s_obj = *i;
1515 block->m_static_objects.m_stored.push_back(s_obj);
1518 Note: Block hasn't really been modified here.
1519 The objects have just been activated and moved from the stored
1520 static list to the active static list.
1521 As such, the block is essentially the same.
1522 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1523 Otherwise there would be a huge amount of unnecessary I/O.
1528 Convert objects that are not standing inside active blocks to static.
1530 If m_known_by_count != 0, active object is not deleted, but static
1531 data is still updated.
1533 If force_delete is set, active object is deleted nevertheless. It
1534 shall only be set so in the destructor of the environment.
1536 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1538 core::list<u16> objects_to_remove;
1539 for(core::map<u16, ServerActiveObject*>::Iterator
1540 i = m_active_objects.getIterator();
1541 i.atEnd()==false; i++)
1543 ServerActiveObject* obj = i.getNode()->getValue();
1545 // This shouldn't happen but check it
1548 errorstream<<"NULL object found in ServerEnvironment"
1554 // If pending deactivation, let removeRemovedObjects() do it
1555 if(obj->m_pending_deactivation)
1558 u16 id = i.getNode()->getKey();
1559 v3f objectpos = obj->getBasePosition();
1561 // The block in which the object resides in
1562 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1564 // If block is active, don't remove
1565 if(m_active_blocks.contains(blockpos_o))
1568 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1569 <<"deactivating object id="<<id<<" on inactive block "
1570 <<PP(blockpos_o)<<std::endl;
1572 // If known by some client, don't immediately delete.
1573 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1576 Update the static data
1579 // Create new static object
1580 std::string staticdata_new = obj->getStaticData();
1581 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1583 bool stays_in_same_block = false;
1584 bool data_changed = true;
1586 if(obj->m_static_exists){
1587 if(obj->m_static_block == blockpos_o)
1588 stays_in_same_block = true;
1590 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1592 core::map<u16, StaticObject>::Node *n =
1593 block->m_static_objects.m_active.find(id);
1595 StaticObject static_old = n->getValue();
1597 float save_movem = obj->getMinimumSavedMovement();
1599 if(static_old.data == staticdata_new &&
1600 (static_old.pos - objectpos).getLength() < save_movem)
1601 data_changed = false;
1603 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1604 <<"id="<<id<<" m_static_exists=true but "
1605 <<"static data doesn't actually exist in "
1606 <<PP(obj->m_static_block)<<std::endl;
1610 bool shall_be_written = (!stays_in_same_block || data_changed);
1612 // Delete old static object
1613 if(obj->m_static_exists)
1615 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1618 block->m_static_objects.remove(id);
1619 obj->m_static_exists = false;
1620 // Only mark block as modified if data changed considerably
1621 if(shall_be_written)
1622 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1623 "deactivateFarObjects: Static data "
1624 "changed considerably");
1628 // Add to the block where the object is located in
1629 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1630 // Get or generate the block
1631 MapBlock *block = m_map->emergeBlock(blockpos);
1635 if(block->m_static_objects.m_stored.size() >= 49){
1636 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1637 <<" statically but block "<<PP(blockpos)
1638 <<" already contains "
1639 <<block->m_static_objects.m_stored.size()
1640 <<" (over 49) objects."
1641 <<" Forcing delete."<<std::endl;
1642 force_delete = true;
1644 u16 new_id = pending_delete ? id : 0;
1645 block->m_static_objects.insert(new_id, s_obj);
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");
1653 obj->m_static_exists = true;
1654 obj->m_static_block = block->getPos();
1658 errorstream<<"ServerEnv: Could not find or generate "
1659 <<"a block for storing id="<<obj->getId()
1660 <<" statically"<<std::endl;
1665 If known by some client, set pending deactivation.
1666 Otherwise delete it immediately.
1671 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1672 <<"object id="<<id<<" is known by clients"
1673 <<"; not deleting yet"<<std::endl;
1675 obj->m_pending_deactivation = true;
1679 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1680 <<"object id="<<id<<" is not known by clients"
1681 <<"; deleting"<<std::endl;
1683 // Deregister in scripting api
1684 scriptapi_rm_object_reference(m_lua, obj);
1686 // Delete active object
1688 // Id to be removed from m_active_objects
1689 objects_to_remove.push_back(id);
1692 // Remove references from m_active_objects
1693 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1694 i != objects_to_remove.end(); i++)
1696 m_active_objects.remove(*i);
1707 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1708 ITextureSource *texturesource, IGameDef *gamedef):
1711 m_texturesource(texturesource),
1718 ClientEnvironment::~ClientEnvironment()
1720 // delete active objects
1721 for(core::map<u16, ClientActiveObject*>::Iterator
1722 i = m_active_objects.getIterator();
1723 i.atEnd()==false; i++)
1725 delete i.getNode()->getValue();
1732 void ClientEnvironment::addPlayer(Player *player)
1734 DSTACK(__FUNCTION_NAME);
1736 It is a failure if player is local and there already is a local
1739 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1741 Environment::addPlayer(player);
1744 LocalPlayer * ClientEnvironment::getLocalPlayer()
1746 for(core::list<Player*>::Iterator i = m_players.begin();
1747 i != m_players.end(); i++)
1749 Player *player = *i;
1750 if(player->isLocal())
1751 return (LocalPlayer*)player;
1756 void ClientEnvironment::step(float dtime)
1758 DSTACK(__FUNCTION_NAME);
1760 // Get some settings
1761 bool free_move = g_settings->getBool("free_move");
1762 bool footprints = g_settings->getBool("footprints");
1765 LocalPlayer *lplayer = getLocalPlayer();
1767 // collision info queue
1768 core::list<CollisionInfo> player_collisions;
1771 Get the speed the player is going
1773 bool is_climbing = lplayer->is_climbing;
1775 f32 player_speed = lplayer->getSpeed().getLength();
1778 Maximum position increment
1780 //f32 position_max_increment = 0.05*BS;
1781 f32 position_max_increment = 0.1*BS;
1783 // Maximum time increment (for collision detection etc)
1784 // time = distance / speed
1785 f32 dtime_max_increment = 1;
1786 if(player_speed > 0.001)
1787 dtime_max_increment = position_max_increment / player_speed;
1789 // Maximum time increment is 10ms or lower
1790 if(dtime_max_increment > 0.01)
1791 dtime_max_increment = 0.01;
1793 // Don't allow overly huge dtime
1797 f32 dtime_downcount = dtime;
1800 Stuff that has a maximum time increment
1809 if(dtime_downcount > dtime_max_increment)
1811 dtime_part = dtime_max_increment;
1812 dtime_downcount -= dtime_part;
1816 dtime_part = dtime_downcount;
1818 Setting this to 0 (no -=dtime_part) disables an infinite loop
1819 when dtime_part is so small that dtime_downcount -= dtime_part
1822 dtime_downcount = 0;
1830 v3f lplayerpos = lplayer->getPosition();
1833 if(free_move == false && is_climbing == false)
1836 v3f speed = lplayer->getSpeed();
1837 if(lplayer->swimming_up == false)
1838 speed.Y -= 9.81 * BS * dtime_part * 2;
1841 if(lplayer->in_water_stable || lplayer->in_water)
1843 f32 max_down = 2.0*BS;
1844 if(speed.Y < -max_down) speed.Y = -max_down;
1847 if(speed.getLength() > max)
1849 speed = speed / speed.getLength() * max;
1853 lplayer->setSpeed(speed);
1858 This also does collision detection.
1860 lplayer->move(dtime_part, *m_map, position_max_increment,
1861 &player_collisions);
1864 while(dtime_downcount > 0.001);
1866 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1868 for(core::list<CollisionInfo>::Iterator
1869 i = player_collisions.begin();
1870 i != player_collisions.end(); i++)
1872 CollisionInfo &info = *i;
1873 if(info.t == COLLISION_FALL)
1875 //f32 tolerance = BS*10; // 2 without damage
1876 f32 tolerance = BS*12; // 3 without damage
1878 if(info.speed > tolerance)
1880 f32 damage_f = (info.speed - tolerance)/BS*factor;
1881 u16 damage = (u16)(damage_f+0.5);
1882 if(lplayer->hp > damage)
1883 lplayer->hp -= damage;
1887 ClientEnvEvent event;
1888 event.type = CEE_PLAYER_DAMAGE;
1889 event.player_damage.amount = damage;
1890 m_client_event_queue.push_back(event);
1896 A quick draft of lava damage
1898 if(m_lava_hurt_interval.step(dtime, 1.0))
1900 v3f pf = lplayer->getPosition();
1902 // Feet, middle and head
1903 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1904 MapNode n1 = m_map->getNodeNoEx(p1);
1905 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1906 MapNode n2 = m_map->getNodeNoEx(p2);
1907 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1908 MapNode n3 = m_map->getNodeNoEx(p2);
1910 u32 damage_per_second = 0;
1911 damage_per_second = MYMAX(damage_per_second,
1912 m_gamedef->ndef()->get(n1).damage_per_second);
1913 damage_per_second = MYMAX(damage_per_second,
1914 m_gamedef->ndef()->get(n2).damage_per_second);
1915 damage_per_second = MYMAX(damage_per_second,
1916 m_gamedef->ndef()->get(n3).damage_per_second);
1918 if(damage_per_second != 0)
1920 ClientEnvEvent event;
1921 event.type = CEE_PLAYER_DAMAGE;
1922 event.player_damage.amount = damage_per_second;
1923 m_client_event_queue.push_back(event);
1928 Stuff that can be done in an arbitarily large dtime
1930 for(core::list<Player*>::Iterator i = m_players.begin();
1931 i != m_players.end(); i++)
1933 Player *player = *i;
1934 v3f playerpos = player->getPosition();
1937 Handle non-local players
1939 if(player->isLocal() == false)
1942 player->move(dtime, *m_map, 100*BS);
1946 // Update lighting on all players on client
1947 u8 light = LIGHT_MAX;
1950 v3s16 p = player->getLightPosition();
1951 MapNode n = m_map->getNode(p);
1952 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
1954 catch(InvalidPositionException &e) {}
1955 player->updateLight(light);
1958 Add footsteps to grass
1962 // Get node that is at BS/4 under player
1963 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1965 MapNode n = m_map->getNode(bottompos);
1966 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1968 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
1969 m_map->setNode(bottompos, n);
1970 // Update mesh on client
1971 if(m_map->mapType() == MAPTYPE_CLIENT)
1973 v3s16 p_blocks = getNodeBlockPos(bottompos);
1974 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1975 //b->updateMesh(getDayNightRatio());
1976 b->setMeshExpired(true);
1980 catch(InvalidPositionException &e)
1987 Step active objects and update lighting of them
1990 for(core::map<u16, ClientActiveObject*>::Iterator
1991 i = m_active_objects.getIterator();
1992 i.atEnd()==false; i++)
1994 ClientActiveObject* obj = i.getNode()->getValue();
1996 obj->step(dtime, this);
1998 if(m_active_object_light_update_interval.step(dtime, 0.21))
2001 //u8 light = LIGHT_MAX;
2005 v3s16 p = obj->getLightPosition();
2006 MapNode n = m_map->getNode(p);
2007 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2009 catch(InvalidPositionException &e) {}
2010 obj->updateLight(light);
2015 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2017 m_map->updateMeshes(blockpos, getDayNightRatio());
2020 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2022 m_map->expireMeshes(only_daynight_diffed);
2025 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2027 core::map<u16, ClientActiveObject*>::Node *n;
2028 n = m_active_objects.find(id);
2031 return n->getValue();
2034 bool isFreeClientActiveObjectId(u16 id,
2035 core::map<u16, ClientActiveObject*> &objects)
2040 for(core::map<u16, ClientActiveObject*>::Iterator
2041 i = objects.getIterator();
2042 i.atEnd()==false; i++)
2044 if(i.getNode()->getKey() == id)
2050 u16 getFreeClientActiveObjectId(
2051 core::map<u16, ClientActiveObject*> &objects)
2056 if(isFreeClientActiveObjectId(new_id, objects))
2066 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2069 if(object->getId() == 0)
2071 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2074 infostream<<"ClientEnvironment::addActiveObject(): "
2075 <<"no free ids available"<<std::endl;
2079 object->setId(new_id);
2081 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2083 infostream<<"ClientEnvironment::addActiveObject(): "
2084 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2088 infostream<<"ClientEnvironment::addActiveObject(): "
2089 <<"added (id="<<object->getId()<<")"<<std::endl;
2090 m_active_objects.insert(object->getId(), object);
2091 object->addToScene(m_smgr, m_texturesource);
2092 { // Update lighting immediately
2096 v3s16 p = object->getLightPosition();
2097 MapNode n = m_map->getNode(p);
2098 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2100 catch(InvalidPositionException &e) {}
2101 object->updateLight(light);
2103 return object->getId();
2106 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2107 const std::string &init_data)
2109 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2112 infostream<<"ClientEnvironment::addActiveObject(): "
2113 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2120 obj->initialize(init_data);
2122 addActiveObject(obj);
2125 void ClientEnvironment::removeActiveObject(u16 id)
2127 infostream<<"ClientEnvironment::removeActiveObject(): "
2128 <<"id="<<id<<std::endl;
2129 ClientActiveObject* obj = getActiveObject(id);
2132 infostream<<"ClientEnvironment::removeActiveObject(): "
2133 <<"id="<<id<<" not found"<<std::endl;
2136 obj->removeFromScene();
2138 m_active_objects.remove(id);
2141 void ClientEnvironment::processActiveObjectMessage(u16 id,
2142 const std::string &data)
2144 ClientActiveObject* obj = getActiveObject(id);
2147 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2148 <<" got message for id="<<id<<", which doesn't exist."
2152 obj->processMessage(data);
2156 Callbacks for activeobjects
2159 void ClientEnvironment::damageLocalPlayer(u8 damage)
2161 LocalPlayer *lplayer = getLocalPlayer();
2164 if(lplayer->hp > damage)
2165 lplayer->hp -= damage;
2169 ClientEnvEvent event;
2170 event.type = CEE_PLAYER_DAMAGE;
2171 event.player_damage.amount = damage;
2172 m_client_event_queue.push_back(event);
2176 Client likes to call these
2179 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2180 core::array<DistanceSortedActiveObject> &dest)
2182 for(core::map<u16, ClientActiveObject*>::Iterator
2183 i = m_active_objects.getIterator();
2184 i.atEnd()==false; i++)
2186 ClientActiveObject* obj = i.getNode()->getValue();
2188 f32 d = (obj->getPosition() - origin).getLength();
2193 DistanceSortedActiveObject dso(obj, d);
2195 dest.push_back(dso);
2199 ClientEnvEvent ClientEnvironment::getClientEvent()
2201 if(m_client_event_queue.size() == 0)
2203 ClientEnvEvent event;
2204 event.type = CEE_NONE;
2207 return m_client_event_queue.pop_front();
2210 #endif // #ifndef SERVER