3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
40 #include "serverremoteplayer.h"
42 #include "clientmap.h"
44 #include "daynightratio.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
56 Environment::~Environment()
59 for(core::list<Player*>::Iterator i = m_players.begin();
60 i != m_players.end(); i++)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(core::list<Player*>::Iterator i = m_players.begin();
88 i != m_players.end(); i++)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(core::list<Player*>::Iterator i = m_players.begin();
105 i != m_players.end(); i++)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(core::list<Player*>::Iterator i = m_players.begin();
117 i != m_players.end(); i++)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 core::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(core::list<Player*>::Iterator
132 i = connected_players.begin();
133 i != connected_players.end(); i++)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 core::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(core::list<Player*>::Iterator
151 i = connected_players.begin();
152 i != connected_players.end(); i++)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 core::list<Player*> Environment::getPlayers()
170 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 core::list<Player*> newlist;
173 for(core::list<Player*>::Iterator
174 i = m_players.begin();
175 i != m_players.end(); i++)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 void Environment::printPlayers(std::ostream &o)
193 o<<"Players in environment:"<<std::endl;
194 for(core::list<Player*>::Iterator i = m_players.begin();
195 i != m_players.end(); i++)
198 o<<"Player peer_id="<<player->peer_id<<std::endl;
202 u32 Environment::getDayNightRatio()
204 return time_to_daynight_ratio(m_time_of_day);
207 void Environment::stepTimeOfDay(float dtime)
209 m_time_counter += dtime;
210 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
211 u32 units = (u32)(m_time_counter*speed);
212 m_time_counter -= (f32)units / speed;
216 if(m_time_of_day + units >= 24000)
218 m_time_of_day = (m_time_of_day + units) % 24000;
220 m_time_of_day_f = (float)m_time_of_day / 24000.0;
223 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
224 if(m_time_of_day_f > 1.0)
225 m_time_of_day_f -= 1.0;
226 if(m_time_of_day_f < 0.0)
227 m_time_of_day_f += 1.0;
235 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
239 // Initialize timer to random value to spread processing
240 float itv = abm->getTriggerInterval();
241 itv = MYMAX(0.001, itv); // No less than 1ms
242 int minval = MYMAX(-0.51*itv, -60); // Clamp to
243 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
244 timer = myrand_range(minval, maxval);
251 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
254 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
255 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
256 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
263 void ActiveBlockList::update(core::list<v3s16> &active_positions,
265 core::map<v3s16, bool> &blocks_removed,
266 core::map<v3s16, bool> &blocks_added)
271 core::map<v3s16, bool> newlist;
272 for(core::list<v3s16>::Iterator i = active_positions.begin();
273 i != active_positions.end(); i++)
275 fillRadiusBlock(*i, radius, newlist);
279 Find out which blocks on the old list are not on the new list
281 // Go through old list
282 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
283 i.atEnd()==false; i++)
285 v3s16 p = i.getNode()->getKey();
286 // If not on new list, it's been removed
287 if(newlist.find(p) == NULL)
288 blocks_removed.insert(p, true);
292 Find out which blocks on the new list are not on the old list
294 // Go through new list
295 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
296 i.atEnd()==false; i++)
298 v3s16 p = i.getNode()->getKey();
299 // If not on old list, it's been added
300 if(m_list.find(p) == NULL)
301 blocks_added.insert(p, true);
308 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
309 i.atEnd()==false; i++)
311 v3s16 p = i.getNode()->getKey();
312 m_list.insert(p, true);
320 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
321 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
326 m_random_spawn_timer(3),
327 m_send_recommended_timer(0),
329 m_game_time_fraction_counter(0)
333 ServerEnvironment::~ServerEnvironment()
335 // Clear active block list.
336 // This makes the next one delete all active objects.
337 m_active_blocks.clear();
339 // Convert all objects to static and delete the active objects
340 deactivateFarObjects(true);
345 // Delete ActiveBlockModifiers
346 for(core::list<ABMWithState>::Iterator
347 i = m_abms.begin(); i != m_abms.end(); i++){
352 void ServerEnvironment::serializePlayers(const std::string &savedir)
354 std::string players_path = savedir + "/players";
355 fs::CreateDir(players_path);
357 core::map<Player*, bool> saved_players;
359 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
360 for(u32 i=0; i<player_files.size(); i++)
362 if(player_files[i].dir)
365 // Full path to this file
366 std::string path = players_path + "/" + player_files[i].name;
368 //infostream<<"Checking player file "<<path<<std::endl;
370 // Load player to see what is its name
371 ServerRemotePlayer testplayer(this);
373 // Open file and deserialize
374 std::ifstream is(path.c_str(), std::ios_base::binary);
375 if(is.good() == false)
377 infostream<<"Failed to read "<<path<<std::endl;
380 testplayer.deSerialize(is);
383 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
385 // Search for the player
386 std::string playername = testplayer.getName();
387 Player *player = getPlayer(playername.c_str());
390 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
394 //infostream<<"Found matching player, overwriting."<<std::endl;
396 // OK, found. Save player there.
398 // Open file and serialize
399 std::ofstream os(path.c_str(), std::ios_base::binary);
400 if(os.good() == false)
402 infostream<<"Failed to overwrite "<<path<<std::endl;
405 player->serialize(os);
406 saved_players.insert(player, true);
410 for(core::list<Player*>::Iterator i = m_players.begin();
411 i != m_players.end(); i++)
414 if(saved_players.find(player) != NULL)
416 /*infostream<<"Player "<<player->getName()
417 <<" was already saved."<<std::endl;*/
420 std::string playername = player->getName();
421 // Don't save unnamed player
424 //infostream<<"Not saving unnamed player."<<std::endl;
430 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
431 playername = "player";
432 std::string path = players_path + "/" + playername;
434 for(u32 i=0; i<1000; i++)
436 if(fs::PathExists(path) == false)
441 path = players_path + "/" + playername + itos(i);
445 infostream<<"Didn't find free file for player"<<std::endl;
450 /*infostream<<"Saving player "<<player->getName()<<" to "
452 // Open file and serialize
453 std::ofstream os(path.c_str(), std::ios_base::binary);
454 if(os.good() == false)
456 infostream<<"Failed to overwrite "<<path<<std::endl;
459 player->serialize(os);
460 saved_players.insert(player, true);
464 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
467 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
469 std::string players_path = savedir + "/players";
471 core::map<Player*, bool> saved_players;
473 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
474 for(u32 i=0; i<player_files.size(); i++)
476 if(player_files[i].dir)
479 // Full path to this file
480 std::string path = players_path + "/" + player_files[i].name;
482 //infostream<<"Checking player file "<<path<<std::endl;
484 // Load player to see what is its name
485 ServerRemotePlayer testplayer(this);
487 // Open file and deserialize
488 std::ifstream is(path.c_str(), std::ios_base::binary);
489 if(is.good() == false)
491 infostream<<"Failed to read "<<path<<std::endl;
494 testplayer.deSerialize(is);
497 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
499 infostream<<"Not loading player with invalid name: "
500 <<testplayer.getName()<<std::endl;
503 /*infostream<<"Loaded test player with name "<<testplayer.getName()
506 // Search for the player
507 std::string playername = testplayer.getName();
508 Player *player = getPlayer(playername.c_str());
509 bool newplayer = false;
512 //infostream<<"Is a new player"<<std::endl;
513 player = new ServerRemotePlayer(this);
517 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
521 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
523 // Open file and deserialize
524 std::ifstream is(path.c_str(), std::ios_base::binary);
525 if(is.good() == false)
527 infostream<<"Failed to read "<<path<<std::endl;
530 srp->deSerialize(is);
531 srp->m_last_good_position = srp->getBasePosition();
532 srp->m_last_good_position_age = 0;
542 void ServerEnvironment::saveMeta(const std::string &savedir)
544 std::string path = savedir + "/env_meta.txt";
546 // Open file and serialize
547 std::ofstream os(path.c_str(), std::ios_base::binary);
548 if(os.good() == false)
550 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
552 throw SerializationError("Couldn't save env meta");
556 args.setU64("game_time", m_game_time);
557 args.setU64("time_of_day", getTimeOfDay());
562 void ServerEnvironment::loadMeta(const std::string &savedir)
564 std::string path = savedir + "/env_meta.txt";
566 // Open file and deserialize
567 std::ifstream is(path.c_str(), std::ios_base::binary);
568 if(is.good() == false)
570 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
572 throw SerializationError("Couldn't load env meta");
580 throw SerializationError
581 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
583 std::getline(is, line);
584 std::string trimmedline = trim(line);
585 if(trimmedline == "EnvArgsEnd")
587 args.parseConfigLine(line);
591 m_game_time = args.getU64("game_time");
592 }catch(SettingNotFoundException &e){
593 // Getting this is crucial, otherwise timestamps are useless
594 throw SerializationError("Couldn't load env meta game_time");
598 m_time_of_day = args.getU64("time_of_day");
599 }catch(SettingNotFoundException &e){
600 // This is not as important
601 m_time_of_day = 9000;
607 ActiveBlockModifier *abm;
609 std::set<content_t> required_neighbors;
615 ServerEnvironment *m_env;
616 std::map<content_t, std::list<ActiveABM> > m_aabms;
618 ABMHandler(core::list<ABMWithState> &abms,
619 float dtime_s, ServerEnvironment *env,
625 INodeDefManager *ndef = env->getGameDef()->ndef();
626 for(core::list<ABMWithState>::Iterator
627 i = abms.begin(); i != abms.end(); i++){
628 ActiveBlockModifier *abm = i->abm;
629 float trigger_interval = abm->getTriggerInterval();
630 if(trigger_interval < 0.001)
631 trigger_interval = 0.001;
632 float actual_interval = dtime_s;
635 if(i->timer < trigger_interval)
637 i->timer -= trigger_interval;
638 actual_interval = trigger_interval;
642 float intervals = actual_interval / trigger_interval;
643 float chance = abm->getTriggerChance();
646 aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
650 std::set<std::string> required_neighbors_s
651 = abm->getRequiredNeighbors();
652 for(std::set<std::string>::iterator
653 i = required_neighbors_s.begin();
654 i != required_neighbors_s.end(); i++){
655 content_t c = ndef->getId(*i);
656 if(c == CONTENT_IGNORE)
658 aabm.required_neighbors.insert(c);
661 std::set<std::string> contents_s = abm->getTriggerContents();
662 for(std::set<std::string>::iterator
663 i = contents_s.begin(); i != contents_s.end(); i++){
664 content_t c = ndef->getId(*i);
665 if(c == CONTENT_IGNORE)
667 std::map<content_t, std::list<ActiveABM> >::iterator j;
669 if(j == m_aabms.end()){
670 std::list<ActiveABM> aabmlist;
671 m_aabms[c] = aabmlist;
674 j->second.push_back(aabm);
678 void apply(MapBlock *block)
683 ServerMap *map = &m_env->getServerMap();
686 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
687 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
688 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
690 MapNode n = block->getNodeNoEx(p0);
691 content_t c = n.getContent();
692 v3s16 p = p0 + block->getPosRelative();
694 std::map<content_t, std::list<ActiveABM> >::iterator j;
696 if(j == m_aabms.end())
699 for(std::list<ActiveABM>::iterator
700 i = j->second.begin(); i != j->second.end(); i++)
702 if(myrand() % i->chance != 0)
706 if(!i->required_neighbors.empty())
709 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
710 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
711 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
715 MapNode n = map->getNodeNoEx(p1);
716 content_t c = n.getContent();
717 std::set<content_t>::const_iterator k;
718 k = i->required_neighbors.find(c);
719 if(k != i->required_neighbors.end()){
723 // No required neighbor found
728 // Find out how many objects the block contains
729 u32 active_object_count = block->m_static_objects.m_active.size();
730 // Find out how many objects this and all the neighbors contain
731 u32 active_object_count_wider = 0;
732 for(s16 x=-1; x<=1; x++)
733 for(s16 y=-1; y<=1; y++)
734 for(s16 z=-1; z<=1; z++)
736 MapBlock *block2 = map->getBlockNoCreateNoEx(
737 block->getPos() + v3s16(x,y,z));
740 active_object_count_wider +=
741 block2->m_static_objects.m_active.size()
742 + block2->m_static_objects.m_stored.size();
745 // Call all the trigger variations
746 i->abm->trigger(m_env, p, n);
747 i->abm->trigger(m_env, p, n,
748 active_object_count, active_object_count_wider);
754 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
756 // Get time difference
758 u32 stamp = block->getTimestamp();
759 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
760 dtime_s = m_game_time - block->getTimestamp();
761 dtime_s += additional_dtime;
763 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
764 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
766 // Set current time as timestamp
767 block->setTimestampNoChangedFlag(m_game_time);
769 /*infostream<<"ServerEnvironment::activateBlock(): block is "
770 <<dtime_s<<" seconds old."<<std::endl;*/
772 // Activate stored objects
773 activateObjects(block);
776 bool changed = block->m_node_metadata->step((float)dtime_s);
780 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
781 event.p = block->getPos();
782 m_map->dispatchEvent(&event);
784 block->raiseModified(MOD_STATE_WRITE_NEEDED,
785 "node metadata modified in activateBlock");
788 /* Handle ActiveBlockModifiers */
789 ABMHandler abmhandler(m_abms, dtime_s, this, false);
790 abmhandler.apply(block);
793 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
795 m_abms.push_back(ABMWithState(abm));
798 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
800 std::set<u16> objects;
801 for(core::map<u16, ServerActiveObject*>::Iterator
802 i = m_active_objects.getIterator();
803 i.atEnd()==false; i++)
805 ServerActiveObject* obj = i.getNode()->getValue();
806 u16 id = i.getNode()->getKey();
807 v3f objectpos = obj->getBasePosition();
808 if(objectpos.getDistanceFrom(pos) > radius)
815 void ServerEnvironment::clearAllObjects()
817 infostream<<"ServerEnvironment::clearAllObjects(): "
818 <<"Removing all active objects"<<std::endl;
819 core::list<u16> objects_to_remove;
820 for(core::map<u16, ServerActiveObject*>::Iterator
821 i = m_active_objects.getIterator();
822 i.atEnd()==false; i++)
824 ServerActiveObject* obj = i.getNode()->getValue();
825 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
827 u16 id = i.getNode()->getKey();
828 v3f objectpos = obj->getBasePosition();
829 // Delete static object if block is loaded
830 if(obj->m_static_exists){
831 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
833 block->m_static_objects.remove(id);
834 block->raiseModified(MOD_STATE_WRITE_NEEDED,
836 obj->m_static_exists = false;
839 // If known by some client, don't delete immediately
840 if(obj->m_known_by_count > 0){
841 obj->m_pending_deactivation = true;
842 obj->m_removed = true;
846 // Tell the object about removal
847 obj->removingFromEnvironment();
848 // Deregister in scripting api
849 scriptapi_rm_object_reference(m_lua, obj);
851 // Delete active object
852 if(obj->environmentDeletes())
854 // Id to be removed from m_active_objects
855 objects_to_remove.push_back(id);
857 // Remove references from m_active_objects
858 for(core::list<u16>::Iterator i = objects_to_remove.begin();
859 i != objects_to_remove.end(); i++)
861 m_active_objects.remove(*i);
864 core::list<v3s16> loadable_blocks;
865 infostream<<"ServerEnvironment::clearAllObjects(): "
866 <<"Listing all loadable blocks"<<std::endl;
867 m_map->listAllLoadableBlocks(loadable_blocks);
868 infostream<<"ServerEnvironment::clearAllObjects(): "
869 <<"Done listing all loadable blocks: "
870 <<loadable_blocks.size()
871 <<", now clearing"<<std::endl;
872 u32 report_interval = loadable_blocks.size() / 10;
873 u32 num_blocks_checked = 0;
874 u32 num_blocks_cleared = 0;
875 u32 num_objs_cleared = 0;
876 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
877 i != loadable_blocks.end(); i++)
880 MapBlock *block = m_map->emergeBlock(p, false);
882 errorstream<<"ServerEnvironment::clearAllObjects(): "
883 <<"Failed to emerge block "<<PP(p)<<std::endl;
886 u32 num_stored = block->m_static_objects.m_stored.size();
887 u32 num_active = block->m_static_objects.m_active.size();
888 if(num_stored != 0 || num_active != 0){
889 block->m_static_objects.m_stored.clear();
890 block->m_static_objects.m_active.clear();
891 block->raiseModified(MOD_STATE_WRITE_NEEDED,
893 num_objs_cleared += num_stored + num_active;
894 num_blocks_cleared++;
896 num_blocks_checked++;
898 if(num_blocks_checked % report_interval == 0){
899 float percent = 100.0 * (float)num_blocks_checked /
900 loadable_blocks.size();
901 infostream<<"ServerEnvironment::clearAllObjects(): "
902 <<"Cleared "<<num_objs_cleared<<" objects"
903 <<" in "<<num_blocks_cleared<<" blocks ("
904 <<percent<<"%)"<<std::endl;
907 infostream<<"ServerEnvironment::clearAllObjects(): "
908 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
909 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
912 void ServerEnvironment::step(float dtime)
914 DSTACK(__FUNCTION_NAME);
916 //TimeTaker timer("ServerEnv step");
918 /* Step time of day */
919 stepTimeOfDay(dtime);
925 m_game_time_fraction_counter += dtime;
926 u32 inc_i = (u32)m_game_time_fraction_counter;
927 m_game_time += inc_i;
928 m_game_time_fraction_counter -= (float)inc_i;
935 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
936 for(core::list<Player*>::Iterator i = m_players.begin();
937 i != m_players.end(); i++)
941 // Ignore disconnected players
942 if(player->peer_id == 0)
945 v3f playerpos = player->getPosition();
948 player->move(dtime, *m_map, 100*BS);
953 Manage active block list
955 if(m_active_blocks_management_interval.step(dtime, 2.0))
957 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
959 Get player block positions
961 core::list<v3s16> players_blockpos;
962 for(core::list<Player*>::Iterator
963 i = m_players.begin();
964 i != m_players.end(); i++)
967 // Ignore disconnected players
968 if(player->peer_id == 0)
970 v3s16 blockpos = getNodeBlockPos(
971 floatToInt(player->getPosition(), BS));
972 players_blockpos.push_back(blockpos);
976 Update list of active blocks, collecting changes
978 const s16 active_block_range = g_settings->getS16("active_block_range");
979 core::map<v3s16, bool> blocks_removed;
980 core::map<v3s16, bool> blocks_added;
981 m_active_blocks.update(players_blockpos, active_block_range,
982 blocks_removed, blocks_added);
985 Handle removed blocks
988 // Convert active objects that are no more in active blocks to static
989 deactivateFarObjects(false);
991 for(core::map<v3s16, bool>::Iterator
992 i = blocks_removed.getIterator();
993 i.atEnd()==false; i++)
995 v3s16 p = i.getNode()->getKey();
997 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
998 <<") became inactive"<<std::endl;*/
1000 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1004 // Set current time as timestamp (and let it set ChangedFlag)
1005 block->setTimestamp(m_game_time);
1012 for(core::map<v3s16, bool>::Iterator
1013 i = blocks_added.getIterator();
1014 i.atEnd()==false; i++)
1016 v3s16 p = i.getNode()->getKey();
1018 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1019 <<") became active"<<std::endl;*/
1021 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1023 // Block needs to be fetched first
1024 m_emerger->queueBlockEmerge(p, false);
1025 m_active_blocks.m_list.remove(p);
1029 activateBlock(block);
1034 Mess around in active blocks
1036 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1038 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1042 for(core::map<v3s16, bool>::Iterator
1043 i = m_active_blocks.m_list.getIterator();
1044 i.atEnd()==false; i++)
1046 v3s16 p = i.getNode()->getKey();
1048 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1049 <<") being handled"<<std::endl;*/
1051 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1055 // Reset block usage timer
1056 block->resetUsageTimer();
1058 // Set current time as timestamp
1059 block->setTimestampNoChangedFlag(m_game_time);
1060 // If time has changed much from the one on disk,
1061 // set block to be saved when it is unloaded
1062 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1063 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1064 "Timestamp older than 60s (step)");
1066 // Run node metadata
1067 bool changed = block->m_node_metadata->step(dtime);
1071 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
1073 m_map->dispatchEvent(&event);
1075 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1076 "node metadata modified in step");
1081 const float abm_interval = 1.0;
1082 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1084 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1085 TimeTaker timer("modify in active blocks");
1087 // Initialize handling of ActiveBlockModifiers
1088 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1090 for(core::map<v3s16, bool>::Iterator
1091 i = m_active_blocks.m_list.getIterator();
1092 i.atEnd()==false; i++)
1094 v3s16 p = i.getNode()->getKey();
1096 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1097 <<") being handled"<<std::endl;*/
1099 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1103 // Set current time as timestamp
1104 block->setTimestampNoChangedFlag(m_game_time);
1106 /* Handle ActiveBlockModifiers */
1107 abmhandler.apply(block);
1110 u32 time_ms = timer.stop(true);
1111 u32 max_time_ms = 200;
1112 if(time_ms > max_time_ms){
1113 infostream<<"WARNING: active block modifiers took "
1114 <<time_ms<<"ms (longer than "
1115 <<max_time_ms<<"ms)"<<std::endl;
1120 Step script environment (run global on_step())
1122 scriptapi_environment_step(m_lua, dtime);
1128 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1129 //TimeTaker timer("Step active objects");
1131 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1133 // This helps the objects to send data at the same time
1134 bool send_recommended = false;
1135 m_send_recommended_timer += dtime;
1136 if(m_send_recommended_timer > getSendRecommendedInterval())
1138 m_send_recommended_timer -= getSendRecommendedInterval();
1139 send_recommended = true;
1142 for(core::map<u16, ServerActiveObject*>::Iterator
1143 i = m_active_objects.getIterator();
1144 i.atEnd()==false; i++)
1146 ServerActiveObject* obj = i.getNode()->getValue();
1147 // Remove non-peaceful mobs on peaceful mode
1148 if(g_settings->getBool("only_peaceful_mobs")){
1149 if(!obj->isPeaceful())
1150 obj->m_removed = true;
1152 // Don't step if is to be removed or stored statically
1153 if(obj->m_removed || obj->m_pending_deactivation)
1156 obj->step(dtime, send_recommended);
1157 // Read messages from object
1158 while(obj->m_messages_out.size() > 0)
1160 m_active_object_messages.push_back(
1161 obj->m_messages_out.pop_front());
1167 Manage active objects
1169 if(m_object_management_interval.step(dtime, 0.5))
1171 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1173 Remove objects that satisfy (m_removed && m_known_by_count==0)
1175 removeRemovedObjects();
1179 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1181 core::map<u16, ServerActiveObject*>::Node *n;
1182 n = m_active_objects.find(id);
1185 return n->getValue();
1188 bool isFreeServerActiveObjectId(u16 id,
1189 core::map<u16, ServerActiveObject*> &objects)
1194 for(core::map<u16, ServerActiveObject*>::Iterator
1195 i = objects.getIterator();
1196 i.atEnd()==false; i++)
1198 if(i.getNode()->getKey() == id)
1204 u16 getFreeServerActiveObjectId(
1205 core::map<u16, ServerActiveObject*> &objects)
1210 if(isFreeServerActiveObjectId(new_id, objects))
1220 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1223 u16 id = addActiveObjectRaw(object, true);
1227 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1231 v3f objectpos = obj->getBasePosition();
1233 // The block in which the object resides in
1234 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1237 Update the static data
1240 // Create new static object
1241 std::string staticdata = obj->getStaticData();
1242 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1243 // Add to the block where the object is located in
1244 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1245 // Get or generate the block
1246 MapBlock *block = m_map->emergeBlock(blockpos);
1248 bool succeeded = false;
1252 block->m_static_objects.insert(0, s_obj);
1253 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1254 "addActiveObjectAsStatic");
1258 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1259 <<"Could not find or generate "
1260 <<"a block for storing static object"<<std::endl;
1264 if(obj->environmentDeletes())
1271 Finds out what new objects have been added to
1272 inside a radius around a position
1274 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1275 core::map<u16, bool> ¤t_objects,
1276 core::map<u16, bool> &added_objects)
1278 v3f pos_f = intToFloat(pos, BS);
1279 f32 radius_f = radius * BS;
1281 Go through the object list,
1282 - discard m_removed objects,
1283 - discard objects that are too far away,
1284 - discard objects that are found in current_objects.
1285 - add remaining objects to added_objects
1287 for(core::map<u16, ServerActiveObject*>::Iterator
1288 i = m_active_objects.getIterator();
1289 i.atEnd()==false; i++)
1291 u16 id = i.getNode()->getKey();
1293 ServerActiveObject *object = i.getNode()->getValue();
1296 // Discard if removed
1297 if(object->m_removed)
1299 if(object->unlimitedTransferDistance() == false){
1300 // Discard if too far
1301 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1302 if(distance_f > radius_f)
1305 // Discard if already on current_objects
1306 core::map<u16, bool>::Node *n;
1307 n = current_objects.find(id);
1310 // Add to added_objects
1311 added_objects.insert(id, false);
1316 Finds out what objects have been removed from
1317 inside a radius around a position
1319 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1320 core::map<u16, bool> ¤t_objects,
1321 core::map<u16, bool> &removed_objects)
1323 v3f pos_f = intToFloat(pos, BS);
1324 f32 radius_f = radius * BS;
1326 Go through current_objects; object is removed if:
1327 - object is not found in m_active_objects (this is actually an
1328 error condition; objects should be set m_removed=true and removed
1329 only after all clients have been informed about removal), or
1330 - object has m_removed=true, or
1331 - object is too far away
1333 for(core::map<u16, bool>::Iterator
1334 i = current_objects.getIterator();
1335 i.atEnd()==false; i++)
1337 u16 id = i.getNode()->getKey();
1338 ServerActiveObject *object = getActiveObject(id);
1341 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1342 <<" object in current_objects is NULL"<<std::endl;
1343 removed_objects.insert(id, false);
1347 if(object->m_removed)
1349 removed_objects.insert(id, false);
1353 // If transfer distance is unlimited, don't remove
1354 if(object->unlimitedTransferDistance())
1357 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1359 if(distance_f >= radius_f)
1361 removed_objects.insert(id, false);
1369 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1371 if(m_active_object_messages.size() == 0)
1372 return ActiveObjectMessage(0);
1374 return m_active_object_messages.pop_front();
1378 ************ Private methods *************
1381 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1385 if(object->getId() == 0){
1386 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1389 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1390 <<"no free ids available"<<std::endl;
1391 if(object->environmentDeletes())
1395 object->setId(new_id);
1398 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1399 <<"supplied with id "<<object->getId()<<std::endl;
1401 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1403 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1404 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1405 if(object->environmentDeletes())
1409 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1410 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1412 m_active_objects.insert(object->getId(), object);
1414 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1415 <<"Added id="<<object->getId()<<"; there are now "
1416 <<m_active_objects.size()<<" active objects."
1419 // Register reference in scripting api (must be done before post-init)
1420 scriptapi_add_object_reference(m_lua, object);
1421 // Post-initialize object
1422 object->addedToEnvironment();
1424 // Add static data to block
1425 if(object->isStaticAllowed())
1427 // Add static object to active static list of the block
1428 v3f objectpos = object->getBasePosition();
1429 std::string staticdata = object->getStaticData();
1430 StaticObject s_obj(object->getType(), objectpos, staticdata);
1431 // Add to the block where the object is located in
1432 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1433 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1436 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1437 object->m_static_exists = true;
1438 object->m_static_block = blockpos;
1441 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1442 "addActiveObjectRaw");
1445 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1446 <<"could not find block for storing id="<<object->getId()
1447 <<" statically"<<std::endl;
1451 return object->getId();
1455 Remove objects that satisfy (m_removed && m_known_by_count==0)
1457 void ServerEnvironment::removeRemovedObjects()
1459 core::list<u16> objects_to_remove;
1460 for(core::map<u16, ServerActiveObject*>::Iterator
1461 i = m_active_objects.getIterator();
1462 i.atEnd()==false; i++)
1464 u16 id = i.getNode()->getKey();
1465 ServerActiveObject* obj = i.getNode()->getValue();
1466 // This shouldn't happen but check it
1469 infostream<<"NULL object found in ServerEnvironment"
1470 <<" while finding removed objects. id="<<id<<std::endl;
1471 // Id to be removed from m_active_objects
1472 objects_to_remove.push_back(id);
1477 We will delete objects that are marked as removed or thatare
1478 waiting for deletion after deactivation
1480 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1484 Delete static data from block if is marked as removed
1486 if(obj->m_static_exists && obj->m_removed)
1488 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1491 block->m_static_objects.remove(id);
1492 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1493 "removeRemovedObjects");
1494 obj->m_static_exists = false;
1498 // If m_known_by_count > 0, don't actually remove.
1499 if(obj->m_known_by_count > 0)
1502 // Tell the object about removal
1503 obj->removingFromEnvironment();
1504 // Deregister in scripting api
1505 scriptapi_rm_object_reference(m_lua, obj);
1508 if(obj->environmentDeletes())
1510 // Id to be removed from m_active_objects
1511 objects_to_remove.push_back(id);
1513 // Remove references from m_active_objects
1514 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1515 i != objects_to_remove.end(); i++)
1517 m_active_objects.remove(*i);
1521 static void print_hexdump(std::ostream &o, const std::string &data)
1523 const int linelength = 16;
1524 for(int l=0; ; l++){
1525 int i0 = linelength * l;
1526 bool at_end = false;
1527 int thislinelength = linelength;
1528 if(i0 + thislinelength > (int)data.size()){
1529 thislinelength = data.size() - i0;
1532 for(int di=0; di<linelength; di++){
1535 if(di<thislinelength)
1536 snprintf(buf, 4, "%.2x ", data[i]);
1538 snprintf(buf, 4, " ");
1542 for(int di=0; di<thislinelength; di++){
1556 Convert stored objects from blocks near the players to active.
1558 void ServerEnvironment::activateObjects(MapBlock *block)
1562 // Ignore if no stored objects (to not set changed flag)
1563 if(block->m_static_objects.m_stored.size() == 0)
1565 verbosestream<<"ServerEnvironment::activateObjects(): "
1566 <<"activating objects of block "<<PP(block->getPos())
1567 <<" ("<<block->m_static_objects.m_stored.size()
1568 <<" objects)"<<std::endl;
1569 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1571 errorstream<<"suspiciously large amount of objects detected: "
1572 <<block->m_static_objects.m_stored.size()<<" in "
1573 <<PP(block->getPos())
1574 <<"; removing all of them."<<std::endl;
1575 // Clear stored list
1576 block->m_static_objects.m_stored.clear();
1577 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1578 "stored list cleared in activateObjects due to "
1579 "large amount of objects");
1582 // A list for objects that couldn't be converted to static for some
1583 // reason. They will be stored back.
1584 core::list<StaticObject> new_stored;
1585 // Loop through stored static objects
1586 for(core::list<StaticObject>::Iterator
1587 i = block->m_static_objects.m_stored.begin();
1588 i != block->m_static_objects.m_stored.end(); i++)
1590 /*infostream<<"Server: Creating an active object from "
1591 <<"static data"<<std::endl;*/
1592 StaticObject &s_obj = *i;
1593 // Create an active object from the data
1594 ServerActiveObject *obj = ServerActiveObject::create
1595 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1596 // If couldn't create object, store static data back.
1599 errorstream<<"ServerEnvironment::activateObjects(): "
1600 <<"failed to create active object from static object "
1601 <<"in block "<<PP(s_obj.pos/BS)
1602 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1603 print_hexdump(verbosestream, s_obj.data);
1605 new_stored.push_back(s_obj);
1608 verbosestream<<"ServerEnvironment::activateObjects(): "
1609 <<"activated static object pos="<<PP(s_obj.pos/BS)
1610 <<" type="<<(int)s_obj.type<<std::endl;
1611 // This will also add the object to the active static list
1612 addActiveObjectRaw(obj, false);
1614 // Clear stored list
1615 block->m_static_objects.m_stored.clear();
1616 // Add leftover failed stuff to stored list
1617 for(core::list<StaticObject>::Iterator
1618 i = new_stored.begin();
1619 i != new_stored.end(); i++)
1621 StaticObject &s_obj = *i;
1622 block->m_static_objects.m_stored.push_back(s_obj);
1625 Note: Block hasn't really been modified here.
1626 The objects have just been activated and moved from the stored
1627 static list to the active static list.
1628 As such, the block is essentially the same.
1629 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1630 Otherwise there would be a huge amount of unnecessary I/O.
1635 Convert objects that are not standing inside active blocks to static.
1637 If m_known_by_count != 0, active object is not deleted, but static
1638 data is still updated.
1640 If force_delete is set, active object is deleted nevertheless. It
1641 shall only be set so in the destructor of the environment.
1643 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1645 core::list<u16> objects_to_remove;
1646 for(core::map<u16, ServerActiveObject*>::Iterator
1647 i = m_active_objects.getIterator();
1648 i.atEnd()==false; i++)
1650 ServerActiveObject* obj = i.getNode()->getValue();
1653 // Do not deactivate if static data creation not allowed
1654 if(!force_delete && !obj->isStaticAllowed())
1657 // If pending deactivation, let removeRemovedObjects() do it
1658 if(!force_delete && obj->m_pending_deactivation)
1661 u16 id = i.getNode()->getKey();
1662 v3f objectpos = obj->getBasePosition();
1664 // The block in which the object resides in
1665 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1667 // If block is active, don't remove
1668 if(!force_delete && m_active_blocks.contains(blockpos_o))
1671 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1672 <<"deactivating object id="<<id<<" on inactive block "
1673 <<PP(blockpos_o)<<std::endl;
1675 // If known by some client, don't immediately delete.
1676 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1679 Update the static data
1682 if(obj->isStaticAllowed())
1684 // Create new static object
1685 std::string staticdata_new = obj->getStaticData();
1686 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1688 bool stays_in_same_block = false;
1689 bool data_changed = true;
1691 if(obj->m_static_exists){
1692 if(obj->m_static_block == blockpos_o)
1693 stays_in_same_block = true;
1695 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1697 core::map<u16, StaticObject>::Node *n =
1698 block->m_static_objects.m_active.find(id);
1700 StaticObject static_old = n->getValue();
1702 float save_movem = obj->getMinimumSavedMovement();
1704 if(static_old.data == staticdata_new &&
1705 (static_old.pos - objectpos).getLength() < save_movem)
1706 data_changed = false;
1708 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1709 <<"id="<<id<<" m_static_exists=true but "
1710 <<"static data doesn't actually exist in "
1711 <<PP(obj->m_static_block)<<std::endl;
1715 bool shall_be_written = (!stays_in_same_block || data_changed);
1717 // Delete old static object
1718 if(obj->m_static_exists)
1720 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1723 block->m_static_objects.remove(id);
1724 obj->m_static_exists = false;
1725 // Only mark block as modified if data changed considerably
1726 if(shall_be_written)
1727 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1728 "deactivateFarObjects: Static data "
1729 "changed considerably");
1733 // Add to the block where the object is located in
1734 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1735 // Get or generate the block
1736 MapBlock *block = m_map->emergeBlock(blockpos);
1740 if(block->m_static_objects.m_stored.size() >= 49){
1741 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1742 <<" statically but block "<<PP(blockpos)
1743 <<" already contains "
1744 <<block->m_static_objects.m_stored.size()
1745 <<" (over 49) objects."
1746 <<" Forcing delete."<<std::endl;
1747 force_delete = true;
1749 u16 new_id = pending_delete ? id : 0;
1750 block->m_static_objects.insert(new_id, s_obj);
1752 // Only mark block as modified if data changed considerably
1753 if(shall_be_written)
1754 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1755 "deactivateFarObjects: Static data "
1756 "changed considerably");
1758 obj->m_static_exists = true;
1759 obj->m_static_block = block->getPos();
1764 errorstream<<"ServerEnv: Could not find or generate "
1765 <<"a block for storing id="<<obj->getId()
1766 <<" statically"<<std::endl;
1773 If known by some client, set pending deactivation.
1774 Otherwise delete it immediately.
1777 if(pending_delete && !force_delete)
1779 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1780 <<"object id="<<id<<" is known by clients"
1781 <<"; not deleting yet"<<std::endl;
1783 obj->m_pending_deactivation = true;
1787 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1788 <<"object id="<<id<<" is not known by clients"
1789 <<"; deleting"<<std::endl;
1791 // Tell the object about removal
1792 obj->removingFromEnvironment();
1793 // Deregister in scripting api
1794 scriptapi_rm_object_reference(m_lua, obj);
1796 // Delete active object
1797 if(obj->environmentDeletes())
1799 // Id to be removed from m_active_objects
1800 objects_to_remove.push_back(id);
1803 // Remove references from m_active_objects
1804 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1805 i != objects_to_remove.end(); i++)
1807 m_active_objects.remove(*i);
1814 #include "clientsimpleobject.h"
1820 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1821 ITextureSource *texturesource, IGameDef *gamedef,
1822 IrrlichtDevice *irr):
1825 m_texturesource(texturesource),
1831 ClientEnvironment::~ClientEnvironment()
1833 // delete active objects
1834 for(core::map<u16, ClientActiveObject*>::Iterator
1835 i = m_active_objects.getIterator();
1836 i.atEnd()==false; i++)
1838 delete i.getNode()->getValue();
1841 for(core::list<ClientSimpleObject*>::Iterator
1842 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1851 Map & ClientEnvironment::getMap()
1856 ClientMap & ClientEnvironment::getClientMap()
1861 void ClientEnvironment::addPlayer(Player *player)
1863 DSTACK(__FUNCTION_NAME);
1865 It is a failure if player is local and there already is a local
1868 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1870 Environment::addPlayer(player);
1873 LocalPlayer * ClientEnvironment::getLocalPlayer()
1875 for(core::list<Player*>::Iterator i = m_players.begin();
1876 i != m_players.end(); i++)
1878 Player *player = *i;
1879 if(player->isLocal())
1880 return (LocalPlayer*)player;
1885 void ClientEnvironment::step(float dtime)
1887 DSTACK(__FUNCTION_NAME);
1889 /* Step time of day */
1890 stepTimeOfDay(dtime);
1892 // Get some settings
1893 bool free_move = g_settings->getBool("free_move");
1896 LocalPlayer *lplayer = getLocalPlayer();
1898 // collision info queue
1899 core::list<CollisionInfo> player_collisions;
1902 Get the speed the player is going
1904 bool is_climbing = lplayer->is_climbing;
1906 f32 player_speed = lplayer->getSpeed().getLength();
1909 Maximum position increment
1911 //f32 position_max_increment = 0.05*BS;
1912 f32 position_max_increment = 0.1*BS;
1914 // Maximum time increment (for collision detection etc)
1915 // time = distance / speed
1916 f32 dtime_max_increment = 1;
1917 if(player_speed > 0.001)
1918 dtime_max_increment = position_max_increment / player_speed;
1920 // Maximum time increment is 10ms or lower
1921 if(dtime_max_increment > 0.01)
1922 dtime_max_increment = 0.01;
1924 // Don't allow overly huge dtime
1928 f32 dtime_downcount = dtime;
1931 Stuff that has a maximum time increment
1940 if(dtime_downcount > dtime_max_increment)
1942 dtime_part = dtime_max_increment;
1943 dtime_downcount -= dtime_part;
1947 dtime_part = dtime_downcount;
1949 Setting this to 0 (no -=dtime_part) disables an infinite loop
1950 when dtime_part is so small that dtime_downcount -= dtime_part
1953 dtime_downcount = 0;
1961 v3f lplayerpos = lplayer->getPosition();
1964 if(free_move == false && is_climbing == false)
1967 v3f speed = lplayer->getSpeed();
1968 if(lplayer->swimming_up == false)
1969 speed.Y -= 9.81 * BS * dtime_part * 2;
1972 if(lplayer->in_water_stable || lplayer->in_water)
1974 f32 max_down = 2.0*BS;
1975 if(speed.Y < -max_down) speed.Y = -max_down;
1978 if(speed.getLength() > max)
1980 speed = speed / speed.getLength() * max;
1984 lplayer->setSpeed(speed);
1989 This also does collision detection.
1991 lplayer->move(dtime_part, *m_map, position_max_increment,
1992 &player_collisions);
1995 while(dtime_downcount > 0.001);
1997 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1999 for(core::list<CollisionInfo>::Iterator
2000 i = player_collisions.begin();
2001 i != player_collisions.end(); i++)
2003 CollisionInfo &info = *i;
2004 if(info.t == COLLISION_FALL)
2006 //f32 tolerance = BS*10; // 2 without damage
2007 f32 tolerance = BS*12; // 3 without damage
2009 if(info.speed > tolerance)
2011 f32 damage_f = (info.speed - tolerance)/BS*factor;
2012 u16 damage = (u16)(damage_f+0.5);
2013 damageLocalPlayer(damage, true);
2019 A quick draft of lava damage
2021 if(m_lava_hurt_interval.step(dtime, 1.0))
2023 v3f pf = lplayer->getPosition();
2025 // Feet, middle and head
2026 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2027 MapNode n1 = m_map->getNodeNoEx(p1);
2028 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2029 MapNode n2 = m_map->getNodeNoEx(p2);
2030 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2031 MapNode n3 = m_map->getNodeNoEx(p2);
2033 u32 damage_per_second = 0;
2034 damage_per_second = MYMAX(damage_per_second,
2035 m_gamedef->ndef()->get(n1).damage_per_second);
2036 damage_per_second = MYMAX(damage_per_second,
2037 m_gamedef->ndef()->get(n2).damage_per_second);
2038 damage_per_second = MYMAX(damage_per_second,
2039 m_gamedef->ndef()->get(n3).damage_per_second);
2041 if(damage_per_second != 0)
2043 damageLocalPlayer(damage_per_second, true);
2048 Stuff that can be done in an arbitarily large dtime
2050 for(core::list<Player*>::Iterator i = m_players.begin();
2051 i != m_players.end(); i++)
2053 Player *player = *i;
2054 v3f playerpos = player->getPosition();
2057 Handle non-local players
2059 if(player->isLocal() == false)
2062 player->move(dtime, *m_map, 100*BS);
2066 // Update lighting on all players on client
2067 u8 light = LIGHT_MAX;
2070 v3s16 p = player->getLightPosition();
2071 MapNode n = m_map->getNode(p);
2072 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2074 catch(InvalidPositionException &e){
2075 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2077 player->updateLight(light);
2081 Step active objects and update lighting of them
2084 for(core::map<u16, ClientActiveObject*>::Iterator
2085 i = m_active_objects.getIterator();
2086 i.atEnd()==false; i++)
2088 ClientActiveObject* obj = i.getNode()->getValue();
2090 obj->step(dtime, this);
2092 if(m_active_object_light_update_interval.step(dtime, 0.21))
2098 v3s16 p = obj->getLightPosition();
2099 MapNode n = m_map->getNode(p);
2100 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2102 catch(InvalidPositionException &e){
2103 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2105 obj->updateLight(light);
2110 Step and handle simple objects
2112 for(core::list<ClientSimpleObject*>::Iterator
2113 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2115 ClientSimpleObject *simple = *i;
2116 core::list<ClientSimpleObject*>::Iterator cur = i;
2118 simple->step(dtime);
2119 if(simple->m_to_be_removed){
2121 m_simple_objects.erase(cur);
2126 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2128 m_simple_objects.push_back(simple);
2131 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2133 core::map<u16, ClientActiveObject*>::Node *n;
2134 n = m_active_objects.find(id);
2137 return n->getValue();
2140 bool isFreeClientActiveObjectId(u16 id,
2141 core::map<u16, ClientActiveObject*> &objects)
2146 for(core::map<u16, ClientActiveObject*>::Iterator
2147 i = objects.getIterator();
2148 i.atEnd()==false; i++)
2150 if(i.getNode()->getKey() == id)
2156 u16 getFreeClientActiveObjectId(
2157 core::map<u16, ClientActiveObject*> &objects)
2162 if(isFreeClientActiveObjectId(new_id, objects))
2172 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2175 if(object->getId() == 0)
2177 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2180 infostream<<"ClientEnvironment::addActiveObject(): "
2181 <<"no free ids available"<<std::endl;
2185 object->setId(new_id);
2187 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2189 infostream<<"ClientEnvironment::addActiveObject(): "
2190 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2194 infostream<<"ClientEnvironment::addActiveObject(): "
2195 <<"added (id="<<object->getId()<<")"<<std::endl;
2196 m_active_objects.insert(object->getId(), object);
2197 object->addToScene(m_smgr, m_texturesource, m_irr);
2198 { // Update lighting immediately
2202 v3s16 p = object->getLightPosition();
2203 MapNode n = m_map->getNode(p);
2204 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2206 catch(InvalidPositionException &e){
2207 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2209 object->updateLight(light);
2211 return object->getId();
2214 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2215 const std::string &init_data)
2217 ClientActiveObject* obj =
2218 ClientActiveObject::create(type, m_gamedef, this);
2221 infostream<<"ClientEnvironment::addActiveObject(): "
2222 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2229 obj->initialize(init_data);
2231 addActiveObject(obj);
2234 void ClientEnvironment::removeActiveObject(u16 id)
2236 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2237 <<"id="<<id<<std::endl;
2238 ClientActiveObject* obj = getActiveObject(id);
2241 infostream<<"ClientEnvironment::removeActiveObject(): "
2242 <<"id="<<id<<" not found"<<std::endl;
2245 obj->removeFromScene();
2247 m_active_objects.remove(id);
2250 void ClientEnvironment::processActiveObjectMessage(u16 id,
2251 const std::string &data)
2253 ClientActiveObject* obj = getActiveObject(id);
2256 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2257 <<" got message for id="<<id<<", which doesn't exist."
2261 obj->processMessage(data);
2265 Callbacks for activeobjects
2268 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2270 LocalPlayer *lplayer = getLocalPlayer();
2274 if(lplayer->hp > damage)
2275 lplayer->hp -= damage;
2280 ClientEnvEvent event;
2281 event.type = CEE_PLAYER_DAMAGE;
2282 event.player_damage.amount = damage;
2283 event.player_damage.send_to_server = handle_hp;
2284 m_client_event_queue.push_back(event);
2288 Client likes to call these
2291 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2292 core::array<DistanceSortedActiveObject> &dest)
2294 for(core::map<u16, ClientActiveObject*>::Iterator
2295 i = m_active_objects.getIterator();
2296 i.atEnd()==false; i++)
2298 ClientActiveObject* obj = i.getNode()->getValue();
2300 f32 d = (obj->getPosition() - origin).getLength();
2305 DistanceSortedActiveObject dso(obj, d);
2307 dest.push_back(dso);
2311 ClientEnvEvent ClientEnvironment::getClientEvent()
2313 if(m_client_event_queue.size() == 0)
2315 ClientEnvEvent event;
2316 event.type = CEE_NONE;
2319 return m_client_event_queue.pop_front();
2322 #endif // #ifndef SERVER