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);
477 infostream<<"Reading player "<<testplayer.getName()<<" from "
479 // Open file and deserialize
480 std::ifstream is(path.c_str(), std::ios_base::binary);
481 if(is.good() == false)
483 infostream<<"Failed to read "<<path<<std::endl;
486 player->deSerialize(is);
494 void ServerEnvironment::saveMeta(const std::string &savedir)
496 std::string path = savedir + "/env_meta.txt";
498 // Open file and serialize
499 std::ofstream os(path.c_str(), std::ios_base::binary);
500 if(os.good() == false)
502 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
504 throw SerializationError("Couldn't save env meta");
508 args.setU64("game_time", m_game_time);
509 args.setU64("time_of_day", getTimeOfDay());
514 void ServerEnvironment::loadMeta(const std::string &savedir)
516 std::string path = savedir + "/env_meta.txt";
518 // Open file and deserialize
519 std::ifstream is(path.c_str(), std::ios_base::binary);
520 if(is.good() == false)
522 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
524 throw SerializationError("Couldn't load env meta");
532 throw SerializationError
533 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
535 std::getline(is, line);
536 std::string trimmedline = trim(line);
537 if(trimmedline == "EnvArgsEnd")
539 args.parseConfigLine(line);
543 m_game_time = args.getU64("game_time");
544 }catch(SettingNotFoundException &e){
545 // Getting this is crucial, otherwise timestamps are useless
546 throw SerializationError("Couldn't load env meta game_time");
550 m_time_of_day = args.getU64("time_of_day");
551 }catch(SettingNotFoundException &e){
552 // This is not as important
553 m_time_of_day = 9000;
559 ActiveBlockModifier *abm;
566 ServerEnvironment *m_env;
567 std::map<content_t, std::list<ActiveABM> > m_aabms;
569 ABMHandler(core::list<ABMWithState> &abms,
570 float dtime_s, ServerEnvironment *env,
576 INodeDefManager *ndef = env->getGameDef()->ndef();
577 for(core::list<ABMWithState>::Iterator
578 i = abms.begin(); i != abms.end(); i++){
579 ActiveBlockModifier *abm = i->abm;
580 float trigger_interval = abm->getTriggerInterval();
581 if(trigger_interval < 0.001)
582 trigger_interval = 0.001;
585 if(i->timer < trigger_interval)
587 i->timer -= trigger_interval;
591 float intervals = dtime_s / trigger_interval;
592 float chance = abm->getTriggerChance();
595 aabm.chance = 1.0 / pow(1.0 / chance, intervals);
598 std::set<std::string> contents_s = abm->getTriggerContents();
599 for(std::set<std::string>::iterator
600 i = contents_s.begin(); i != contents_s.end(); i++){
601 content_t c = ndef->getId(*i);
602 if(c == CONTENT_IGNORE)
604 std::map<content_t, std::list<ActiveABM> >::iterator j;
606 if(j == m_aabms.end()){
607 std::list<ActiveABM> aabmlist;
608 m_aabms[c] = aabmlist;
611 j->second.push_back(aabm);
615 void apply(MapBlock *block)
620 ServerMap *map = &m_env->getServerMap();
621 // Find out how many objects the block contains
622 u32 active_object_count = block->m_static_objects.m_active.size();
623 // Find out how many objects this and all the neighbors contain
624 u32 active_object_count_wider = 0;
625 for(s16 x=-1; x<=1; x++)
626 for(s16 y=-1; y<=1; y++)
627 for(s16 z=-1; z<=1; z++)
629 MapBlock *block2 = map->getBlockNoCreateNoEx(
630 block->getPos() + v3s16(x,y,z));
633 active_object_count_wider +=
634 block2->m_static_objects.m_active.size()
635 + block2->m_static_objects.m_stored.size();
639 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
640 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
641 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
643 MapNode n = block->getNodeNoEx(p0);
644 content_t c = n.getContent();
645 v3s16 p = p0 + block->getPosRelative();
647 std::map<content_t, std::list<ActiveABM> >::iterator j;
649 if(j == m_aabms.end())
652 for(std::list<ActiveABM>::iterator
653 i = j->second.begin(); i != j->second.end(); i++){
654 if(myrand() % i->chance != 0)
656 // Call all the trigger variations
657 i->abm->trigger(m_env, p, n);
658 i->abm->trigger(m_env, p, n,
659 active_object_count, active_object_count_wider);
665 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
667 // Get time difference
669 u32 stamp = block->getTimestamp();
670 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
671 dtime_s = m_game_time - block->getTimestamp();
672 dtime_s += additional_dtime;
674 infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
675 <<stamp<<", game time: "<<m_game_time<<std::endl;
677 // Set current time as timestamp
678 block->setTimestampNoChangedFlag(m_game_time);
680 infostream<<"ServerEnvironment::activateBlock(): block is "
681 <<dtime_s<<" seconds old."<<std::endl;
683 // Activate stored objects
684 activateObjects(block);
687 bool changed = block->m_node_metadata->step((float)dtime_s);
691 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
692 event.p = block->getPos();
693 m_map->dispatchEvent(&event);
695 block->raiseModified(MOD_STATE_WRITE_NEEDED,
696 "node metadata modified in activateBlock");
699 /* Handle ActiveBlockModifiers */
700 ABMHandler abmhandler(m_abms, dtime_s, this, false);
701 abmhandler.apply(block);
704 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
706 m_abms.push_back(ABMWithState(abm));
709 void ServerEnvironment::clearAllObjects()
711 infostream<<"ServerEnvironment::clearAllObjects(): "
712 <<"Removing all active objects"<<std::endl;
713 core::list<u16> objects_to_remove;
714 for(core::map<u16, ServerActiveObject*>::Iterator
715 i = m_active_objects.getIterator();
716 i.atEnd()==false; i++)
718 ServerActiveObject* obj = i.getNode()->getValue();
719 u16 id = i.getNode()->getKey();
720 v3f objectpos = obj->getBasePosition();
721 // Delete static object if block is loaded
722 if(obj->m_static_exists){
723 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
725 block->m_static_objects.remove(id);
726 block->raiseModified(MOD_STATE_WRITE_NEEDED,
728 obj->m_static_exists = false;
731 // If known by some client, don't delete immediately
732 if(obj->m_known_by_count > 0){
733 obj->m_pending_deactivation = true;
734 obj->m_removed = true;
737 // Deregister in scripting api
738 scriptapi_rm_object_reference(m_lua, obj);
739 // Delete active object
741 // Id to be removed from m_active_objects
742 objects_to_remove.push_back(id);
744 // Remove references from m_active_objects
745 for(core::list<u16>::Iterator i = objects_to_remove.begin();
746 i != objects_to_remove.end(); i++)
748 m_active_objects.remove(*i);
751 core::list<v3s16> loadable_blocks;
752 infostream<<"ServerEnvironment::clearAllObjects(): "
753 <<"Listing all loadable blocks"<<std::endl;
754 m_map->listAllLoadableBlocks(loadable_blocks);
755 infostream<<"ServerEnvironment::clearAllObjects(): "
756 <<"Done listing all loadable blocks: "
757 <<loadable_blocks.size()
758 <<", now clearing"<<std::endl;
759 u32 report_interval = loadable_blocks.size() / 10;
760 u32 num_blocks_checked = 0;
761 u32 num_blocks_cleared = 0;
762 u32 num_objs_cleared = 0;
763 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
764 i != loadable_blocks.end(); i++)
767 MapBlock *block = m_map->emergeBlock(p, false);
769 errorstream<<"ServerEnvironment::clearAllObjects(): "
770 <<"Failed to emerge block "<<PP(p)<<std::endl;
773 u32 num_stored = block->m_static_objects.m_stored.size();
774 u32 num_active = block->m_static_objects.m_active.size();
775 if(num_stored != 0 || num_active != 0){
776 block->m_static_objects.m_stored.clear();
777 block->m_static_objects.m_active.clear();
778 block->raiseModified(MOD_STATE_WRITE_NEEDED,
780 num_objs_cleared += num_stored + num_active;
781 num_blocks_cleared++;
783 num_blocks_checked++;
785 if(num_blocks_checked % report_interval == 0){
786 float percent = 100.0 * (float)num_blocks_checked /
787 loadable_blocks.size();
788 infostream<<"ServerEnvironment::clearAllObjects(): "
789 <<"Cleared "<<num_objs_cleared<<" objects"
790 <<" in "<<num_blocks_cleared<<" blocks ("
791 <<percent<<"%)"<<std::endl;
794 infostream<<"ServerEnvironment::clearAllObjects(): "
795 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
796 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
799 void ServerEnvironment::step(float dtime)
801 DSTACK(__FUNCTION_NAME);
803 //TimeTaker timer("ServerEnv step");
806 bool footprints = g_settings->getBool("footprints");
812 m_game_time_fraction_counter += dtime;
813 u32 inc_i = (u32)m_game_time_fraction_counter;
814 m_game_time += inc_i;
815 m_game_time_fraction_counter -= (float)inc_i;
822 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
823 for(core::list<Player*>::Iterator i = m_players.begin();
824 i != m_players.end(); i++)
828 // Ignore disconnected players
829 if(player->peer_id == 0)
832 v3f playerpos = player->getPosition();
835 player->move(dtime, *m_map, 100*BS);
838 Add footsteps to grass
842 // Get node that is at BS/4 under player
843 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
845 MapNode n = m_map->getNode(bottompos);
846 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
848 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
849 m_map->setNode(bottompos, n);
852 catch(InvalidPositionException &e)
860 Manage active block list
862 if(m_active_blocks_management_interval.step(dtime, 2.0))
864 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
866 Get player block positions
868 core::list<v3s16> players_blockpos;
869 for(core::list<Player*>::Iterator
870 i = m_players.begin();
871 i != m_players.end(); i++)
874 // Ignore disconnected players
875 if(player->peer_id == 0)
877 v3s16 blockpos = getNodeBlockPos(
878 floatToInt(player->getPosition(), BS));
879 players_blockpos.push_back(blockpos);
883 Update list of active blocks, collecting changes
885 const s16 active_block_range = g_settings->getS16("active_block_range");
886 core::map<v3s16, bool> blocks_removed;
887 core::map<v3s16, bool> blocks_added;
888 m_active_blocks.update(players_blockpos, active_block_range,
889 blocks_removed, blocks_added);
892 Handle removed blocks
895 // Convert active objects that are no more in active blocks to static
896 deactivateFarObjects(false);
898 for(core::map<v3s16, bool>::Iterator
899 i = blocks_removed.getIterator();
900 i.atEnd()==false; i++)
902 v3s16 p = i.getNode()->getKey();
904 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
905 <<") became inactive"<<std::endl;*/
907 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
911 // Set current time as timestamp (and let it set ChangedFlag)
912 block->setTimestamp(m_game_time);
919 for(core::map<v3s16, bool>::Iterator
920 i = blocks_added.getIterator();
921 i.atEnd()==false; i++)
923 v3s16 p = i.getNode()->getKey();
925 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
926 <<") became active"<<std::endl;*/
928 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
930 // Block needs to be fetched first
931 m_emerger->queueBlockEmerge(p, false);
932 m_active_blocks.m_list.remove(p);
936 activateBlock(block);
941 Mess around in active blocks
943 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
945 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
949 for(core::map<v3s16, bool>::Iterator
950 i = m_active_blocks.m_list.getIterator();
951 i.atEnd()==false; i++)
953 v3s16 p = i.getNode()->getKey();
955 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
956 <<") being handled"<<std::endl;*/
958 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
962 // Reset block usage timer
963 block->resetUsageTimer();
965 // Set current time as timestamp
966 block->setTimestampNoChangedFlag(m_game_time);
967 // If time has changed much from the one on disk,
968 // set block to be saved when it is unloaded
969 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
970 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
971 "Timestamp older than 60s (step)");
974 bool changed = block->m_node_metadata->step(dtime);
978 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
980 m_map->dispatchEvent(&event);
982 block->raiseModified(MOD_STATE_WRITE_NEEDED,
983 "node metadata modified in step");
988 const float abm_interval = 1.0;
989 if(m_active_block_modifier_interval.step(dtime, abm_interval))
991 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
992 TimeTaker timer("modify in active blocks");
994 // Initialize handling of ActiveBlockModifiers
995 ABMHandler abmhandler(m_abms, abm_interval, this, true);
997 for(core::map<v3s16, bool>::Iterator
998 i = m_active_blocks.m_list.getIterator();
999 i.atEnd()==false; i++)
1001 v3s16 p = i.getNode()->getKey();
1003 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1004 <<") being handled"<<std::endl;*/
1006 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1010 // Set current time as timestamp
1011 block->setTimestampNoChangedFlag(m_game_time);
1013 /* Handle ActiveBlockModifiers */
1014 abmhandler.apply(block);
1017 u32 time_ms = timer.stop(true);
1018 u32 max_time_ms = 200;
1019 if(time_ms > max_time_ms){
1020 infostream<<"WARNING: active block modifiers took "
1021 <<time_ms<<"ms (longer than "
1022 <<max_time_ms<<"ms)"<<std::endl;
1027 Step script environment (run global on_step())
1029 scriptapi_environment_step(m_lua, dtime);
1035 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1036 //TimeTaker timer("Step active objects");
1038 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1040 // This helps the objects to send data at the same time
1041 bool send_recommended = false;
1042 m_send_recommended_timer += dtime;
1043 if(m_send_recommended_timer > getSendRecommendedInterval())
1045 m_send_recommended_timer -= getSendRecommendedInterval();
1046 send_recommended = true;
1049 for(core::map<u16, ServerActiveObject*>::Iterator
1050 i = m_active_objects.getIterator();
1051 i.atEnd()==false; i++)
1053 ServerActiveObject* obj = i.getNode()->getValue();
1054 // Remove non-peaceful mobs on peaceful mode
1055 if(g_settings->getBool("only_peaceful_mobs")){
1056 if(!obj->isPeaceful())
1057 obj->m_removed = true;
1059 // Don't step if is to be removed or stored statically
1060 if(obj->m_removed || obj->m_pending_deactivation)
1063 obj->step(dtime, send_recommended);
1064 // Read messages from object
1065 while(obj->m_messages_out.size() > 0)
1067 m_active_object_messages.push_back(
1068 obj->m_messages_out.pop_front());
1074 Manage active objects
1076 if(m_object_management_interval.step(dtime, 0.5))
1078 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1080 Remove objects that satisfy (m_removed && m_known_by_count==0)
1082 removeRemovedObjects();
1086 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1088 core::map<u16, ServerActiveObject*>::Node *n;
1089 n = m_active_objects.find(id);
1092 return n->getValue();
1095 bool isFreeServerActiveObjectId(u16 id,
1096 core::map<u16, ServerActiveObject*> &objects)
1101 for(core::map<u16, ServerActiveObject*>::Iterator
1102 i = objects.getIterator();
1103 i.atEnd()==false; i++)
1105 if(i.getNode()->getKey() == id)
1111 u16 getFreeServerActiveObjectId(
1112 core::map<u16, ServerActiveObject*> &objects)
1117 if(isFreeServerActiveObjectId(new_id, objects))
1127 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1130 u16 id = addActiveObjectRaw(object, true);
1134 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1138 v3f objectpos = obj->getBasePosition();
1140 // The block in which the object resides in
1141 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1144 Update the static data
1147 // Create new static object
1148 std::string staticdata = obj->getStaticData();
1149 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1150 // Add to the block where the object is located in
1151 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1152 // Get or generate the block
1153 MapBlock *block = m_map->emergeBlock(blockpos);
1155 bool succeeded = false;
1159 block->m_static_objects.insert(0, s_obj);
1160 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1161 "addActiveObjectAsStatic");
1165 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1166 <<"Could not find or generate "
1167 <<"a block for storing static object"<<std::endl;
1177 Finds out what new objects have been added to
1178 inside a radius around a position
1180 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1181 core::map<u16, bool> ¤t_objects,
1182 core::map<u16, bool> &added_objects)
1184 v3f pos_f = intToFloat(pos, BS);
1185 f32 radius_f = radius * BS;
1187 Go through the object list,
1188 - discard m_removed objects,
1189 - discard objects that are too far away,
1190 - discard objects that are found in current_objects.
1191 - add remaining objects to added_objects
1193 for(core::map<u16, ServerActiveObject*>::Iterator
1194 i = m_active_objects.getIterator();
1195 i.atEnd()==false; i++)
1197 u16 id = i.getNode()->getKey();
1199 ServerActiveObject *object = i.getNode()->getValue();
1202 // Discard if removed
1203 if(object->m_removed)
1205 // Discard if too far
1206 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1207 if(distance_f > radius_f)
1209 // Discard if already on current_objects
1210 core::map<u16, bool>::Node *n;
1211 n = current_objects.find(id);
1214 // Add to added_objects
1215 added_objects.insert(id, false);
1220 Finds out what objects have been removed from
1221 inside a radius around a position
1223 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1224 core::map<u16, bool> ¤t_objects,
1225 core::map<u16, bool> &removed_objects)
1227 v3f pos_f = intToFloat(pos, BS);
1228 f32 radius_f = radius * BS;
1230 Go through current_objects; object is removed if:
1231 - object is not found in m_active_objects (this is actually an
1232 error condition; objects should be set m_removed=true and removed
1233 only after all clients have been informed about removal), or
1234 - object has m_removed=true, or
1235 - object is too far away
1237 for(core::map<u16, bool>::Iterator
1238 i = current_objects.getIterator();
1239 i.atEnd()==false; i++)
1241 u16 id = i.getNode()->getKey();
1242 ServerActiveObject *object = getActiveObject(id);
1245 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1246 <<" object in current_objects is NULL"<<std::endl;
1248 else if(object->m_removed == false)
1250 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1251 /*infostream<<"removed == false"
1252 <<"distance_f = "<<distance_f
1253 <<", radius_f = "<<radius_f<<std::endl;*/
1254 if(distance_f < radius_f)
1260 removed_objects.insert(id, false);
1264 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1266 if(m_active_object_messages.size() == 0)
1267 return ActiveObjectMessage(0);
1269 return m_active_object_messages.pop_front();
1273 ************ Private methods *************
1276 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1280 if(object->getId() == 0){
1281 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1284 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1285 <<"no free ids available"<<std::endl;
1289 object->setId(new_id);
1292 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1293 <<"supplied with id "<<object->getId()<<std::endl;
1295 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1297 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1298 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1302 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1303 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1305 m_active_objects.insert(object->getId(), object);
1307 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1308 <<"Added id="<<object->getId()<<"; there are now "
1309 <<m_active_objects.size()<<" active objects."
1312 // Add static object to active static list of the block
1313 v3f objectpos = object->getBasePosition();
1314 std::string staticdata = object->getStaticData();
1315 StaticObject s_obj(object->getType(), objectpos, staticdata);
1316 // Add to the block where the object is located in
1317 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1318 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1321 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1322 object->m_static_exists = true;
1323 object->m_static_block = blockpos;
1326 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1327 "addActiveObjectRaw");
1330 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1331 <<"could not find block for storing id="<<object->getId()
1332 <<" statically"<<std::endl;
1335 // Register reference in scripting api (must be done before post-init)
1336 scriptapi_add_object_reference(m_lua, object);
1337 // Post-initialize object
1338 object->addedToEnvironment();
1340 return object->getId();
1344 Remove objects that satisfy (m_removed && m_known_by_count==0)
1346 void ServerEnvironment::removeRemovedObjects()
1348 core::list<u16> objects_to_remove;
1349 for(core::map<u16, ServerActiveObject*>::Iterator
1350 i = m_active_objects.getIterator();
1351 i.atEnd()==false; i++)
1353 u16 id = i.getNode()->getKey();
1354 ServerActiveObject* obj = i.getNode()->getValue();
1355 // This shouldn't happen but check it
1358 infostream<<"NULL object found in ServerEnvironment"
1359 <<" while finding removed objects. id="<<id<<std::endl;
1360 // Id to be removed from m_active_objects
1361 objects_to_remove.push_back(id);
1366 We will delete objects that are marked as removed or thatare
1367 waiting for deletion after deactivation
1369 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1373 Delete static data from block if is marked as removed
1375 if(obj->m_static_exists && obj->m_removed)
1377 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1380 block->m_static_objects.remove(id);
1381 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1382 "removeRemovedObjects");
1383 obj->m_static_exists = false;
1387 // If m_known_by_count > 0, don't actually remove.
1388 if(obj->m_known_by_count > 0)
1391 // Deregister in scripting api
1392 scriptapi_rm_object_reference(m_lua, obj);
1396 // Id to be removed from m_active_objects
1397 objects_to_remove.push_back(id);
1399 // Remove references from m_active_objects
1400 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1401 i != objects_to_remove.end(); i++)
1403 m_active_objects.remove(*i);
1407 static void print_hexdump(std::ostream &o, const std::string &data)
1409 const int linelength = 16;
1410 for(int l=0; ; l++){
1411 int i0 = linelength * l;
1412 bool at_end = false;
1413 int thislinelength = linelength;
1414 if(i0 + thislinelength > (int)data.size()){
1415 thislinelength = data.size() - i0;
1418 for(int di=0; di<linelength; di++){
1421 if(di<thislinelength)
1422 snprintf(buf, 4, "%.2x ", data[i]);
1424 snprintf(buf, 4, " ");
1428 for(int di=0; di<thislinelength; di++){
1442 Convert stored objects from blocks near the players to active.
1444 void ServerEnvironment::activateObjects(MapBlock *block)
1448 // Ignore if no stored objects (to not set changed flag)
1449 if(block->m_static_objects.m_stored.size() == 0)
1451 verbosestream<<"ServerEnvironment::activateObjects(): "
1452 <<"activating objects of block "<<PP(block->getPos())
1453 <<" ("<<block->m_static_objects.m_stored.size()
1454 <<" objects)"<<std::endl;
1455 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1457 errorstream<<"suspiciously large amount of objects detected: "
1458 <<block->m_static_objects.m_stored.size()<<" in "
1459 <<PP(block->getPos())
1460 <<"; removing all of them."<<std::endl;
1461 // Clear stored list
1462 block->m_static_objects.m_stored.clear();
1463 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1464 "stored list cleared in activateObjects due to "
1465 "large amount of objects");
1468 // A list for objects that couldn't be converted to static for some
1469 // reason. They will be stored back.
1470 core::list<StaticObject> new_stored;
1471 // Loop through stored static objects
1472 for(core::list<StaticObject>::Iterator
1473 i = block->m_static_objects.m_stored.begin();
1474 i != block->m_static_objects.m_stored.end(); i++)
1476 /*infostream<<"Server: Creating an active object from "
1477 <<"static data"<<std::endl;*/
1478 StaticObject &s_obj = *i;
1479 // Create an active object from the data
1480 ServerActiveObject *obj = ServerActiveObject::create
1481 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1482 // If couldn't create object, store static data back.
1485 errorstream<<"ServerEnvironment::activateObjects(): "
1486 <<"failed to create active object from static object "
1487 <<"in block "<<PP(s_obj.pos/BS)
1488 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1489 print_hexdump(verbosestream, s_obj.data);
1491 new_stored.push_back(s_obj);
1494 verbosestream<<"ServerEnvironment::activateObjects(): "
1495 <<"activated static object pos="<<PP(s_obj.pos/BS)
1496 <<" type="<<(int)s_obj.type<<std::endl;
1497 // This will also add the object to the active static list
1498 addActiveObjectRaw(obj, false);
1500 // Clear stored list
1501 block->m_static_objects.m_stored.clear();
1502 // Add leftover failed stuff to stored list
1503 for(core::list<StaticObject>::Iterator
1504 i = new_stored.begin();
1505 i != new_stored.end(); i++)
1507 StaticObject &s_obj = *i;
1508 block->m_static_objects.m_stored.push_back(s_obj);
1511 Note: Block hasn't really been modified here.
1512 The objects have just been activated and moved from the stored
1513 static list to the active static list.
1514 As such, the block is essentially the same.
1515 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1516 Otherwise there would be a huge amount of unnecessary I/O.
1521 Convert objects that are not standing inside active blocks to static.
1523 If m_known_by_count != 0, active object is not deleted, but static
1524 data is still updated.
1526 If force_delete is set, active object is deleted nevertheless. It
1527 shall only be set so in the destructor of the environment.
1529 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1531 core::list<u16> objects_to_remove;
1532 for(core::map<u16, ServerActiveObject*>::Iterator
1533 i = m_active_objects.getIterator();
1534 i.atEnd()==false; i++)
1536 ServerActiveObject* obj = i.getNode()->getValue();
1538 // This shouldn't happen but check it
1541 errorstream<<"NULL object found in ServerEnvironment"
1547 // If pending deactivation, let removeRemovedObjects() do it
1548 if(obj->m_pending_deactivation)
1551 u16 id = i.getNode()->getKey();
1552 v3f objectpos = obj->getBasePosition();
1554 // The block in which the object resides in
1555 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1557 // If block is active, don't remove
1558 if(m_active_blocks.contains(blockpos_o))
1561 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1562 <<"deactivating object id="<<id<<" on inactive block "
1563 <<PP(blockpos_o)<<std::endl;
1565 // If known by some client, don't immediately delete.
1566 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1569 Update the static data
1572 // Create new static object
1573 std::string staticdata_new = obj->getStaticData();
1574 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1576 bool stays_in_same_block = false;
1577 bool data_changed = true;
1579 if(obj->m_static_exists){
1580 if(obj->m_static_block == blockpos_o)
1581 stays_in_same_block = true;
1583 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1585 core::map<u16, StaticObject>::Node *n =
1586 block->m_static_objects.m_active.find(id);
1588 StaticObject static_old = n->getValue();
1590 float save_movem = obj->getMinimumSavedMovement();
1592 if(static_old.data == staticdata_new &&
1593 (static_old.pos - objectpos).getLength() < save_movem)
1594 data_changed = false;
1596 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1597 <<"id="<<id<<" m_static_exists=true but "
1598 <<"static data doesn't actually exist in "
1599 <<PP(obj->m_static_block)<<std::endl;
1603 bool shall_be_written = (!stays_in_same_block || data_changed);
1605 // Delete old static object
1606 if(obj->m_static_exists)
1608 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1611 block->m_static_objects.remove(id);
1612 obj->m_static_exists = false;
1613 // Only mark block as modified if data changed considerably
1614 if(shall_be_written)
1615 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1616 "deactivateFarObjects: Static data "
1617 "changed considerably");
1621 // Add to the block where the object is located in
1622 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1623 // Get or generate the block
1624 MapBlock *block = m_map->emergeBlock(blockpos);
1628 if(block->m_static_objects.m_stored.size() >= 49){
1629 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1630 <<" statically but block "<<PP(blockpos)
1631 <<" already contains "
1632 <<block->m_static_objects.m_stored.size()
1633 <<" (over 49) objects."
1634 <<" Forcing delete."<<std::endl;
1635 force_delete = true;
1637 u16 new_id = pending_delete ? id : 0;
1638 block->m_static_objects.insert(new_id, s_obj);
1640 // Only mark block as modified if data changed considerably
1641 if(shall_be_written)
1642 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1643 "deactivateFarObjects: Static data "
1644 "changed considerably");
1646 obj->m_static_exists = true;
1647 obj->m_static_block = block->getPos();
1651 errorstream<<"ServerEnv: Could not find or generate "
1652 <<"a block for storing id="<<obj->getId()
1653 <<" statically"<<std::endl;
1658 If known by some client, set pending deactivation.
1659 Otherwise delete it immediately.
1664 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1665 <<"object id="<<id<<" is known by clients"
1666 <<"; not deleting yet"<<std::endl;
1668 obj->m_pending_deactivation = true;
1672 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1673 <<"object id="<<id<<" is not known by clients"
1674 <<"; deleting"<<std::endl;
1676 // Deregister in scripting api
1677 scriptapi_rm_object_reference(m_lua, obj);
1679 // Delete active object
1681 // Id to be removed from m_active_objects
1682 objects_to_remove.push_back(id);
1685 // Remove references from m_active_objects
1686 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1687 i != objects_to_remove.end(); i++)
1689 m_active_objects.remove(*i);
1700 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1701 ITextureSource *texturesource, IGameDef *gamedef):
1704 m_texturesource(texturesource),
1711 ClientEnvironment::~ClientEnvironment()
1713 // delete active objects
1714 for(core::map<u16, ClientActiveObject*>::Iterator
1715 i = m_active_objects.getIterator();
1716 i.atEnd()==false; i++)
1718 delete i.getNode()->getValue();
1725 void ClientEnvironment::addPlayer(Player *player)
1727 DSTACK(__FUNCTION_NAME);
1729 It is a failure if player is local and there already is a local
1732 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1734 Environment::addPlayer(player);
1737 LocalPlayer * ClientEnvironment::getLocalPlayer()
1739 for(core::list<Player*>::Iterator i = m_players.begin();
1740 i != m_players.end(); i++)
1742 Player *player = *i;
1743 if(player->isLocal())
1744 return (LocalPlayer*)player;
1749 void ClientEnvironment::step(float dtime)
1751 DSTACK(__FUNCTION_NAME);
1753 // Get some settings
1754 bool free_move = g_settings->getBool("free_move");
1755 bool footprints = g_settings->getBool("footprints");
1758 LocalPlayer *lplayer = getLocalPlayer();
1760 // collision info queue
1761 core::list<CollisionInfo> player_collisions;
1764 Get the speed the player is going
1766 bool is_climbing = lplayer->is_climbing;
1768 f32 player_speed = lplayer->getSpeed().getLength();
1771 Maximum position increment
1773 //f32 position_max_increment = 0.05*BS;
1774 f32 position_max_increment = 0.1*BS;
1776 // Maximum time increment (for collision detection etc)
1777 // time = distance / speed
1778 f32 dtime_max_increment = 1;
1779 if(player_speed > 0.001)
1780 dtime_max_increment = position_max_increment / player_speed;
1782 // Maximum time increment is 10ms or lower
1783 if(dtime_max_increment > 0.01)
1784 dtime_max_increment = 0.01;
1786 // Don't allow overly huge dtime
1790 f32 dtime_downcount = dtime;
1793 Stuff that has a maximum time increment
1802 if(dtime_downcount > dtime_max_increment)
1804 dtime_part = dtime_max_increment;
1805 dtime_downcount -= dtime_part;
1809 dtime_part = dtime_downcount;
1811 Setting this to 0 (no -=dtime_part) disables an infinite loop
1812 when dtime_part is so small that dtime_downcount -= dtime_part
1815 dtime_downcount = 0;
1823 v3f lplayerpos = lplayer->getPosition();
1826 if(free_move == false && is_climbing == false)
1829 v3f speed = lplayer->getSpeed();
1830 if(lplayer->swimming_up == false)
1831 speed.Y -= 9.81 * BS * dtime_part * 2;
1834 if(lplayer->in_water_stable || lplayer->in_water)
1836 f32 max_down = 2.0*BS;
1837 if(speed.Y < -max_down) speed.Y = -max_down;
1840 if(speed.getLength() > max)
1842 speed = speed / speed.getLength() * max;
1846 lplayer->setSpeed(speed);
1851 This also does collision detection.
1853 lplayer->move(dtime_part, *m_map, position_max_increment,
1854 &player_collisions);
1857 while(dtime_downcount > 0.001);
1859 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1861 for(core::list<CollisionInfo>::Iterator
1862 i = player_collisions.begin();
1863 i != player_collisions.end(); i++)
1865 CollisionInfo &info = *i;
1866 if(info.t == COLLISION_FALL)
1868 //f32 tolerance = BS*10; // 2 without damage
1869 f32 tolerance = BS*12; // 3 without damage
1871 if(info.speed > tolerance)
1873 f32 damage_f = (info.speed - tolerance)/BS*factor;
1874 u16 damage = (u16)(damage_f+0.5);
1875 if(lplayer->hp > damage)
1876 lplayer->hp -= damage;
1880 ClientEnvEvent event;
1881 event.type = CEE_PLAYER_DAMAGE;
1882 event.player_damage.amount = damage;
1883 m_client_event_queue.push_back(event);
1889 A quick draft of lava damage
1891 if(m_lava_hurt_interval.step(dtime, 1.0))
1893 v3f pf = lplayer->getPosition();
1895 // Feet, middle and head
1896 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1897 MapNode n1 = m_map->getNodeNoEx(p1);
1898 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1899 MapNode n2 = m_map->getNodeNoEx(p2);
1900 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1901 MapNode n3 = m_map->getNodeNoEx(p2);
1903 u32 damage_per_second = 0;
1904 damage_per_second = MYMAX(damage_per_second,
1905 m_gamedef->ndef()->get(n1).damage_per_second);
1906 damage_per_second = MYMAX(damage_per_second,
1907 m_gamedef->ndef()->get(n2).damage_per_second);
1908 damage_per_second = MYMAX(damage_per_second,
1909 m_gamedef->ndef()->get(n3).damage_per_second);
1911 if(damage_per_second != 0)
1913 ClientEnvEvent event;
1914 event.type = CEE_PLAYER_DAMAGE;
1915 event.player_damage.amount = damage_per_second;
1916 m_client_event_queue.push_back(event);
1921 Stuff that can be done in an arbitarily large dtime
1923 for(core::list<Player*>::Iterator i = m_players.begin();
1924 i != m_players.end(); i++)
1926 Player *player = *i;
1927 v3f playerpos = player->getPosition();
1930 Handle non-local players
1932 if(player->isLocal() == false)
1935 player->move(dtime, *m_map, 100*BS);
1939 // Update lighting on all players on client
1940 u8 light = LIGHT_MAX;
1943 v3s16 p = player->getLightPosition();
1944 MapNode n = m_map->getNode(p);
1945 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
1947 catch(InvalidPositionException &e) {}
1948 player->updateLight(light);
1951 Add footsteps to grass
1955 // Get node that is at BS/4 under player
1956 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1958 MapNode n = m_map->getNode(bottompos);
1959 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1961 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
1962 m_map->setNode(bottompos, n);
1963 // Update mesh on client
1964 if(m_map->mapType() == MAPTYPE_CLIENT)
1966 v3s16 p_blocks = getNodeBlockPos(bottompos);
1967 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1968 //b->updateMesh(getDayNightRatio());
1969 b->setMeshExpired(true);
1973 catch(InvalidPositionException &e)
1980 Step active objects and update lighting of them
1983 for(core::map<u16, ClientActiveObject*>::Iterator
1984 i = m_active_objects.getIterator();
1985 i.atEnd()==false; i++)
1987 ClientActiveObject* obj = i.getNode()->getValue();
1989 obj->step(dtime, this);
1991 if(m_active_object_light_update_interval.step(dtime, 0.21))
1994 //u8 light = LIGHT_MAX;
1998 v3s16 p = obj->getLightPosition();
1999 MapNode n = m_map->getNode(p);
2000 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2002 catch(InvalidPositionException &e) {}
2003 obj->updateLight(light);
2008 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2010 m_map->updateMeshes(blockpos, getDayNightRatio());
2013 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2015 m_map->expireMeshes(only_daynight_diffed);
2018 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2020 core::map<u16, ClientActiveObject*>::Node *n;
2021 n = m_active_objects.find(id);
2024 return n->getValue();
2027 bool isFreeClientActiveObjectId(u16 id,
2028 core::map<u16, ClientActiveObject*> &objects)
2033 for(core::map<u16, ClientActiveObject*>::Iterator
2034 i = objects.getIterator();
2035 i.atEnd()==false; i++)
2037 if(i.getNode()->getKey() == id)
2043 u16 getFreeClientActiveObjectId(
2044 core::map<u16, ClientActiveObject*> &objects)
2049 if(isFreeClientActiveObjectId(new_id, objects))
2059 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2062 if(object->getId() == 0)
2064 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2067 infostream<<"ClientEnvironment::addActiveObject(): "
2068 <<"no free ids available"<<std::endl;
2072 object->setId(new_id);
2074 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2076 infostream<<"ClientEnvironment::addActiveObject(): "
2077 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2081 infostream<<"ClientEnvironment::addActiveObject(): "
2082 <<"added (id="<<object->getId()<<")"<<std::endl;
2083 m_active_objects.insert(object->getId(), object);
2084 object->addToScene(m_smgr, m_texturesource);
2085 { // Update lighting immediately
2089 v3s16 p = object->getLightPosition();
2090 MapNode n = m_map->getNode(p);
2091 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2093 catch(InvalidPositionException &e) {}
2094 object->updateLight(light);
2096 return object->getId();
2099 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2100 const std::string &init_data)
2102 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2105 infostream<<"ClientEnvironment::addActiveObject(): "
2106 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2113 obj->initialize(init_data);
2115 addActiveObject(obj);
2118 void ClientEnvironment::removeActiveObject(u16 id)
2120 infostream<<"ClientEnvironment::removeActiveObject(): "
2121 <<"id="<<id<<std::endl;
2122 ClientActiveObject* obj = getActiveObject(id);
2125 infostream<<"ClientEnvironment::removeActiveObject(): "
2126 <<"id="<<id<<" not found"<<std::endl;
2129 obj->removeFromScene();
2131 m_active_objects.remove(id);
2134 void ClientEnvironment::processActiveObjectMessage(u16 id,
2135 const std::string &data)
2137 ClientActiveObject* obj = getActiveObject(id);
2140 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2141 <<" got message for id="<<id<<", which doesn't exist."
2145 obj->processMessage(data);
2149 Callbacks for activeobjects
2152 void ClientEnvironment::damageLocalPlayer(u8 damage)
2154 LocalPlayer *lplayer = getLocalPlayer();
2157 if(lplayer->hp > damage)
2158 lplayer->hp -= damage;
2162 ClientEnvEvent event;
2163 event.type = CEE_PLAYER_DAMAGE;
2164 event.player_damage.amount = damage;
2165 m_client_event_queue.push_back(event);
2169 Client likes to call these
2172 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2173 core::array<DistanceSortedActiveObject> &dest)
2175 for(core::map<u16, ClientActiveObject*>::Iterator
2176 i = m_active_objects.getIterator();
2177 i.atEnd()==false; i++)
2179 ClientActiveObject* obj = i.getNode()->getValue();
2181 f32 d = (obj->getPosition() - origin).getLength();
2186 DistanceSortedActiveObject dso(obj, d);
2188 dest.push_back(dso);
2192 ClientEnvEvent ClientEnvironment::getClientEvent()
2194 if(m_client_event_queue.size() == 0)
2196 ClientEnvEvent event;
2197 event.type = CEE_NONE;
2200 return m_client_event_queue.pop_front();
2203 #endif // #ifndef SERVER