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((float)1.0/chance, (float)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();
623 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
624 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
625 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
627 MapNode n = block->getNodeNoEx(p0);
628 content_t c = n.getContent();
629 v3s16 p = p0 + block->getPosRelative();
631 std::map<content_t, std::list<ActiveABM> >::iterator j;
633 if(j == m_aabms.end())
636 for(std::list<ActiveABM>::iterator
637 i = j->second.begin(); i != j->second.end(); i++)
639 if(myrand() % i->chance != 0)
642 // Find out how many objects the block contains
643 u32 active_object_count = block->m_static_objects.m_active.size();
644 // Find out how many objects this and all the neighbors contain
645 u32 active_object_count_wider = 0;
646 for(s16 x=-1; x<=1; x++)
647 for(s16 y=-1; y<=1; y++)
648 for(s16 z=-1; z<=1; z++)
650 MapBlock *block2 = map->getBlockNoCreateNoEx(
651 block->getPos() + v3s16(x,y,z));
654 active_object_count_wider +=
655 block2->m_static_objects.m_active.size()
656 + block2->m_static_objects.m_stored.size();
659 // Call all the trigger variations
660 i->abm->trigger(m_env, p, n);
661 i->abm->trigger(m_env, p, n,
662 active_object_count, active_object_count_wider);
668 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
670 // Get time difference
672 u32 stamp = block->getTimestamp();
673 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
674 dtime_s = m_game_time - block->getTimestamp();
675 dtime_s += additional_dtime;
677 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
678 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
680 // Set current time as timestamp
681 block->setTimestampNoChangedFlag(m_game_time);
683 /*infostream<<"ServerEnvironment::activateBlock(): block is "
684 <<dtime_s<<" seconds old."<<std::endl;*/
686 // Activate stored objects
687 activateObjects(block);
690 bool changed = block->m_node_metadata->step((float)dtime_s);
694 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
695 event.p = block->getPos();
696 m_map->dispatchEvent(&event);
698 block->raiseModified(MOD_STATE_WRITE_NEEDED,
699 "node metadata modified in activateBlock");
702 /* Handle ActiveBlockModifiers */
703 ABMHandler abmhandler(m_abms, dtime_s, this, false);
704 abmhandler.apply(block);
707 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
709 m_abms.push_back(ABMWithState(abm));
712 void ServerEnvironment::clearAllObjects()
714 infostream<<"ServerEnvironment::clearAllObjects(): "
715 <<"Removing all active objects"<<std::endl;
716 core::list<u16> objects_to_remove;
717 for(core::map<u16, ServerActiveObject*>::Iterator
718 i = m_active_objects.getIterator();
719 i.atEnd()==false; i++)
721 ServerActiveObject* obj = i.getNode()->getValue();
722 u16 id = i.getNode()->getKey();
723 v3f objectpos = obj->getBasePosition();
724 // Delete static object if block is loaded
725 if(obj->m_static_exists){
726 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
728 block->m_static_objects.remove(id);
729 block->raiseModified(MOD_STATE_WRITE_NEEDED,
731 obj->m_static_exists = false;
734 // If known by some client, don't delete immediately
735 if(obj->m_known_by_count > 0){
736 obj->m_pending_deactivation = true;
737 obj->m_removed = true;
740 // Deregister in scripting api
741 scriptapi_rm_object_reference(m_lua, obj);
742 // Delete active object
744 // Id to be removed from m_active_objects
745 objects_to_remove.push_back(id);
747 // Remove references from m_active_objects
748 for(core::list<u16>::Iterator i = objects_to_remove.begin();
749 i != objects_to_remove.end(); i++)
751 m_active_objects.remove(*i);
754 core::list<v3s16> loadable_blocks;
755 infostream<<"ServerEnvironment::clearAllObjects(): "
756 <<"Listing all loadable blocks"<<std::endl;
757 m_map->listAllLoadableBlocks(loadable_blocks);
758 infostream<<"ServerEnvironment::clearAllObjects(): "
759 <<"Done listing all loadable blocks: "
760 <<loadable_blocks.size()
761 <<", now clearing"<<std::endl;
762 u32 report_interval = loadable_blocks.size() / 10;
763 u32 num_blocks_checked = 0;
764 u32 num_blocks_cleared = 0;
765 u32 num_objs_cleared = 0;
766 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
767 i != loadable_blocks.end(); i++)
770 MapBlock *block = m_map->emergeBlock(p, false);
772 errorstream<<"ServerEnvironment::clearAllObjects(): "
773 <<"Failed to emerge block "<<PP(p)<<std::endl;
776 u32 num_stored = block->m_static_objects.m_stored.size();
777 u32 num_active = block->m_static_objects.m_active.size();
778 if(num_stored != 0 || num_active != 0){
779 block->m_static_objects.m_stored.clear();
780 block->m_static_objects.m_active.clear();
781 block->raiseModified(MOD_STATE_WRITE_NEEDED,
783 num_objs_cleared += num_stored + num_active;
784 num_blocks_cleared++;
786 num_blocks_checked++;
788 if(num_blocks_checked % report_interval == 0){
789 float percent = 100.0 * (float)num_blocks_checked /
790 loadable_blocks.size();
791 infostream<<"ServerEnvironment::clearAllObjects(): "
792 <<"Cleared "<<num_objs_cleared<<" objects"
793 <<" in "<<num_blocks_cleared<<" blocks ("
794 <<percent<<"%)"<<std::endl;
797 infostream<<"ServerEnvironment::clearAllObjects(): "
798 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
799 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
802 void ServerEnvironment::step(float dtime)
804 DSTACK(__FUNCTION_NAME);
806 //TimeTaker timer("ServerEnv step");
809 bool footprints = g_settings->getBool("footprints");
815 m_game_time_fraction_counter += dtime;
816 u32 inc_i = (u32)m_game_time_fraction_counter;
817 m_game_time += inc_i;
818 m_game_time_fraction_counter -= (float)inc_i;
825 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
826 for(core::list<Player*>::Iterator i = m_players.begin();
827 i != m_players.end(); i++)
831 // Ignore disconnected players
832 if(player->peer_id == 0)
835 v3f playerpos = player->getPosition();
838 player->move(dtime, *m_map, 100*BS);
841 Add footsteps to grass
845 // Get node that is at BS/4 under player
846 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
848 MapNode n = m_map->getNode(bottompos);
849 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
851 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
852 m_map->setNode(bottompos, n);
855 catch(InvalidPositionException &e)
863 Manage active block list
865 if(m_active_blocks_management_interval.step(dtime, 2.0))
867 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
869 Get player block positions
871 core::list<v3s16> players_blockpos;
872 for(core::list<Player*>::Iterator
873 i = m_players.begin();
874 i != m_players.end(); i++)
877 // Ignore disconnected players
878 if(player->peer_id == 0)
880 v3s16 blockpos = getNodeBlockPos(
881 floatToInt(player->getPosition(), BS));
882 players_blockpos.push_back(blockpos);
886 Update list of active blocks, collecting changes
888 const s16 active_block_range = g_settings->getS16("active_block_range");
889 core::map<v3s16, bool> blocks_removed;
890 core::map<v3s16, bool> blocks_added;
891 m_active_blocks.update(players_blockpos, active_block_range,
892 blocks_removed, blocks_added);
895 Handle removed blocks
898 // Convert active objects that are no more in active blocks to static
899 deactivateFarObjects(false);
901 for(core::map<v3s16, bool>::Iterator
902 i = blocks_removed.getIterator();
903 i.atEnd()==false; i++)
905 v3s16 p = i.getNode()->getKey();
907 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
908 <<") became inactive"<<std::endl;*/
910 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
914 // Set current time as timestamp (and let it set ChangedFlag)
915 block->setTimestamp(m_game_time);
922 for(core::map<v3s16, bool>::Iterator
923 i = blocks_added.getIterator();
924 i.atEnd()==false; i++)
926 v3s16 p = i.getNode()->getKey();
928 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
929 <<") became active"<<std::endl;*/
931 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
933 // Block needs to be fetched first
934 m_emerger->queueBlockEmerge(p, false);
935 m_active_blocks.m_list.remove(p);
939 activateBlock(block);
944 Mess around in active blocks
946 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
948 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
952 for(core::map<v3s16, bool>::Iterator
953 i = m_active_blocks.m_list.getIterator();
954 i.atEnd()==false; i++)
956 v3s16 p = i.getNode()->getKey();
958 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
959 <<") being handled"<<std::endl;*/
961 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
965 // Reset block usage timer
966 block->resetUsageTimer();
968 // Set current time as timestamp
969 block->setTimestampNoChangedFlag(m_game_time);
970 // If time has changed much from the one on disk,
971 // set block to be saved when it is unloaded
972 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
973 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
974 "Timestamp older than 60s (step)");
977 bool changed = block->m_node_metadata->step(dtime);
981 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
983 m_map->dispatchEvent(&event);
985 block->raiseModified(MOD_STATE_WRITE_NEEDED,
986 "node metadata modified in step");
991 const float abm_interval = 1.0;
992 if(m_active_block_modifier_interval.step(dtime, abm_interval))
994 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
995 TimeTaker timer("modify in active blocks");
997 // Initialize handling of ActiveBlockModifiers
998 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1000 for(core::map<v3s16, bool>::Iterator
1001 i = m_active_blocks.m_list.getIterator();
1002 i.atEnd()==false; i++)
1004 v3s16 p = i.getNode()->getKey();
1006 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1007 <<") being handled"<<std::endl;*/
1009 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1013 // Set current time as timestamp
1014 block->setTimestampNoChangedFlag(m_game_time);
1016 /* Handle ActiveBlockModifiers */
1017 abmhandler.apply(block);
1020 u32 time_ms = timer.stop(true);
1021 u32 max_time_ms = 200;
1022 if(time_ms > max_time_ms){
1023 infostream<<"WARNING: active block modifiers took "
1024 <<time_ms<<"ms (longer than "
1025 <<max_time_ms<<"ms)"<<std::endl;
1030 Step script environment (run global on_step())
1032 scriptapi_environment_step(m_lua, dtime);
1038 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1039 //TimeTaker timer("Step active objects");
1041 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1043 // This helps the objects to send data at the same time
1044 bool send_recommended = false;
1045 m_send_recommended_timer += dtime;
1046 if(m_send_recommended_timer > getSendRecommendedInterval())
1048 m_send_recommended_timer -= getSendRecommendedInterval();
1049 send_recommended = true;
1052 for(core::map<u16, ServerActiveObject*>::Iterator
1053 i = m_active_objects.getIterator();
1054 i.atEnd()==false; i++)
1056 ServerActiveObject* obj = i.getNode()->getValue();
1057 // Remove non-peaceful mobs on peaceful mode
1058 if(g_settings->getBool("only_peaceful_mobs")){
1059 if(!obj->isPeaceful())
1060 obj->m_removed = true;
1062 // Don't step if is to be removed or stored statically
1063 if(obj->m_removed || obj->m_pending_deactivation)
1066 obj->step(dtime, send_recommended);
1067 // Read messages from object
1068 while(obj->m_messages_out.size() > 0)
1070 m_active_object_messages.push_back(
1071 obj->m_messages_out.pop_front());
1077 Manage active objects
1079 if(m_object_management_interval.step(dtime, 0.5))
1081 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1083 Remove objects that satisfy (m_removed && m_known_by_count==0)
1085 removeRemovedObjects();
1089 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1091 core::map<u16, ServerActiveObject*>::Node *n;
1092 n = m_active_objects.find(id);
1095 return n->getValue();
1098 bool isFreeServerActiveObjectId(u16 id,
1099 core::map<u16, ServerActiveObject*> &objects)
1104 for(core::map<u16, ServerActiveObject*>::Iterator
1105 i = objects.getIterator();
1106 i.atEnd()==false; i++)
1108 if(i.getNode()->getKey() == id)
1114 u16 getFreeServerActiveObjectId(
1115 core::map<u16, ServerActiveObject*> &objects)
1120 if(isFreeServerActiveObjectId(new_id, objects))
1130 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1133 u16 id = addActiveObjectRaw(object, true);
1137 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1141 v3f objectpos = obj->getBasePosition();
1143 // The block in which the object resides in
1144 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1147 Update the static data
1150 // Create new static object
1151 std::string staticdata = obj->getStaticData();
1152 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1153 // Add to the block where the object is located in
1154 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1155 // Get or generate the block
1156 MapBlock *block = m_map->emergeBlock(blockpos);
1158 bool succeeded = false;
1162 block->m_static_objects.insert(0, s_obj);
1163 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1164 "addActiveObjectAsStatic");
1168 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1169 <<"Could not find or generate "
1170 <<"a block for storing static object"<<std::endl;
1180 Finds out what new objects have been added to
1181 inside a radius around a position
1183 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1184 core::map<u16, bool> ¤t_objects,
1185 core::map<u16, bool> &added_objects)
1187 v3f pos_f = intToFloat(pos, BS);
1188 f32 radius_f = radius * BS;
1190 Go through the object list,
1191 - discard m_removed objects,
1192 - discard objects that are too far away,
1193 - discard objects that are found in current_objects.
1194 - add remaining objects to added_objects
1196 for(core::map<u16, ServerActiveObject*>::Iterator
1197 i = m_active_objects.getIterator();
1198 i.atEnd()==false; i++)
1200 u16 id = i.getNode()->getKey();
1202 ServerActiveObject *object = i.getNode()->getValue();
1205 // Discard if removed
1206 if(object->m_removed)
1208 // Discard if too far
1209 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1210 if(distance_f > radius_f)
1212 // Discard if already on current_objects
1213 core::map<u16, bool>::Node *n;
1214 n = current_objects.find(id);
1217 // Add to added_objects
1218 added_objects.insert(id, false);
1223 Finds out what objects have been removed from
1224 inside a radius around a position
1226 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1227 core::map<u16, bool> ¤t_objects,
1228 core::map<u16, bool> &removed_objects)
1230 v3f pos_f = intToFloat(pos, BS);
1231 f32 radius_f = radius * BS;
1233 Go through current_objects; object is removed if:
1234 - object is not found in m_active_objects (this is actually an
1235 error condition; objects should be set m_removed=true and removed
1236 only after all clients have been informed about removal), or
1237 - object has m_removed=true, or
1238 - object is too far away
1240 for(core::map<u16, bool>::Iterator
1241 i = current_objects.getIterator();
1242 i.atEnd()==false; i++)
1244 u16 id = i.getNode()->getKey();
1245 ServerActiveObject *object = getActiveObject(id);
1248 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1249 <<" object in current_objects is NULL"<<std::endl;
1251 else if(object->m_removed == false)
1253 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1254 /*infostream<<"removed == false"
1255 <<"distance_f = "<<distance_f
1256 <<", radius_f = "<<radius_f<<std::endl;*/
1257 if(distance_f < radius_f)
1263 removed_objects.insert(id, false);
1267 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1269 if(m_active_object_messages.size() == 0)
1270 return ActiveObjectMessage(0);
1272 return m_active_object_messages.pop_front();
1276 ************ Private methods *************
1279 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1283 if(object->getId() == 0){
1284 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1287 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1288 <<"no free ids available"<<std::endl;
1292 object->setId(new_id);
1295 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1296 <<"supplied with id "<<object->getId()<<std::endl;
1298 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1300 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1301 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1305 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1306 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1308 m_active_objects.insert(object->getId(), object);
1310 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1311 <<"Added id="<<object->getId()<<"; there are now "
1312 <<m_active_objects.size()<<" active objects."
1315 // Add static object to active static list of the block
1316 v3f objectpos = object->getBasePosition();
1317 std::string staticdata = object->getStaticData();
1318 StaticObject s_obj(object->getType(), objectpos, staticdata);
1319 // Add to the block where the object is located in
1320 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1321 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1324 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1325 object->m_static_exists = true;
1326 object->m_static_block = blockpos;
1329 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1330 "addActiveObjectRaw");
1333 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1334 <<"could not find block for storing id="<<object->getId()
1335 <<" statically"<<std::endl;
1338 // Register reference in scripting api (must be done before post-init)
1339 scriptapi_add_object_reference(m_lua, object);
1340 // Post-initialize object
1341 object->addedToEnvironment();
1343 return object->getId();
1347 Remove objects that satisfy (m_removed && m_known_by_count==0)
1349 void ServerEnvironment::removeRemovedObjects()
1351 core::list<u16> objects_to_remove;
1352 for(core::map<u16, ServerActiveObject*>::Iterator
1353 i = m_active_objects.getIterator();
1354 i.atEnd()==false; i++)
1356 u16 id = i.getNode()->getKey();
1357 ServerActiveObject* obj = i.getNode()->getValue();
1358 // This shouldn't happen but check it
1361 infostream<<"NULL object found in ServerEnvironment"
1362 <<" while finding removed objects. id="<<id<<std::endl;
1363 // Id to be removed from m_active_objects
1364 objects_to_remove.push_back(id);
1369 We will delete objects that are marked as removed or thatare
1370 waiting for deletion after deactivation
1372 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1376 Delete static data from block if is marked as removed
1378 if(obj->m_static_exists && obj->m_removed)
1380 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1383 block->m_static_objects.remove(id);
1384 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1385 "removeRemovedObjects");
1386 obj->m_static_exists = false;
1390 // If m_known_by_count > 0, don't actually remove.
1391 if(obj->m_known_by_count > 0)
1394 // Deregister in scripting api
1395 scriptapi_rm_object_reference(m_lua, obj);
1399 // Id to be removed from m_active_objects
1400 objects_to_remove.push_back(id);
1402 // Remove references from m_active_objects
1403 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1404 i != objects_to_remove.end(); i++)
1406 m_active_objects.remove(*i);
1410 static void print_hexdump(std::ostream &o, const std::string &data)
1412 const int linelength = 16;
1413 for(int l=0; ; l++){
1414 int i0 = linelength * l;
1415 bool at_end = false;
1416 int thislinelength = linelength;
1417 if(i0 + thislinelength > (int)data.size()){
1418 thislinelength = data.size() - i0;
1421 for(int di=0; di<linelength; di++){
1424 if(di<thislinelength)
1425 snprintf(buf, 4, "%.2x ", data[i]);
1427 snprintf(buf, 4, " ");
1431 for(int di=0; di<thislinelength; di++){
1445 Convert stored objects from blocks near the players to active.
1447 void ServerEnvironment::activateObjects(MapBlock *block)
1451 // Ignore if no stored objects (to not set changed flag)
1452 if(block->m_static_objects.m_stored.size() == 0)
1454 verbosestream<<"ServerEnvironment::activateObjects(): "
1455 <<"activating objects of block "<<PP(block->getPos())
1456 <<" ("<<block->m_static_objects.m_stored.size()
1457 <<" objects)"<<std::endl;
1458 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1460 errorstream<<"suspiciously large amount of objects detected: "
1461 <<block->m_static_objects.m_stored.size()<<" in "
1462 <<PP(block->getPos())
1463 <<"; removing all of them."<<std::endl;
1464 // Clear stored list
1465 block->m_static_objects.m_stored.clear();
1466 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1467 "stored list cleared in activateObjects due to "
1468 "large amount of objects");
1471 // A list for objects that couldn't be converted to static for some
1472 // reason. They will be stored back.
1473 core::list<StaticObject> new_stored;
1474 // Loop through stored static objects
1475 for(core::list<StaticObject>::Iterator
1476 i = block->m_static_objects.m_stored.begin();
1477 i != block->m_static_objects.m_stored.end(); i++)
1479 /*infostream<<"Server: Creating an active object from "
1480 <<"static data"<<std::endl;*/
1481 StaticObject &s_obj = *i;
1482 // Create an active object from the data
1483 ServerActiveObject *obj = ServerActiveObject::create
1484 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1485 // If couldn't create object, store static data back.
1488 errorstream<<"ServerEnvironment::activateObjects(): "
1489 <<"failed to create active object from static object "
1490 <<"in block "<<PP(s_obj.pos/BS)
1491 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1492 print_hexdump(verbosestream, s_obj.data);
1494 new_stored.push_back(s_obj);
1497 verbosestream<<"ServerEnvironment::activateObjects(): "
1498 <<"activated static object pos="<<PP(s_obj.pos/BS)
1499 <<" type="<<(int)s_obj.type<<std::endl;
1500 // This will also add the object to the active static list
1501 addActiveObjectRaw(obj, false);
1503 // Clear stored list
1504 block->m_static_objects.m_stored.clear();
1505 // Add leftover failed stuff to stored list
1506 for(core::list<StaticObject>::Iterator
1507 i = new_stored.begin();
1508 i != new_stored.end(); i++)
1510 StaticObject &s_obj = *i;
1511 block->m_static_objects.m_stored.push_back(s_obj);
1514 Note: Block hasn't really been modified here.
1515 The objects have just been activated and moved from the stored
1516 static list to the active static list.
1517 As such, the block is essentially the same.
1518 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1519 Otherwise there would be a huge amount of unnecessary I/O.
1524 Convert objects that are not standing inside active blocks to static.
1526 If m_known_by_count != 0, active object is not deleted, but static
1527 data is still updated.
1529 If force_delete is set, active object is deleted nevertheless. It
1530 shall only be set so in the destructor of the environment.
1532 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1534 core::list<u16> objects_to_remove;
1535 for(core::map<u16, ServerActiveObject*>::Iterator
1536 i = m_active_objects.getIterator();
1537 i.atEnd()==false; i++)
1539 ServerActiveObject* obj = i.getNode()->getValue();
1541 // This shouldn't happen but check it
1544 errorstream<<"NULL object found in ServerEnvironment"
1550 // If pending deactivation, let removeRemovedObjects() do it
1551 if(obj->m_pending_deactivation)
1554 u16 id = i.getNode()->getKey();
1555 v3f objectpos = obj->getBasePosition();
1557 // The block in which the object resides in
1558 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1560 // If block is active, don't remove
1561 if(m_active_blocks.contains(blockpos_o))
1564 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1565 <<"deactivating object id="<<id<<" on inactive block "
1566 <<PP(blockpos_o)<<std::endl;
1568 // If known by some client, don't immediately delete.
1569 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1572 Update the static data
1575 // Create new static object
1576 std::string staticdata_new = obj->getStaticData();
1577 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1579 bool stays_in_same_block = false;
1580 bool data_changed = true;
1582 if(obj->m_static_exists){
1583 if(obj->m_static_block == blockpos_o)
1584 stays_in_same_block = true;
1586 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1588 core::map<u16, StaticObject>::Node *n =
1589 block->m_static_objects.m_active.find(id);
1591 StaticObject static_old = n->getValue();
1593 float save_movem = obj->getMinimumSavedMovement();
1595 if(static_old.data == staticdata_new &&
1596 (static_old.pos - objectpos).getLength() < save_movem)
1597 data_changed = false;
1599 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1600 <<"id="<<id<<" m_static_exists=true but "
1601 <<"static data doesn't actually exist in "
1602 <<PP(obj->m_static_block)<<std::endl;
1606 bool shall_be_written = (!stays_in_same_block || data_changed);
1608 // Delete old static object
1609 if(obj->m_static_exists)
1611 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1614 block->m_static_objects.remove(id);
1615 obj->m_static_exists = false;
1616 // Only mark block as modified if data changed considerably
1617 if(shall_be_written)
1618 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1619 "deactivateFarObjects: Static data "
1620 "changed considerably");
1624 // Add to the block where the object is located in
1625 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1626 // Get or generate the block
1627 MapBlock *block = m_map->emergeBlock(blockpos);
1631 if(block->m_static_objects.m_stored.size() >= 49){
1632 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1633 <<" statically but block "<<PP(blockpos)
1634 <<" already contains "
1635 <<block->m_static_objects.m_stored.size()
1636 <<" (over 49) objects."
1637 <<" Forcing delete."<<std::endl;
1638 force_delete = true;
1640 u16 new_id = pending_delete ? id : 0;
1641 block->m_static_objects.insert(new_id, s_obj);
1643 // Only mark block as modified if data changed considerably
1644 if(shall_be_written)
1645 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1646 "deactivateFarObjects: Static data "
1647 "changed considerably");
1649 obj->m_static_exists = true;
1650 obj->m_static_block = block->getPos();
1654 errorstream<<"ServerEnv: Could not find or generate "
1655 <<"a block for storing id="<<obj->getId()
1656 <<" statically"<<std::endl;
1661 If known by some client, set pending deactivation.
1662 Otherwise delete it immediately.
1667 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1668 <<"object id="<<id<<" is known by clients"
1669 <<"; not deleting yet"<<std::endl;
1671 obj->m_pending_deactivation = true;
1675 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1676 <<"object id="<<id<<" is not known by clients"
1677 <<"; deleting"<<std::endl;
1679 // Deregister in scripting api
1680 scriptapi_rm_object_reference(m_lua, obj);
1682 // Delete active object
1684 // Id to be removed from m_active_objects
1685 objects_to_remove.push_back(id);
1688 // Remove references from m_active_objects
1689 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1690 i != objects_to_remove.end(); i++)
1692 m_active_objects.remove(*i);
1703 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1704 ITextureSource *texturesource, IGameDef *gamedef):
1707 m_texturesource(texturesource),
1714 ClientEnvironment::~ClientEnvironment()
1716 // delete active objects
1717 for(core::map<u16, ClientActiveObject*>::Iterator
1718 i = m_active_objects.getIterator();
1719 i.atEnd()==false; i++)
1721 delete i.getNode()->getValue();
1728 void ClientEnvironment::addPlayer(Player *player)
1730 DSTACK(__FUNCTION_NAME);
1732 It is a failure if player is local and there already is a local
1735 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1737 Environment::addPlayer(player);
1740 LocalPlayer * ClientEnvironment::getLocalPlayer()
1742 for(core::list<Player*>::Iterator i = m_players.begin();
1743 i != m_players.end(); i++)
1745 Player *player = *i;
1746 if(player->isLocal())
1747 return (LocalPlayer*)player;
1752 void ClientEnvironment::step(float dtime)
1754 DSTACK(__FUNCTION_NAME);
1756 // Get some settings
1757 bool free_move = g_settings->getBool("free_move");
1758 bool footprints = g_settings->getBool("footprints");
1761 LocalPlayer *lplayer = getLocalPlayer();
1763 // collision info queue
1764 core::list<CollisionInfo> player_collisions;
1767 Get the speed the player is going
1769 bool is_climbing = lplayer->is_climbing;
1771 f32 player_speed = lplayer->getSpeed().getLength();
1774 Maximum position increment
1776 //f32 position_max_increment = 0.05*BS;
1777 f32 position_max_increment = 0.1*BS;
1779 // Maximum time increment (for collision detection etc)
1780 // time = distance / speed
1781 f32 dtime_max_increment = 1;
1782 if(player_speed > 0.001)
1783 dtime_max_increment = position_max_increment / player_speed;
1785 // Maximum time increment is 10ms or lower
1786 if(dtime_max_increment > 0.01)
1787 dtime_max_increment = 0.01;
1789 // Don't allow overly huge dtime
1793 f32 dtime_downcount = dtime;
1796 Stuff that has a maximum time increment
1805 if(dtime_downcount > dtime_max_increment)
1807 dtime_part = dtime_max_increment;
1808 dtime_downcount -= dtime_part;
1812 dtime_part = dtime_downcount;
1814 Setting this to 0 (no -=dtime_part) disables an infinite loop
1815 when dtime_part is so small that dtime_downcount -= dtime_part
1818 dtime_downcount = 0;
1826 v3f lplayerpos = lplayer->getPosition();
1829 if(free_move == false && is_climbing == false)
1832 v3f speed = lplayer->getSpeed();
1833 if(lplayer->swimming_up == false)
1834 speed.Y -= 9.81 * BS * dtime_part * 2;
1837 if(lplayer->in_water_stable || lplayer->in_water)
1839 f32 max_down = 2.0*BS;
1840 if(speed.Y < -max_down) speed.Y = -max_down;
1843 if(speed.getLength() > max)
1845 speed = speed / speed.getLength() * max;
1849 lplayer->setSpeed(speed);
1854 This also does collision detection.
1856 lplayer->move(dtime_part, *m_map, position_max_increment,
1857 &player_collisions);
1860 while(dtime_downcount > 0.001);
1862 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1864 for(core::list<CollisionInfo>::Iterator
1865 i = player_collisions.begin();
1866 i != player_collisions.end(); i++)
1868 CollisionInfo &info = *i;
1869 if(info.t == COLLISION_FALL)
1871 //f32 tolerance = BS*10; // 2 without damage
1872 f32 tolerance = BS*12; // 3 without damage
1874 if(info.speed > tolerance)
1876 f32 damage_f = (info.speed - tolerance)/BS*factor;
1877 u16 damage = (u16)(damage_f+0.5);
1878 if(lplayer->hp > damage)
1879 lplayer->hp -= damage;
1883 ClientEnvEvent event;
1884 event.type = CEE_PLAYER_DAMAGE;
1885 event.player_damage.amount = damage;
1886 m_client_event_queue.push_back(event);
1892 A quick draft of lava damage
1894 if(m_lava_hurt_interval.step(dtime, 1.0))
1896 v3f pf = lplayer->getPosition();
1898 // Feet, middle and head
1899 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1900 MapNode n1 = m_map->getNodeNoEx(p1);
1901 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1902 MapNode n2 = m_map->getNodeNoEx(p2);
1903 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1904 MapNode n3 = m_map->getNodeNoEx(p2);
1906 u32 damage_per_second = 0;
1907 damage_per_second = MYMAX(damage_per_second,
1908 m_gamedef->ndef()->get(n1).damage_per_second);
1909 damage_per_second = MYMAX(damage_per_second,
1910 m_gamedef->ndef()->get(n2).damage_per_second);
1911 damage_per_second = MYMAX(damage_per_second,
1912 m_gamedef->ndef()->get(n3).damage_per_second);
1914 if(damage_per_second != 0)
1916 ClientEnvEvent event;
1917 event.type = CEE_PLAYER_DAMAGE;
1918 event.player_damage.amount = damage_per_second;
1919 m_client_event_queue.push_back(event);
1924 Stuff that can be done in an arbitarily large dtime
1926 for(core::list<Player*>::Iterator i = m_players.begin();
1927 i != m_players.end(); i++)
1929 Player *player = *i;
1930 v3f playerpos = player->getPosition();
1933 Handle non-local players
1935 if(player->isLocal() == false)
1938 player->move(dtime, *m_map, 100*BS);
1942 // Update lighting on all players on client
1943 u8 light = LIGHT_MAX;
1946 v3s16 p = player->getLightPosition();
1947 MapNode n = m_map->getNode(p);
1948 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
1950 catch(InvalidPositionException &e) {}
1951 player->updateLight(light);
1954 Add footsteps to grass
1958 // Get node that is at BS/4 under player
1959 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1961 MapNode n = m_map->getNode(bottompos);
1962 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1964 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
1965 m_map->setNode(bottompos, n);
1966 // Update mesh on client
1967 if(m_map->mapType() == MAPTYPE_CLIENT)
1969 v3s16 p_blocks = getNodeBlockPos(bottompos);
1970 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1971 //b->updateMesh(getDayNightRatio());
1972 b->setMeshExpired(true);
1976 catch(InvalidPositionException &e)
1983 Step active objects and update lighting of them
1986 for(core::map<u16, ClientActiveObject*>::Iterator
1987 i = m_active_objects.getIterator();
1988 i.atEnd()==false; i++)
1990 ClientActiveObject* obj = i.getNode()->getValue();
1992 obj->step(dtime, this);
1994 if(m_active_object_light_update_interval.step(dtime, 0.21))
1997 //u8 light = LIGHT_MAX;
2001 v3s16 p = obj->getLightPosition();
2002 MapNode n = m_map->getNode(p);
2003 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2005 catch(InvalidPositionException &e) {}
2006 obj->updateLight(light);
2011 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2013 m_map->updateMeshes(blockpos, getDayNightRatio());
2016 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2018 m_map->expireMeshes(only_daynight_diffed);
2021 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2023 core::map<u16, ClientActiveObject*>::Node *n;
2024 n = m_active_objects.find(id);
2027 return n->getValue();
2030 bool isFreeClientActiveObjectId(u16 id,
2031 core::map<u16, ClientActiveObject*> &objects)
2036 for(core::map<u16, ClientActiveObject*>::Iterator
2037 i = objects.getIterator();
2038 i.atEnd()==false; i++)
2040 if(i.getNode()->getKey() == id)
2046 u16 getFreeClientActiveObjectId(
2047 core::map<u16, ClientActiveObject*> &objects)
2052 if(isFreeClientActiveObjectId(new_id, objects))
2062 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2065 if(object->getId() == 0)
2067 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2070 infostream<<"ClientEnvironment::addActiveObject(): "
2071 <<"no free ids available"<<std::endl;
2075 object->setId(new_id);
2077 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2079 infostream<<"ClientEnvironment::addActiveObject(): "
2080 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2084 infostream<<"ClientEnvironment::addActiveObject(): "
2085 <<"added (id="<<object->getId()<<")"<<std::endl;
2086 m_active_objects.insert(object->getId(), object);
2087 object->addToScene(m_smgr, m_texturesource);
2088 { // Update lighting immediately
2092 v3s16 p = object->getLightPosition();
2093 MapNode n = m_map->getNode(p);
2094 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2096 catch(InvalidPositionException &e) {}
2097 object->updateLight(light);
2099 return object->getId();
2102 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2103 const std::string &init_data)
2105 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2108 infostream<<"ClientEnvironment::addActiveObject(): "
2109 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2116 obj->initialize(init_data);
2118 addActiveObject(obj);
2121 void ClientEnvironment::removeActiveObject(u16 id)
2123 infostream<<"ClientEnvironment::removeActiveObject(): "
2124 <<"id="<<id<<std::endl;
2125 ClientActiveObject* obj = getActiveObject(id);
2128 infostream<<"ClientEnvironment::removeActiveObject(): "
2129 <<"id="<<id<<" not found"<<std::endl;
2132 obj->removeFromScene();
2134 m_active_objects.remove(id);
2137 void ClientEnvironment::processActiveObjectMessage(u16 id,
2138 const std::string &data)
2140 ClientActiveObject* obj = getActiveObject(id);
2143 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2144 <<" got message for id="<<id<<", which doesn't exist."
2148 obj->processMessage(data);
2152 Callbacks for activeobjects
2155 void ClientEnvironment::damageLocalPlayer(u8 damage)
2157 LocalPlayer *lplayer = getLocalPlayer();
2160 if(lplayer->hp > damage)
2161 lplayer->hp -= damage;
2165 ClientEnvEvent event;
2166 event.type = CEE_PLAYER_DAMAGE;
2167 event.player_damage.amount = damage;
2168 m_client_event_queue.push_back(event);
2172 Client likes to call these
2175 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2176 core::array<DistanceSortedActiveObject> &dest)
2178 for(core::map<u16, ClientActiveObject*>::Iterator
2179 i = m_active_objects.getIterator();
2180 i.atEnd()==false; i++)
2182 ClientActiveObject* obj = i.getNode()->getValue();
2184 f32 d = (obj->getPosition() - origin).getLength();
2189 DistanceSortedActiveObject dso(obj, d);
2191 dest.push_back(dso);
2195 ClientEnvEvent ClientEnvironment::getClientEvent()
2197 if(m_client_event_queue.size() == 0)
2199 ClientEnvEvent event;
2200 event.type = CEE_NONE;
2203 return m_client_event_queue.pop_front();
2206 #endif // #ifndef SERVER