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.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
32 #include "scriptapi.h"
34 #include "nodemetadata.h"
35 #include "main.h" // For g_settings, g_profiler
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
40 Environment::Environment():
45 Environment::~Environment()
48 for(core::list<Player*>::Iterator i = m_players.begin();
49 i != m_players.end(); i++)
55 void Environment::addPlayer(Player *player)
57 DSTACK(__FUNCTION_NAME);
59 Check that peer_ids are unique.
60 Also check that names are unique.
61 Exception: there can be multiple players with peer_id=0
63 // If peer id is non-zero, it has to be unique.
64 if(player->peer_id != 0)
65 assert(getPlayer(player->peer_id) == NULL);
66 // Name has to be unique.
67 assert(getPlayer(player->getName()) == NULL);
69 m_players.push_back(player);
72 void Environment::removePlayer(u16 peer_id)
74 DSTACK(__FUNCTION_NAME);
76 for(core::list<Player*>::Iterator i = m_players.begin();
77 i != m_players.end(); i++)
80 if(player->peer_id != peer_id)
85 // See if there is an another one
86 // (shouldn't be, but just to be sure)
91 Player * Environment::getPlayer(u16 peer_id)
93 for(core::list<Player*>::Iterator i = m_players.begin();
94 i != m_players.end(); i++)
97 if(player->peer_id == peer_id)
103 Player * Environment::getPlayer(const char *name)
105 for(core::list<Player*>::Iterator i = m_players.begin();
106 i != m_players.end(); i++)
109 if(strcmp(player->getName(), name) == 0)
115 Player * Environment::getRandomConnectedPlayer()
117 core::list<Player*> connected_players = getPlayers(true);
118 u32 chosen_one = myrand() % connected_players.size();
120 for(core::list<Player*>::Iterator
121 i = connected_players.begin();
122 i != connected_players.end(); i++)
134 Player * Environment::getNearestConnectedPlayer(v3f pos)
136 core::list<Player*> connected_players = getPlayers(true);
138 Player *nearest_player = NULL;
139 for(core::list<Player*>::Iterator
140 i = connected_players.begin();
141 i != connected_players.end(); i++)
144 f32 d = player->getPosition().getDistanceFrom(pos);
145 if(d < nearest_d || nearest_player == NULL)
148 nearest_player = player;
151 return nearest_player;
154 core::list<Player*> Environment::getPlayers()
159 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
161 core::list<Player*> newlist;
162 for(core::list<Player*>::Iterator
163 i = m_players.begin();
164 i != m_players.end(); i++)
168 if(ignore_disconnected)
170 // Ignore disconnected players
171 if(player->peer_id == 0)
175 newlist.push_back(player);
180 void Environment::printPlayers(std::ostream &o)
182 o<<"Players in environment:"<<std::endl;
183 for(core::list<Player*>::Iterator i = m_players.begin();
184 i != m_players.end(); i++)
187 o<<"Player peer_id="<<player->peer_id<<std::endl;
191 /*void Environment::setDayNightRatio(u32 r)
193 getDayNightRatio() = r;
196 u32 Environment::getDayNightRatio()
198 //return getDayNightRatio();
199 return time_to_daynight_ratio(m_time_of_day);
206 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
209 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
210 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
211 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
218 void ActiveBlockList::update(core::list<v3s16> &active_positions,
220 core::map<v3s16, bool> &blocks_removed,
221 core::map<v3s16, bool> &blocks_added)
226 core::map<v3s16, bool> newlist;
227 for(core::list<v3s16>::Iterator i = active_positions.begin();
228 i != active_positions.end(); i++)
230 fillRadiusBlock(*i, radius, newlist);
234 Find out which blocks on the old list are not on the new list
236 // Go through old list
237 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
238 i.atEnd()==false; i++)
240 v3s16 p = i.getNode()->getKey();
241 // If not on new list, it's been removed
242 if(newlist.find(p) == NULL)
243 blocks_removed.insert(p, true);
247 Find out which blocks on the new list are not on the old list
249 // Go through new list
250 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251 i.atEnd()==false; i++)
253 v3s16 p = i.getNode()->getKey();
254 // If not on old list, it's been added
255 if(m_list.find(p) == NULL)
256 blocks_added.insert(p, true);
263 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
264 i.atEnd()==false; i++)
266 v3s16 p = i.getNode()->getKey();
267 m_list.insert(p, true);
275 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
280 m_random_spawn_timer(3),
281 m_send_recommended_timer(0),
283 m_game_time_fraction_counter(0)
287 ServerEnvironment::~ServerEnvironment()
289 // Clear active block list.
290 // This makes the next one delete all active objects.
291 m_active_blocks.clear();
293 // Convert all objects to static and delete the active objects
294 deactivateFarObjects(true);
300 void ServerEnvironment::serializePlayers(const std::string &savedir)
302 std::string players_path = savedir + "/players";
303 fs::CreateDir(players_path);
305 core::map<Player*, bool> saved_players;
307 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
308 for(u32 i=0; i<player_files.size(); i++)
310 if(player_files[i].dir)
313 // Full path to this file
314 std::string path = players_path + "/" + player_files[i].name;
316 //infostream<<"Checking player file "<<path<<std::endl;
318 // Load player to see what is its name
319 ServerRemotePlayer testplayer(this);
321 // Open file and deserialize
322 std::ifstream is(path.c_str(), std::ios_base::binary);
323 if(is.good() == false)
325 infostream<<"Failed to read "<<path<<std::endl;
328 testplayer.deSerialize(is);
331 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
333 // Search for the player
334 std::string playername = testplayer.getName();
335 Player *player = getPlayer(playername.c_str());
338 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
342 //infostream<<"Found matching player, overwriting."<<std::endl;
344 // OK, found. Save player there.
346 // Open file and serialize
347 std::ofstream os(path.c_str(), std::ios_base::binary);
348 if(os.good() == false)
350 infostream<<"Failed to overwrite "<<path<<std::endl;
353 player->serialize(os);
354 saved_players.insert(player, true);
358 for(core::list<Player*>::Iterator i = m_players.begin();
359 i != m_players.end(); i++)
362 if(saved_players.find(player) != NULL)
364 /*infostream<<"Player "<<player->getName()
365 <<" was already saved."<<std::endl;*/
368 std::string playername = player->getName();
369 // Don't save unnamed player
372 //infostream<<"Not saving unnamed player."<<std::endl;
378 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
379 playername = "player";
380 std::string path = players_path + "/" + playername;
382 for(u32 i=0; i<1000; i++)
384 if(fs::PathExists(path) == false)
389 path = players_path + "/" + playername + itos(i);
393 infostream<<"Didn't find free file for player"<<std::endl;
398 /*infostream<<"Saving player "<<player->getName()<<" to "
400 // Open file and serialize
401 std::ofstream os(path.c_str(), std::ios_base::binary);
402 if(os.good() == false)
404 infostream<<"Failed to overwrite "<<path<<std::endl;
407 player->serialize(os);
408 saved_players.insert(player, true);
412 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
415 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
417 std::string players_path = savedir + "/players";
419 core::map<Player*, bool> saved_players;
421 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
422 for(u32 i=0; i<player_files.size(); i++)
424 if(player_files[i].dir)
427 // Full path to this file
428 std::string path = players_path + "/" + player_files[i].name;
430 infostream<<"Checking player file "<<path<<std::endl;
432 // Load player to see what is its name
433 ServerRemotePlayer testplayer(this);
435 // Open file and deserialize
436 std::ifstream is(path.c_str(), std::ios_base::binary);
437 if(is.good() == false)
439 infostream<<"Failed to read "<<path<<std::endl;
442 testplayer.deSerialize(is);
445 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
447 infostream<<"Not loading player with invalid name: "
448 <<testplayer.getName()<<std::endl;
451 infostream<<"Loaded test player with name "<<testplayer.getName()
454 // Search for the player
455 std::string playername = testplayer.getName();
456 Player *player = getPlayer(playername.c_str());
457 bool newplayer = false;
460 infostream<<"Is a new player"<<std::endl;
461 player = new ServerRemotePlayer(this);
467 infostream<<"Reading player "<<testplayer.getName()<<" from "
469 // Open file and deserialize
470 std::ifstream is(path.c_str(), std::ios_base::binary);
471 if(is.good() == false)
473 infostream<<"Failed to read "<<path<<std::endl;
476 player->deSerialize(is);
484 void ServerEnvironment::saveMeta(const std::string &savedir)
486 std::string path = savedir + "/env_meta.txt";
488 // Open file and serialize
489 std::ofstream os(path.c_str(), std::ios_base::binary);
490 if(os.good() == false)
492 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
494 throw SerializationError("Couldn't save env meta");
498 args.setU64("game_time", m_game_time);
499 args.setU64("time_of_day", getTimeOfDay());
504 void ServerEnvironment::loadMeta(const std::string &savedir)
506 std::string path = savedir + "/env_meta.txt";
508 // Open file and deserialize
509 std::ifstream is(path.c_str(), std::ios_base::binary);
510 if(is.good() == false)
512 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
514 throw SerializationError("Couldn't load env meta");
522 throw SerializationError
523 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
525 std::getline(is, line);
526 std::string trimmedline = trim(line);
527 if(trimmedline == "EnvArgsEnd")
529 args.parseConfigLine(line);
533 m_game_time = args.getU64("game_time");
534 }catch(SettingNotFoundException &e){
535 // Getting this is crucial, otherwise timestamps are useless
536 throw SerializationError("Couldn't load env meta game_time");
540 m_time_of_day = args.getU64("time_of_day");
541 }catch(SettingNotFoundException &e){
542 // This is not as important
543 m_time_of_day = 9000;
548 // This is probably very useless
549 void spawnRandomObjects(MapBlock *block)
551 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
552 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
554 bool last_node_walkable = false;
555 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
558 MapNode n = block->getNodeNoEx(p);
559 if(n.getContent() == CONTENT_IGNORE)
561 if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
563 if(m_gamedef->ndef()->get(n).walkable)
565 last_node_walkable = true;
568 if(last_node_walkable)
570 // If block contains light information
571 if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
573 if(n.getLight(LIGHTBANK_DAY) <= 5)
575 if(myrand() % 1000 == 0)
577 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
579 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
580 std::string data = obj->getStaticData();
581 StaticObject s_obj(obj->getType(),
582 obj->getBasePosition(), data);
584 block->m_static_objects.insert(0, s_obj);
586 block->raiseModified(MOD_STATE_WRITE_NEEDED,
587 "spawnRandomObjects");
592 last_node_walkable = false;
598 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
600 // Get time difference
602 u32 stamp = block->getTimestamp();
603 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
604 dtime_s = m_game_time - block->getTimestamp();
605 dtime_s += additional_dtime;
607 // Set current time as timestamp (and let it set ChangedFlag)
608 block->setTimestamp(m_game_time);
610 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
612 // Activate stored objects
613 activateObjects(block);
616 bool changed = block->m_node_metadata->step((float)dtime_s);
620 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
621 event.p = block->getPos();
622 m_map->dispatchEvent(&event);
624 block->raiseModified(MOD_STATE_WRITE_NEEDED,
625 "node metadata modified in activateBlock");
628 // TODO: Do something
629 // TODO: Implement usage of ActiveBlockModifier
631 // Here's a quick demonstration
633 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
634 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
635 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
637 v3s16 p = p0 + block->getPosRelative();
638 MapNode n = block->getNodeNoEx(p0);
641 // Convert all mud under proper day lighting to grass
642 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
646 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
647 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
648 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
650 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
651 m_map->addNodeWithEvent(p, n);
659 void ServerEnvironment::clearAllObjects()
661 infostream<<"ServerEnvironment::clearAllObjects(): "
662 <<"Removing all active objects"<<std::endl;
663 core::list<u16> objects_to_remove;
664 for(core::map<u16, ServerActiveObject*>::Iterator
665 i = m_active_objects.getIterator();
666 i.atEnd()==false; i++)
668 ServerActiveObject* obj = i.getNode()->getValue();
669 u16 id = i.getNode()->getKey();
670 v3f objectpos = obj->getBasePosition();
671 // Delete static object if block is loaded
672 if(obj->m_static_exists){
673 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
675 block->m_static_objects.remove(id);
676 block->raiseModified(MOD_STATE_WRITE_NEEDED,
678 obj->m_static_exists = false;
681 // If known by some client, don't delete immediately
682 if(obj->m_known_by_count > 0){
683 obj->m_pending_deactivation = true;
684 obj->m_removed = true;
687 // Deregister in scripting api
688 scriptapi_rm_object_reference(m_lua, obj);
689 // Delete active object
691 // Id to be removed from m_active_objects
692 objects_to_remove.push_back(id);
694 // Remove references from m_active_objects
695 for(core::list<u16>::Iterator i = objects_to_remove.begin();
696 i != objects_to_remove.end(); i++)
698 m_active_objects.remove(*i);
701 core::list<v3s16> loadable_blocks;
702 infostream<<"ServerEnvironment::clearAllObjects(): "
703 <<"Listing all loadable blocks"<<std::endl;
704 m_map->listAllLoadableBlocks(loadable_blocks);
705 infostream<<"ServerEnvironment::clearAllObjects(): "
706 <<"Done listing all loadable blocks: "
707 <<loadable_blocks.size()
708 <<", now clearing"<<std::endl;
709 u32 report_interval = loadable_blocks.size() / 10;
710 u32 num_blocks_checked = 0;
711 u32 num_blocks_cleared = 0;
712 u32 num_objs_cleared = 0;
713 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
714 i != loadable_blocks.end(); i++)
717 MapBlock *block = m_map->emergeBlock(p, false);
719 errorstream<<"ServerEnvironment::clearAllObjects(): "
720 <<"Failed to emerge block "<<PP(p)<<std::endl;
723 u32 num_stored = block->m_static_objects.m_stored.size();
724 u32 num_active = block->m_static_objects.m_active.size();
725 if(num_stored != 0 || num_active != 0){
726 block->m_static_objects.m_stored.clear();
727 block->m_static_objects.m_active.clear();
728 block->raiseModified(MOD_STATE_WRITE_NEEDED,
730 num_objs_cleared += num_stored + num_active;
731 num_blocks_cleared++;
733 num_blocks_checked++;
735 if(num_blocks_checked % report_interval == 0){
736 float percent = 100.0 * (float)num_blocks_checked /
737 loadable_blocks.size();
738 infostream<<"ServerEnvironment::clearAllObjects(): "
739 <<"Cleared "<<num_objs_cleared<<" objects"
740 <<" in "<<num_blocks_cleared<<" blocks ("
741 <<percent<<"%)"<<std::endl;
744 infostream<<"ServerEnvironment::clearAllObjects(): "
745 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
746 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
749 static void getMob_dungeon_master(Settings &properties)
751 properties.set("looks", "dungeon_master");
752 properties.setFloat("yaw", 1.57);
753 properties.setFloat("hp", 30);
754 properties.setBool("bright_shooting", true);
755 properties.set("shoot_type", "fireball");
756 properties.set("shoot_y", "0.7");
757 properties.set("player_hit_damage", "1");
758 properties.set("player_hit_distance", "1.0");
759 properties.set("player_hit_interval", "0.5");
760 properties.setBool("mindless_rage", myrand_range(0,100)==0);
763 void ServerEnvironment::step(float dtime)
765 DSTACK(__FUNCTION_NAME);
767 //TimeTaker timer("ServerEnv step");
770 bool footprints = g_settings->getBool("footprints");
776 m_game_time_fraction_counter += dtime;
777 u32 inc_i = (u32)m_game_time_fraction_counter;
778 m_game_time += inc_i;
779 m_game_time_fraction_counter -= (float)inc_i;
786 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
787 for(core::list<Player*>::Iterator i = m_players.begin();
788 i != m_players.end(); i++)
792 // Ignore disconnected players
793 if(player->peer_id == 0)
796 v3f playerpos = player->getPosition();
799 player->move(dtime, *m_map, 100*BS);
802 Add footsteps to grass
806 // Get node that is at BS/4 under player
807 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
809 MapNode n = m_map->getNode(bottompos);
810 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
812 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
813 m_map->setNode(bottompos, n);
816 catch(InvalidPositionException &e)
824 Manage active block list
826 if(m_active_blocks_management_interval.step(dtime, 2.0))
828 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
830 Get player block positions
832 core::list<v3s16> players_blockpos;
833 for(core::list<Player*>::Iterator
834 i = m_players.begin();
835 i != m_players.end(); i++)
838 // Ignore disconnected players
839 if(player->peer_id == 0)
841 v3s16 blockpos = getNodeBlockPos(
842 floatToInt(player->getPosition(), BS));
843 players_blockpos.push_back(blockpos);
847 Update list of active blocks, collecting changes
849 const s16 active_block_range = g_settings->getS16("active_block_range");
850 core::map<v3s16, bool> blocks_removed;
851 core::map<v3s16, bool> blocks_added;
852 m_active_blocks.update(players_blockpos, active_block_range,
853 blocks_removed, blocks_added);
856 Handle removed blocks
859 // Convert active objects that are no more in active blocks to static
860 deactivateFarObjects(false);
862 for(core::map<v3s16, bool>::Iterator
863 i = blocks_removed.getIterator();
864 i.atEnd()==false; i++)
866 v3s16 p = i.getNode()->getKey();
868 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
869 <<") became inactive"<<std::endl;*/
871 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
875 // Set current time as timestamp (and let it set ChangedFlag)
876 block->setTimestamp(m_game_time);
883 for(core::map<v3s16, bool>::Iterator
884 i = blocks_added.getIterator();
885 i.atEnd()==false; i++)
887 v3s16 p = i.getNode()->getKey();
889 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
890 <<") became active"<<std::endl;*/
892 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
896 activateBlock(block);
901 Mess around in active blocks
903 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
905 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
909 for(core::map<v3s16, bool>::Iterator
910 i = m_active_blocks.m_list.getIterator();
911 i.atEnd()==false; i++)
913 v3s16 p = i.getNode()->getKey();
915 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
916 <<") being handled"<<std::endl;*/
918 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
922 // Reset block usage timer
923 block->resetUsageTimer();
925 // Set current time as timestamp
926 block->setTimestampNoChangedFlag(m_game_time);
929 bool changed = block->m_node_metadata->step(dtime);
933 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
935 m_map->dispatchEvent(&event);
937 block->raiseModified(MOD_STATE_WRITE_NEEDED,
938 "node metadata modified in step");
943 if(m_active_blocks_test_interval.step(dtime, 10.0))
945 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
946 //float dtime = 10.0;
948 for(core::map<v3s16, bool>::Iterator
949 i = m_active_blocks.m_list.getIterator();
950 i.atEnd()==false; i++)
952 v3s16 p = i.getNode()->getKey();
954 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
955 <<") being handled"<<std::endl;*/
957 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
961 // Set current time as timestamp
962 block->setTimestampNoChangedFlag(m_game_time);
967 Note that map modifications should be done using the event-
968 making map methods so that the server gets information
971 Reading can be done quickly directly from the block.
973 Everything should bind to inside this single content
974 searching loop to keep things fast.
976 // TODO: Implement usage of ActiveBlockModifier
978 // Find out how many objects the block contains
979 //u32 active_object_count = block->m_static_objects.m_active.size();
980 // Find out how many objects this and all the neighbors contain
981 u32 active_object_count_wider = 0;
982 for(s16 x=-1; x<=1; x++)
983 for(s16 y=-1; y<=1; y++)
984 for(s16 z=-1; z<=1; z++)
986 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
989 active_object_count_wider +=
990 block->m_static_objects.m_active.size()
991 + block->m_static_objects.m_stored.size();
993 /*if(block->m_static_objects.m_stored.size() != 0){
994 errorstream<<"ServerEnvironment::step(): "
995 <<PP(block->getPos())<<" contains "
996 <<block->m_static_objects.m_stored.size()
997 <<" stored objects; "
998 <<"when spawning objects, when counting active "
999 <<"objects in wide area. relative position: "
1000 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1005 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1006 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1007 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1009 v3s16 p = p0 + block->getPosRelative();
1010 MapNode n = block->getNodeNoEx(p0);
1014 Convert mud under proper lighting to grass
1016 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
1018 if(myrand()%20 == 0)
1020 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1021 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
1022 n_top.getLightBlend(getDayNightRatio(),
1023 m_gamedef->ndef()) >= 13)
1025 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
1026 m_map->addNodeWithEvent(p, n);
1031 Convert grass into mud if under something else than air
1033 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
1035 //if(myrand()%20 == 0)
1037 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1038 if(m_gamedef->ndef()->get(n_top).air_equivalent == false)
1040 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_MUD"));
1041 m_map->addNodeWithEvent(p, n);
1046 Rats spawn around regular trees
1048 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_TREE") ||
1049 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_JUNGLETREE"))
1051 if(myrand()%200 == 0 && active_object_count_wider == 0)
1053 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1054 0, myrand_range(-2, 2));
1055 MapNode n1 = m_map->getNodeNoEx(p1);
1056 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1057 if(n1b.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS") &&
1058 n1.getContent() == CONTENT_AIR)
1060 v3f pos = intToFloat(p1, BS);
1061 ServerActiveObject *obj = new RatSAO(this, pos);
1062 addActiveObject(obj);
1067 Fun things spawn in caves and dungeons
1069 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_STONE") ||
1070 n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MOSSYCOBBLE"))
1072 if(myrand()%200 == 0 && active_object_count_wider == 0)
1074 v3s16 p1 = p + v3s16(0,1,0);
1075 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1076 if(n1a.getLightBlend(getDayNightRatio(),
1077 m_gamedef->ndef()) <= 3){
1078 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1079 if(n1a.getContent() == CONTENT_AIR &&
1080 n1b.getContent() == CONTENT_AIR)
1082 v3f pos = intToFloat(p1, BS);
1084 if(i == 0 || i == 1){
1085 actionstream<<"A dungeon master spawns at "
1086 <<PP(p1)<<std::endl;
1087 Settings properties;
1088 getMob_dungeon_master(properties);
1089 ServerActiveObject *obj = new MobV2SAO(
1090 this, pos, &properties);
1091 addActiveObject(obj);
1092 } else if(i == 2 || i == 3){
1093 actionstream<<"Rats spawn at "
1094 <<PP(p1)<<std::endl;
1095 for(int j=0; j<3; j++){
1096 ServerActiveObject *obj = new RatSAO(
1098 addActiveObject(obj);
1101 actionstream<<"An oerkki spawns at "
1102 <<PP(p1)<<std::endl;
1103 ServerActiveObject *obj = new Oerkki1SAO(
1105 addActiveObject(obj);
1112 Make trees from saplings!
1114 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_SAPLING"))
1116 if(myrand()%50 == 0)
1118 actionstream<<"A sapling grows into a tree at "
1121 core::map<v3s16, MapBlock*> modified_blocks;
1123 ManualMapVoxelManipulator vmanip(m_map);
1124 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1125 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1126 bool is_apple_tree = myrand()%4 == 0;
1127 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1129 vmanip.blitBackAll(&modified_blocks);
1132 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1133 for(core::map<v3s16, MapBlock*>::Iterator
1134 i = modified_blocks.getIterator();
1135 i.atEnd() == false; i++)
1137 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1139 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1141 // Send a MEET_OTHER event
1143 event.type = MEET_OTHER;
1144 for(core::map<v3s16, MapBlock*>::Iterator
1145 i = modified_blocks.getIterator();
1146 i.atEnd() == false; i++)
1148 v3s16 p = i.getNode()->getKey();
1149 event.modified_blocks.insert(p, true);
1151 m_map->dispatchEvent(&event);
1159 Step script environment (run global on_step())
1161 scriptapi_environment_step(m_lua, dtime);
1167 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1168 //TimeTaker timer("Step active objects");
1170 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1172 // This helps the objects to send data at the same time
1173 bool send_recommended = false;
1174 m_send_recommended_timer += dtime;
1175 if(m_send_recommended_timer > getSendRecommendedInterval())
1177 m_send_recommended_timer -= getSendRecommendedInterval();
1178 send_recommended = true;
1181 for(core::map<u16, ServerActiveObject*>::Iterator
1182 i = m_active_objects.getIterator();
1183 i.atEnd()==false; i++)
1185 ServerActiveObject* obj = i.getNode()->getValue();
1186 // Remove non-peaceful mobs on peaceful mode
1187 if(g_settings->getBool("only_peaceful_mobs")){
1188 if(!obj->isPeaceful())
1189 obj->m_removed = true;
1191 // Don't step if is to be removed or stored statically
1192 if(obj->m_removed || obj->m_pending_deactivation)
1195 obj->step(dtime, send_recommended);
1196 // Read messages from object
1197 while(obj->m_messages_out.size() > 0)
1199 m_active_object_messages.push_back(
1200 obj->m_messages_out.pop_front());
1206 Manage active objects
1208 if(m_object_management_interval.step(dtime, 0.5))
1210 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1212 Remove objects that satisfy (m_removed && m_known_by_count==0)
1214 removeRemovedObjects();
1217 if(g_settings->getBool("enable_experimental"))
1224 m_random_spawn_timer -= dtime;
1225 if(m_random_spawn_timer < 0)
1227 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1228 //m_random_spawn_timer += 2.0;
1229 m_random_spawn_timer += 200.0;
1235 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1236 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1237 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1239 Player *player = getRandomConnectedPlayer();
1242 pos = player->getPosition();
1244 myrand_range(-3,3)*BS,
1246 myrand_range(-3,3)*BS
1250 Create a ServerActiveObject
1253 //TestSAO *obj = new TestSAO(this, pos);
1254 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1255 //ServerActiveObject *obj = new RatSAO(this, pos);
1256 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1257 //ServerActiveObject *obj = new FireflySAO(this, pos);
1259 infostream<<"Server: Spawning MobV2SAO at "
1260 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1262 Settings properties;
1263 getMob_dungeon_master(properties);
1264 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1265 addActiveObject(obj);
1269 } // enable_experimental
1272 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1274 core::map<u16, ServerActiveObject*>::Node *n;
1275 n = m_active_objects.find(id);
1278 return n->getValue();
1281 bool isFreeServerActiveObjectId(u16 id,
1282 core::map<u16, ServerActiveObject*> &objects)
1287 for(core::map<u16, ServerActiveObject*>::Iterator
1288 i = objects.getIterator();
1289 i.atEnd()==false; i++)
1291 if(i.getNode()->getKey() == id)
1297 u16 getFreeServerActiveObjectId(
1298 core::map<u16, ServerActiveObject*> &objects)
1303 if(isFreeServerActiveObjectId(new_id, objects))
1313 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1316 u16 id = addActiveObjectRaw(object, true);
1320 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1324 v3f objectpos = obj->getBasePosition();
1326 // The block in which the object resides in
1327 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1330 Update the static data
1333 // Create new static object
1334 std::string staticdata = obj->getStaticData();
1335 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1336 // Add to the block where the object is located in
1337 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1338 // Get or generate the block
1339 MapBlock *block = m_map->emergeBlock(blockpos);
1341 bool succeeded = false;
1345 block->m_static_objects.insert(0, s_obj);
1346 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1347 "addActiveObjectAsStatic");
1351 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1352 <<"Could not find or generate "
1353 <<"a block for storing static object"<<std::endl;
1363 Finds out what new objects have been added to
1364 inside a radius around a position
1366 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1367 core::map<u16, bool> ¤t_objects,
1368 core::map<u16, bool> &added_objects)
1370 v3f pos_f = intToFloat(pos, BS);
1371 f32 radius_f = radius * BS;
1373 Go through the object list,
1374 - discard m_removed objects,
1375 - discard objects that are too far away,
1376 - discard objects that are found in current_objects.
1377 - add remaining objects to added_objects
1379 for(core::map<u16, ServerActiveObject*>::Iterator
1380 i = m_active_objects.getIterator();
1381 i.atEnd()==false; i++)
1383 u16 id = i.getNode()->getKey();
1385 ServerActiveObject *object = i.getNode()->getValue();
1388 // Discard if removed
1389 if(object->m_removed)
1391 // Discard if too far
1392 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1393 if(distance_f > radius_f)
1395 // Discard if already on current_objects
1396 core::map<u16, bool>::Node *n;
1397 n = current_objects.find(id);
1400 // Add to added_objects
1401 added_objects.insert(id, false);
1406 Finds out what objects have been removed from
1407 inside a radius around a position
1409 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1410 core::map<u16, bool> ¤t_objects,
1411 core::map<u16, bool> &removed_objects)
1413 v3f pos_f = intToFloat(pos, BS);
1414 f32 radius_f = radius * BS;
1416 Go through current_objects; object is removed if:
1417 - object is not found in m_active_objects (this is actually an
1418 error condition; objects should be set m_removed=true and removed
1419 only after all clients have been informed about removal), or
1420 - object has m_removed=true, or
1421 - object is too far away
1423 for(core::map<u16, bool>::Iterator
1424 i = current_objects.getIterator();
1425 i.atEnd()==false; i++)
1427 u16 id = i.getNode()->getKey();
1428 ServerActiveObject *object = getActiveObject(id);
1431 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1432 <<" object in current_objects is NULL"<<std::endl;
1434 else if(object->m_removed == false)
1436 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1437 /*infostream<<"removed == false"
1438 <<"distance_f = "<<distance_f
1439 <<", radius_f = "<<radius_f<<std::endl;*/
1440 if(distance_f < radius_f)
1446 removed_objects.insert(id, false);
1450 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1452 if(m_active_object_messages.size() == 0)
1453 return ActiveObjectMessage(0);
1455 return m_active_object_messages.pop_front();
1459 ************ Private methods *************
1462 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1466 if(object->getId() == 0){
1467 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1470 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1471 <<"no free ids available"<<std::endl;
1475 object->setId(new_id);
1478 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1479 <<"supplied with id "<<object->getId()<<std::endl;
1481 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1483 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1488 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1489 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1491 m_active_objects.insert(object->getId(), object);
1493 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1494 <<"Added id="<<object->getId()<<"; there are now "
1495 <<m_active_objects.size()<<" active objects."
1498 // Add static object to active static list of the block
1499 v3f objectpos = object->getBasePosition();
1500 std::string staticdata = object->getStaticData();
1501 StaticObject s_obj(object->getType(), objectpos, staticdata);
1502 // Add to the block where the object is located in
1503 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1504 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1507 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1508 object->m_static_exists = true;
1509 object->m_static_block = blockpos;
1512 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1513 "addActiveObjectRaw");
1516 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1517 <<"could not find block for storing id="<<object->getId()
1518 <<" statically"<<std::endl;
1521 // Register reference in scripting api (must be done before post-init)
1522 scriptapi_add_object_reference(m_lua, object);
1523 // Post-initialize object
1524 object->addedToEnvironment();
1526 return object->getId();
1530 Remove objects that satisfy (m_removed && m_known_by_count==0)
1532 void ServerEnvironment::removeRemovedObjects()
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 u16 id = i.getNode()->getKey();
1540 ServerActiveObject* obj = i.getNode()->getValue();
1541 // This shouldn't happen but check it
1544 infostream<<"NULL object found in ServerEnvironment"
1545 <<" while finding removed objects. id="<<id<<std::endl;
1546 // Id to be removed from m_active_objects
1547 objects_to_remove.push_back(id);
1552 We will delete objects that are marked as removed or thatare
1553 waiting for deletion after deactivation
1555 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1559 Delete static data from block if is marked as removed
1561 if(obj->m_static_exists && obj->m_removed)
1563 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1566 block->m_static_objects.remove(id);
1567 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1568 "removeRemovedObjects");
1569 obj->m_static_exists = false;
1573 // If m_known_by_count > 0, don't actually remove.
1574 if(obj->m_known_by_count > 0)
1577 // Deregister in scripting api
1578 scriptapi_rm_object_reference(m_lua, obj);
1582 // Id to be removed from m_active_objects
1583 objects_to_remove.push_back(id);
1585 // Remove references from m_active_objects
1586 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1587 i != objects_to_remove.end(); i++)
1589 m_active_objects.remove(*i);
1593 static void print_hexdump(std::ostream &o, const std::string &data)
1595 const int linelength = 16;
1596 for(int l=0; ; l++){
1597 int i0 = linelength * l;
1598 bool at_end = false;
1599 int thislinelength = linelength;
1600 if(i0 + thislinelength > (int)data.size()){
1601 thislinelength = data.size() - i0;
1604 for(int di=0; di<linelength; di++){
1607 if(di<thislinelength)
1608 snprintf(buf, 4, "%.2x ", data[i]);
1610 snprintf(buf, 4, " ");
1614 for(int di=0; di<thislinelength; di++){
1628 Convert stored objects from blocks near the players to active.
1630 void ServerEnvironment::activateObjects(MapBlock *block)
1634 // Ignore if no stored objects (to not set changed flag)
1635 if(block->m_static_objects.m_stored.size() == 0)
1637 verbosestream<<"ServerEnvironment::activateObjects(): "
1638 <<"activating objects of block "<<PP(block->getPos())
1639 <<" ("<<block->m_static_objects.m_stored.size()
1640 <<" objects)"<<std::endl;
1641 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1643 errorstream<<"suspiciously large amount of objects detected: "
1644 <<block->m_static_objects.m_stored.size()<<" in "
1645 <<PP(block->getPos())
1646 <<"; removing all of them."<<std::endl;
1647 // Clear stored list
1648 block->m_static_objects.m_stored.clear();
1649 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1650 "stored list cleared in activateObjects due to "
1651 "large amount of objects");
1654 // A list for objects that couldn't be converted to static for some
1655 // reason. They will be stored back.
1656 core::list<StaticObject> new_stored;
1657 // Loop through stored static objects
1658 for(core::list<StaticObject>::Iterator
1659 i = block->m_static_objects.m_stored.begin();
1660 i != block->m_static_objects.m_stored.end(); i++)
1662 /*infostream<<"Server: Creating an active object from "
1663 <<"static data"<<std::endl;*/
1664 StaticObject &s_obj = *i;
1665 // Create an active object from the data
1666 ServerActiveObject *obj = ServerActiveObject::create
1667 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1668 // If couldn't create object, store static data back.
1671 errorstream<<"ServerEnvironment::activateObjects(): "
1672 <<"failed to create active object from static object "
1673 <<"in block "<<PP(s_obj.pos/BS)
1674 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1675 print_hexdump(verbosestream, s_obj.data);
1677 new_stored.push_back(s_obj);
1680 verbosestream<<"ServerEnvironment::activateObjects(): "
1681 <<"activated static object pos="<<PP(s_obj.pos/BS)
1682 <<" type="<<(int)s_obj.type<<std::endl;
1683 // This will also add the object to the active static list
1684 addActiveObjectRaw(obj, false);
1686 // Clear stored list
1687 block->m_static_objects.m_stored.clear();
1688 // Add leftover failed stuff to stored list
1689 for(core::list<StaticObject>::Iterator
1690 i = new_stored.begin();
1691 i != new_stored.end(); i++)
1693 StaticObject &s_obj = *i;
1694 block->m_static_objects.m_stored.push_back(s_obj);
1697 Note: Block hasn't really been modified here.
1698 The objects have just been activated and moved from the stored
1699 static list to the active static list.
1700 As such, the block is essentially the same.
1701 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1702 Otherwise there would be a huge amount of unnecessary I/O.
1707 Convert objects that are not standing inside active blocks to static.
1709 If m_known_by_count != 0, active object is not deleted, but static
1710 data is still updated.
1712 If force_delete is set, active object is deleted nevertheless. It
1713 shall only be set so in the destructor of the environment.
1715 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1717 core::list<u16> objects_to_remove;
1718 for(core::map<u16, ServerActiveObject*>::Iterator
1719 i = m_active_objects.getIterator();
1720 i.atEnd()==false; i++)
1722 ServerActiveObject* obj = i.getNode()->getValue();
1724 // This shouldn't happen but check it
1727 errorstream<<"NULL object found in ServerEnvironment"
1733 // If pending deactivation, let removeRemovedObjects() do it
1734 if(obj->m_pending_deactivation)
1737 u16 id = i.getNode()->getKey();
1738 v3f objectpos = obj->getBasePosition();
1740 // The block in which the object resides in
1741 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1743 // If block is active, don't remove
1744 if(m_active_blocks.contains(blockpos_o))
1747 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1748 <<"deactivating object id="<<id<<" on inactive block "
1749 <<PP(blockpos_o)<<std::endl;
1751 // If known by some client, don't immediately delete.
1752 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1755 Update the static data
1758 // Create new static object
1759 std::string staticdata_new = obj->getStaticData();
1760 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1762 bool stays_in_same_block = false;
1763 bool data_changed = true;
1765 if(obj->m_static_exists){
1766 if(obj->m_static_block == blockpos_o)
1767 stays_in_same_block = true;
1769 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1771 core::map<u16, StaticObject>::Node *n =
1772 block->m_static_objects.m_active.find(id);
1774 StaticObject static_old = n->getValue();
1776 float save_movem = obj->getMinimumSavedMovement();
1778 if(static_old.data == staticdata_new &&
1779 (static_old.pos - objectpos).getLength() < save_movem)
1780 data_changed = false;
1782 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1783 <<"id="<<id<<" m_static_exists=true but "
1784 <<"static data doesn't actually exist in "
1785 <<PP(obj->m_static_block)<<std::endl;
1789 bool shall_be_written = (!stays_in_same_block || data_changed);
1791 // Delete old static object
1792 if(obj->m_static_exists)
1794 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1797 block->m_static_objects.remove(id);
1798 obj->m_static_exists = false;
1799 // Only mark block as modified if data changed considerably
1800 if(shall_be_written)
1801 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1802 "deactivateFarObjects: Static data "
1803 "changed considerably");
1807 // Add to the block where the object is located in
1808 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1809 // Get or generate the block
1810 MapBlock *block = m_map->emergeBlock(blockpos);
1814 if(block->m_static_objects.m_stored.size() >= 49){
1815 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1816 <<" statically but block "<<PP(blockpos)
1817 <<" already contains "
1818 <<block->m_static_objects.m_stored.size()
1819 <<" (over 49) objects."
1820 <<" Forcing delete."<<std::endl;
1821 force_delete = true;
1823 u16 new_id = pending_delete ? id : 0;
1824 block->m_static_objects.insert(new_id, s_obj);
1826 // Only mark block as modified if data changed considerably
1827 if(shall_be_written)
1828 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1829 "deactivateFarObjects: Static data "
1830 "changed considerably");
1832 obj->m_static_exists = true;
1833 obj->m_static_block = block->getPos();
1837 errorstream<<"ServerEnv: Could not find or generate "
1838 <<"a block for storing id="<<obj->getId()
1839 <<" statically"<<std::endl;
1844 If known by some client, set pending deactivation.
1845 Otherwise delete it immediately.
1850 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1851 <<"object id="<<id<<" is known by clients"
1852 <<"; not deleting yet"<<std::endl;
1854 obj->m_pending_deactivation = true;
1858 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1859 <<"object id="<<id<<" is not known by clients"
1860 <<"; deleting"<<std::endl;
1862 // Deregister in scripting api
1863 scriptapi_rm_object_reference(m_lua, obj);
1865 // Delete active object
1867 // Id to be removed from m_active_objects
1868 objects_to_remove.push_back(id);
1871 // Remove references from m_active_objects
1872 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1873 i != objects_to_remove.end(); i++)
1875 m_active_objects.remove(*i);
1886 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1887 ITextureSource *texturesource, IGameDef *gamedef):
1890 m_texturesource(texturesource),
1897 ClientEnvironment::~ClientEnvironment()
1899 // delete active objects
1900 for(core::map<u16, ClientActiveObject*>::Iterator
1901 i = m_active_objects.getIterator();
1902 i.atEnd()==false; i++)
1904 delete i.getNode()->getValue();
1911 void ClientEnvironment::addPlayer(Player *player)
1913 DSTACK(__FUNCTION_NAME);
1915 It is a failure if player is local and there already is a local
1918 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1920 Environment::addPlayer(player);
1923 LocalPlayer * ClientEnvironment::getLocalPlayer()
1925 for(core::list<Player*>::Iterator i = m_players.begin();
1926 i != m_players.end(); i++)
1928 Player *player = *i;
1929 if(player->isLocal())
1930 return (LocalPlayer*)player;
1935 void ClientEnvironment::step(float dtime)
1937 DSTACK(__FUNCTION_NAME);
1939 // Get some settings
1940 bool free_move = g_settings->getBool("free_move");
1941 bool footprints = g_settings->getBool("footprints");
1944 LocalPlayer *lplayer = getLocalPlayer();
1946 // collision info queue
1947 core::list<CollisionInfo> player_collisions;
1950 Get the speed the player is going
1952 bool is_climbing = lplayer->is_climbing;
1954 f32 player_speed = 0.001; // just some small value
1955 player_speed = lplayer->getSpeed().getLength();
1958 Maximum position increment
1960 //f32 position_max_increment = 0.05*BS;
1961 f32 position_max_increment = 0.1*BS;
1963 // Maximum time increment (for collision detection etc)
1964 // time = distance / speed
1965 f32 dtime_max_increment = position_max_increment / player_speed;
1967 // Maximum time increment is 10ms or lower
1968 if(dtime_max_increment > 0.01)
1969 dtime_max_increment = 0.01;
1971 // Don't allow overly huge dtime
1975 f32 dtime_downcount = dtime;
1978 Stuff that has a maximum time increment
1987 if(dtime_downcount > dtime_max_increment)
1989 dtime_part = dtime_max_increment;
1990 dtime_downcount -= dtime_part;
1994 dtime_part = dtime_downcount;
1996 Setting this to 0 (no -=dtime_part) disables an infinite loop
1997 when dtime_part is so small that dtime_downcount -= dtime_part
2000 dtime_downcount = 0;
2008 v3f lplayerpos = lplayer->getPosition();
2011 if(free_move == false && is_climbing == false)
2014 v3f speed = lplayer->getSpeed();
2015 if(lplayer->swimming_up == false)
2016 speed.Y -= 9.81 * BS * dtime_part * 2;
2019 if(lplayer->in_water_stable || lplayer->in_water)
2021 f32 max_down = 2.0*BS;
2022 if(speed.Y < -max_down) speed.Y = -max_down;
2025 if(speed.getLength() > max)
2027 speed = speed / speed.getLength() * max;
2031 lplayer->setSpeed(speed);
2036 This also does collision detection.
2038 lplayer->move(dtime_part, *m_map, position_max_increment,
2039 &player_collisions);
2042 while(dtime_downcount > 0.001);
2044 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2046 for(core::list<CollisionInfo>::Iterator
2047 i = player_collisions.begin();
2048 i != player_collisions.end(); i++)
2050 CollisionInfo &info = *i;
2051 if(info.t == COLLISION_FALL)
2053 //f32 tolerance = BS*10; // 2 without damage
2054 f32 tolerance = BS*12; // 3 without damage
2056 if(info.speed > tolerance)
2058 f32 damage_f = (info.speed - tolerance)/BS*factor;
2059 u16 damage = (u16)(damage_f+0.5);
2060 if(lplayer->hp > damage)
2061 lplayer->hp -= damage;
2065 ClientEnvEvent event;
2066 event.type = CEE_PLAYER_DAMAGE;
2067 event.player_damage.amount = damage;
2068 m_client_event_queue.push_back(event);
2074 A quick draft of lava damage
2076 if(m_lava_hurt_interval.step(dtime, 1.0))
2078 v3f pf = lplayer->getPosition();
2080 // Feet, middle and head
2081 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2082 MapNode n1 = m_map->getNodeNoEx(p1);
2083 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2084 MapNode n2 = m_map->getNodeNoEx(p2);
2085 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2086 MapNode n3 = m_map->getNodeNoEx(p2);
2088 u32 damage_per_second = 0;
2089 damage_per_second = MYMAX(damage_per_second,
2090 m_gamedef->ndef()->get(n1).damage_per_second);
2091 damage_per_second = MYMAX(damage_per_second,
2092 m_gamedef->ndef()->get(n2).damage_per_second);
2093 damage_per_second = MYMAX(damage_per_second,
2094 m_gamedef->ndef()->get(n3).damage_per_second);
2096 if(damage_per_second != 0)
2098 ClientEnvEvent event;
2099 event.type = CEE_PLAYER_DAMAGE;
2100 event.player_damage.amount = damage_per_second;
2101 m_client_event_queue.push_back(event);
2106 Stuff that can be done in an arbitarily large dtime
2108 for(core::list<Player*>::Iterator i = m_players.begin();
2109 i != m_players.end(); i++)
2111 Player *player = *i;
2112 v3f playerpos = player->getPosition();
2115 Handle non-local players
2117 if(player->isLocal() == false)
2120 player->move(dtime, *m_map, 100*BS);
2124 // Update lighting on all players on client
2125 u8 light = LIGHT_MAX;
2128 v3s16 p = player->getLightPosition();
2129 MapNode n = m_map->getNode(p);
2130 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2132 catch(InvalidPositionException &e) {}
2133 player->updateLight(light);
2136 Add footsteps to grass
2140 // Get node that is at BS/4 under player
2141 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2143 MapNode n = m_map->getNode(bottompos);
2144 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2146 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2147 m_map->setNode(bottompos, n);
2148 // Update mesh on client
2149 if(m_map->mapType() == MAPTYPE_CLIENT)
2151 v3s16 p_blocks = getNodeBlockPos(bottompos);
2152 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2153 //b->updateMesh(getDayNightRatio());
2154 b->setMeshExpired(true);
2158 catch(InvalidPositionException &e)
2165 Step active objects and update lighting of them
2168 for(core::map<u16, ClientActiveObject*>::Iterator
2169 i = m_active_objects.getIterator();
2170 i.atEnd()==false; i++)
2172 ClientActiveObject* obj = i.getNode()->getValue();
2174 obj->step(dtime, this);
2176 if(m_active_object_light_update_interval.step(dtime, 0.21))
2179 //u8 light = LIGHT_MAX;
2183 v3s16 p = obj->getLightPosition();
2184 MapNode n = m_map->getNode(p);
2185 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2187 catch(InvalidPositionException &e) {}
2188 obj->updateLight(light);
2193 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2195 m_map->updateMeshes(blockpos, getDayNightRatio());
2198 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2200 m_map->expireMeshes(only_daynight_diffed);
2203 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2205 core::map<u16, ClientActiveObject*>::Node *n;
2206 n = m_active_objects.find(id);
2209 return n->getValue();
2212 bool isFreeClientActiveObjectId(u16 id,
2213 core::map<u16, ClientActiveObject*> &objects)
2218 for(core::map<u16, ClientActiveObject*>::Iterator
2219 i = objects.getIterator();
2220 i.atEnd()==false; i++)
2222 if(i.getNode()->getKey() == id)
2228 u16 getFreeClientActiveObjectId(
2229 core::map<u16, ClientActiveObject*> &objects)
2234 if(isFreeClientActiveObjectId(new_id, objects))
2244 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2247 if(object->getId() == 0)
2249 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2252 infostream<<"ClientEnvironment::addActiveObject(): "
2253 <<"no free ids available"<<std::endl;
2257 object->setId(new_id);
2259 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2261 infostream<<"ClientEnvironment::addActiveObject(): "
2262 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2266 infostream<<"ClientEnvironment::addActiveObject(): "
2267 <<"added (id="<<object->getId()<<")"<<std::endl;
2268 m_active_objects.insert(object->getId(), object);
2269 // TODO: Make g_texturesource non-global
2270 object->addToScene(m_smgr, m_texturesource);
2271 return object->getId();
2274 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2275 const std::string &init_data)
2277 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2280 infostream<<"ClientEnvironment::addActiveObject(): "
2281 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2288 obj->initialize(init_data);
2290 addActiveObject(obj);
2293 void ClientEnvironment::removeActiveObject(u16 id)
2295 infostream<<"ClientEnvironment::removeActiveObject(): "
2296 <<"id="<<id<<std::endl;
2297 ClientActiveObject* obj = getActiveObject(id);
2300 infostream<<"ClientEnvironment::removeActiveObject(): "
2301 <<"id="<<id<<" not found"<<std::endl;
2304 obj->removeFromScene();
2306 m_active_objects.remove(id);
2309 void ClientEnvironment::processActiveObjectMessage(u16 id,
2310 const std::string &data)
2312 ClientActiveObject* obj = getActiveObject(id);
2315 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2316 <<" got message for id="<<id<<", which doesn't exist."
2320 obj->processMessage(data);
2324 Callbacks for activeobjects
2327 void ClientEnvironment::damageLocalPlayer(u8 damage)
2329 LocalPlayer *lplayer = getLocalPlayer();
2332 if(lplayer->hp > damage)
2333 lplayer->hp -= damage;
2337 ClientEnvEvent event;
2338 event.type = CEE_PLAYER_DAMAGE;
2339 event.player_damage.amount = damage;
2340 m_client_event_queue.push_back(event);
2344 Client likes to call these
2347 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2348 core::array<DistanceSortedActiveObject> &dest)
2350 for(core::map<u16, ClientActiveObject*>::Iterator
2351 i = m_active_objects.getIterator();
2352 i.atEnd()==false; i++)
2354 ClientActiveObject* obj = i.getNode()->getValue();
2356 f32 d = (obj->getPosition() - origin).getLength();
2361 DistanceSortedActiveObject dso(obj, d);
2363 dest.push_back(dso);
2367 ClientEnvEvent ClientEnvironment::getClientEvent()
2369 if(m_client_event_queue.size() == 0)
2371 ClientEnvEvent event;
2372 event.type = CEE_NONE;
2375 return m_client_event_queue.pop_front();
2378 #endif // #ifndef SERVER