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->setChangedFlag();
591 last_node_walkable = false;
597 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
599 // Get time difference
601 u32 stamp = block->getTimestamp();
602 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
603 dtime_s = m_game_time - block->getTimestamp();
604 dtime_s += additional_dtime;
606 // Set current time as timestamp (and let it set ChangedFlag)
607 block->setTimestamp(m_game_time);
609 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
611 // Activate stored objects
612 activateObjects(block);
615 bool changed = block->m_node_metadata->step((float)dtime_s);
619 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
620 event.p = block->getPos();
621 m_map->dispatchEvent(&event);
623 block->setChangedFlag();
626 // TODO: Do something
627 // TODO: Implement usage of ActiveBlockModifier
629 // Here's a quick demonstration
631 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
632 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
633 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
635 v3s16 p = p0 + block->getPosRelative();
636 MapNode n = block->getNodeNoEx(p0);
639 // Convert all mud under proper day lighting to grass
640 if(n.getContent() == CONTENT_MUD)
644 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
645 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
646 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
648 n.setContent(CONTENT_GRASS);
649 m_map->addNodeWithEvent(p, n);
657 void ServerEnvironment::clearAllObjects()
659 infostream<<"ServerEnvironment::clearAllObjects(): "
660 <<"Removing all active objects"<<std::endl;
661 core::list<u16> objects_to_remove;
662 for(core::map<u16, ServerActiveObject*>::Iterator
663 i = m_active_objects.getIterator();
664 i.atEnd()==false; i++)
666 ServerActiveObject* obj = i.getNode()->getValue();
667 u16 id = i.getNode()->getKey();
668 v3f objectpos = obj->getBasePosition();
669 // Delete static object if block is loaded
670 if(obj->m_static_exists){
671 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
673 block->m_static_objects.remove(id);
674 block->raiseModified(MOD_STATE_WRITE_NEEDED);
675 obj->m_static_exists = false;
678 // If known by some client, don't delete immediately
679 if(obj->m_known_by_count > 0){
680 obj->m_pending_deactivation = true;
681 obj->m_removed = true;
684 // Deregister in scripting api
685 scriptapi_rm_object_reference(m_lua, obj);
686 // Delete active object
688 // Id to be removed from m_active_objects
689 objects_to_remove.push_back(id);
691 // Remove references from m_active_objects
692 for(core::list<u16>::Iterator i = objects_to_remove.begin();
693 i != objects_to_remove.end(); i++)
695 m_active_objects.remove(*i);
698 core::list<v3s16> loadable_blocks;
699 infostream<<"ServerEnvironment::clearAllObjects(): "
700 <<"Listing all loadable blocks"<<std::endl;
701 m_map->listAllLoadableBlocks(loadable_blocks);
702 infostream<<"ServerEnvironment::clearAllObjects(): "
703 <<"Done listing all loadable blocks: "
704 <<loadable_blocks.size()
705 <<", now clearing"<<std::endl;
706 u32 report_interval = loadable_blocks.size() / 10;
707 u32 num_blocks_checked = 0;
708 u32 num_blocks_cleared = 0;
709 u32 num_objs_cleared = 0;
710 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
711 i != loadable_blocks.end(); i++)
714 MapBlock *block = m_map->emergeBlock(p, false);
716 errorstream<<"ServerEnvironment::clearAllObjects(): "
717 <<"Failed to emerge block "<<PP(p)<<std::endl;
720 u32 num_stored = block->m_static_objects.m_stored.size();
721 u32 num_active = block->m_static_objects.m_active.size();
722 if(num_stored != 0 || num_active != 0){
723 block->m_static_objects.m_stored.clear();
724 block->m_static_objects.m_active.clear();
725 block->raiseModified(MOD_STATE_WRITE_NEEDED);
726 num_objs_cleared += num_stored + num_active;
727 num_blocks_cleared++;
729 num_blocks_checked++;
731 if(num_blocks_checked % report_interval == 0){
732 float percent = 100.0 * (float)num_blocks_checked /
733 loadable_blocks.size();
734 infostream<<"ServerEnvironment::clearAllObjects(): "
735 <<"Cleared "<<num_objs_cleared<<" objects"
736 <<" in "<<num_blocks_cleared<<" blocks ("
737 <<percent<<"%)"<<std::endl;
740 infostream<<"ServerEnvironment::clearAllObjects(): "
741 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
742 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
745 static void getMob_dungeon_master(Settings &properties)
747 properties.set("looks", "dungeon_master");
748 properties.setFloat("yaw", 1.57);
749 properties.setFloat("hp", 30);
750 properties.setBool("bright_shooting", true);
751 properties.set("shoot_type", "fireball");
752 properties.set("shoot_y", "0.7");
753 properties.set("player_hit_damage", "1");
754 properties.set("player_hit_distance", "1.0");
755 properties.set("player_hit_interval", "0.5");
756 properties.setBool("mindless_rage", myrand_range(0,100)==0);
759 void ServerEnvironment::step(float dtime)
761 DSTACK(__FUNCTION_NAME);
763 //TimeTaker timer("ServerEnv step");
766 bool footprints = g_settings->getBool("footprints");
772 m_game_time_fraction_counter += dtime;
773 u32 inc_i = (u32)m_game_time_fraction_counter;
774 m_game_time += inc_i;
775 m_game_time_fraction_counter -= (float)inc_i;
782 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
783 for(core::list<Player*>::Iterator i = m_players.begin();
784 i != m_players.end(); i++)
788 // Ignore disconnected players
789 if(player->peer_id == 0)
792 v3f playerpos = player->getPosition();
795 player->move(dtime, *m_map, 100*BS);
798 Add footsteps to grass
802 // Get node that is at BS/4 under player
803 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
805 MapNode n = m_map->getNode(bottompos);
806 if(n.getContent() == CONTENT_GRASS)
808 n.setContent(CONTENT_GRASS_FOOTSTEPS);
809 m_map->setNode(bottompos, n);
812 catch(InvalidPositionException &e)
820 Manage active block list
822 if(m_active_blocks_management_interval.step(dtime, 2.0))
824 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
826 Get player block positions
828 core::list<v3s16> players_blockpos;
829 for(core::list<Player*>::Iterator
830 i = m_players.begin();
831 i != m_players.end(); i++)
834 // Ignore disconnected players
835 if(player->peer_id == 0)
837 v3s16 blockpos = getNodeBlockPos(
838 floatToInt(player->getPosition(), BS));
839 players_blockpos.push_back(blockpos);
843 Update list of active blocks, collecting changes
845 const s16 active_block_range = g_settings->getS16("active_block_range");
846 core::map<v3s16, bool> blocks_removed;
847 core::map<v3s16, bool> blocks_added;
848 m_active_blocks.update(players_blockpos, active_block_range,
849 blocks_removed, blocks_added);
852 Handle removed blocks
855 // Convert active objects that are no more in active blocks to static
856 deactivateFarObjects(false);
858 for(core::map<v3s16, bool>::Iterator
859 i = blocks_removed.getIterator();
860 i.atEnd()==false; i++)
862 v3s16 p = i.getNode()->getKey();
864 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
865 <<") became inactive"<<std::endl;*/
867 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
871 // Set current time as timestamp (and let it set ChangedFlag)
872 block->setTimestamp(m_game_time);
879 for(core::map<v3s16, bool>::Iterator
880 i = blocks_added.getIterator();
881 i.atEnd()==false; i++)
883 v3s16 p = i.getNode()->getKey();
885 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
886 <<") became active"<<std::endl;*/
888 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
892 activateBlock(block);
897 Mess around in active blocks
899 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
901 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
905 for(core::map<v3s16, bool>::Iterator
906 i = m_active_blocks.m_list.getIterator();
907 i.atEnd()==false; i++)
909 v3s16 p = i.getNode()->getKey();
911 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
912 <<") being handled"<<std::endl;*/
914 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
918 // Reset block usage timer
919 block->resetUsageTimer();
921 // Set current time as timestamp
922 block->setTimestampNoChangedFlag(m_game_time);
925 bool changed = block->m_node_metadata->step(dtime);
929 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
931 m_map->dispatchEvent(&event);
933 block->setChangedFlag();
938 if(m_active_blocks_test_interval.step(dtime, 10.0))
940 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
941 //float dtime = 10.0;
943 for(core::map<v3s16, bool>::Iterator
944 i = m_active_blocks.m_list.getIterator();
945 i.atEnd()==false; i++)
947 v3s16 p = i.getNode()->getKey();
949 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
950 <<") being handled"<<std::endl;*/
952 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
956 // Set current time as timestamp
957 block->setTimestampNoChangedFlag(m_game_time);
962 Note that map modifications should be done using the event-
963 making map methods so that the server gets information
966 Reading can be done quickly directly from the block.
968 Everything should bind to inside this single content
969 searching loop to keep things fast.
971 // TODO: Implement usage of ActiveBlockModifier
973 // Find out how many objects the block contains
974 //u32 active_object_count = block->m_static_objects.m_active.size();
975 // Find out how many objects this and all the neighbors contain
976 u32 active_object_count_wider = 0;
977 for(s16 x=-1; x<=1; x++)
978 for(s16 y=-1; y<=1; y++)
979 for(s16 z=-1; z<=1; z++)
981 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
984 active_object_count_wider +=
985 block->m_static_objects.m_active.size()
986 + block->m_static_objects.m_stored.size();
988 /*if(block->m_static_objects.m_stored.size() != 0){
989 errorstream<<"ServerEnvironment::step(): "
990 <<PP(block->getPos())<<" contains "
991 <<block->m_static_objects.m_stored.size()
992 <<" stored objects; "
993 <<"when spawning objects, when counting active "
994 <<"objects in wide area. relative position: "
995 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1000 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1001 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1002 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1004 v3s16 p = p0 + block->getPosRelative();
1005 MapNode n = block->getNodeNoEx(p0);
1009 Convert mud under proper lighting to grass
1011 if(n.getContent() == CONTENT_MUD)
1013 if(myrand()%20 == 0)
1015 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1016 if(m_gamedef->ndef()->get(n_top).air_equivalent &&
1017 n_top.getLightBlend(getDayNightRatio(),
1018 m_gamedef->ndef()) >= 13)
1020 n.setContent(CONTENT_GRASS);
1021 m_map->addNodeWithEvent(p, n);
1026 Convert grass into mud if under something else than air
1028 if(n.getContent() == CONTENT_GRASS)
1030 //if(myrand()%20 == 0)
1032 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1033 if(m_gamedef->ndef()->get(n_top).air_equivalent == false)
1035 n.setContent(CONTENT_MUD);
1036 m_map->addNodeWithEvent(p, n);
1041 Rats spawn around regular trees
1043 if(n.getContent() == CONTENT_TREE ||
1044 n.getContent() == CONTENT_JUNGLETREE)
1046 if(myrand()%200 == 0 && active_object_count_wider == 0)
1048 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1049 0, myrand_range(-2, 2));
1050 MapNode n1 = m_map->getNodeNoEx(p1);
1051 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1052 if(n1b.getContent() == CONTENT_GRASS &&
1053 n1.getContent() == CONTENT_AIR)
1055 v3f pos = intToFloat(p1, BS);
1056 ServerActiveObject *obj = new RatSAO(this, pos);
1057 addActiveObject(obj);
1062 Fun things spawn in caves and dungeons
1064 if(n.getContent() == CONTENT_STONE ||
1065 n.getContent() == CONTENT_MOSSYCOBBLE)
1067 if(myrand()%200 == 0 && active_object_count_wider == 0)
1069 v3s16 p1 = p + v3s16(0,1,0);
1070 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1071 if(n1a.getLightBlend(getDayNightRatio(),
1072 m_gamedef->ndef()) <= 3){
1073 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1074 if(n1a.getContent() == CONTENT_AIR &&
1075 n1b.getContent() == CONTENT_AIR)
1077 v3f pos = intToFloat(p1, BS);
1079 if(i == 0 || i == 1){
1080 actionstream<<"A dungeon master spawns at "
1081 <<PP(p1)<<std::endl;
1082 Settings properties;
1083 getMob_dungeon_master(properties);
1084 ServerActiveObject *obj = new MobV2SAO(
1085 this, pos, &properties);
1086 addActiveObject(obj);
1087 } else if(i == 2 || i == 3){
1088 actionstream<<"Rats spawn at "
1089 <<PP(p1)<<std::endl;
1090 for(int j=0; j<3; j++){
1091 ServerActiveObject *obj = new RatSAO(
1093 addActiveObject(obj);
1096 actionstream<<"An oerkki spawns at "
1097 <<PP(p1)<<std::endl;
1098 ServerActiveObject *obj = new Oerkki1SAO(
1100 addActiveObject(obj);
1107 Make trees from saplings!
1109 if(n.getContent() == CONTENT_SAPLING)
1111 if(myrand()%50 == 0)
1113 actionstream<<"A sapling grows into a tree at "
1116 core::map<v3s16, MapBlock*> modified_blocks;
1118 ManualMapVoxelManipulator vmanip(m_map);
1119 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1120 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1121 bool is_apple_tree = myrand()%4 == 0;
1122 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1123 vmanip.blitBackAll(&modified_blocks);
1126 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1127 for(core::map<v3s16, MapBlock*>::Iterator
1128 i = modified_blocks.getIterator();
1129 i.atEnd() == false; i++)
1131 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1133 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1135 // Send a MEET_OTHER event
1137 event.type = MEET_OTHER;
1138 for(core::map<v3s16, MapBlock*>::Iterator
1139 i = modified_blocks.getIterator();
1140 i.atEnd() == false; i++)
1142 v3s16 p = i.getNode()->getKey();
1143 event.modified_blocks.insert(p, true);
1145 m_map->dispatchEvent(&event);
1153 Step script environment (run global on_step())
1155 scriptapi_environment_step(m_lua, dtime);
1161 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1162 //TimeTaker timer("Step active objects");
1164 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1166 // This helps the objects to send data at the same time
1167 bool send_recommended = false;
1168 m_send_recommended_timer += dtime;
1169 if(m_send_recommended_timer > getSendRecommendedInterval())
1171 m_send_recommended_timer -= getSendRecommendedInterval();
1172 send_recommended = true;
1175 for(core::map<u16, ServerActiveObject*>::Iterator
1176 i = m_active_objects.getIterator();
1177 i.atEnd()==false; i++)
1179 ServerActiveObject* obj = i.getNode()->getValue();
1180 // Remove non-peaceful mobs on peaceful mode
1181 if(g_settings->getBool("only_peaceful_mobs")){
1182 if(!obj->isPeaceful())
1183 obj->m_removed = true;
1185 // Don't step if is to be removed or stored statically
1186 if(obj->m_removed || obj->m_pending_deactivation)
1189 obj->step(dtime, send_recommended);
1190 // Read messages from object
1191 while(obj->m_messages_out.size() > 0)
1193 m_active_object_messages.push_back(
1194 obj->m_messages_out.pop_front());
1200 Manage active objects
1202 if(m_object_management_interval.step(dtime, 0.5))
1204 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1206 Remove objects that satisfy (m_removed && m_known_by_count==0)
1208 removeRemovedObjects();
1211 if(g_settings->getBool("enable_experimental"))
1218 m_random_spawn_timer -= dtime;
1219 if(m_random_spawn_timer < 0)
1221 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1222 //m_random_spawn_timer += 2.0;
1223 m_random_spawn_timer += 200.0;
1229 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1230 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1231 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1233 Player *player = getRandomConnectedPlayer();
1236 pos = player->getPosition();
1238 myrand_range(-3,3)*BS,
1240 myrand_range(-3,3)*BS
1244 Create a ServerActiveObject
1247 //TestSAO *obj = new TestSAO(this, pos);
1248 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1249 //ServerActiveObject *obj = new RatSAO(this, pos);
1250 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1251 //ServerActiveObject *obj = new FireflySAO(this, pos);
1253 infostream<<"Server: Spawning MobV2SAO at "
1254 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1256 Settings properties;
1257 getMob_dungeon_master(properties);
1258 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1259 addActiveObject(obj);
1263 } // enable_experimental
1266 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1268 core::map<u16, ServerActiveObject*>::Node *n;
1269 n = m_active_objects.find(id);
1272 return n->getValue();
1275 bool isFreeServerActiveObjectId(u16 id,
1276 core::map<u16, ServerActiveObject*> &objects)
1281 for(core::map<u16, ServerActiveObject*>::Iterator
1282 i = objects.getIterator();
1283 i.atEnd()==false; i++)
1285 if(i.getNode()->getKey() == id)
1291 u16 getFreeServerActiveObjectId(
1292 core::map<u16, ServerActiveObject*> &objects)
1297 if(isFreeServerActiveObjectId(new_id, objects))
1307 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1310 u16 id = addActiveObjectRaw(object, true);
1314 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1318 v3f objectpos = obj->getBasePosition();
1320 // The block in which the object resides in
1321 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1324 Update the static data
1327 // Create new static object
1328 std::string staticdata = obj->getStaticData();
1329 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1330 // Add to the block where the object is located in
1331 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1332 // Get or generate the block
1333 MapBlock *block = m_map->emergeBlock(blockpos);
1335 bool succeeded = false;
1339 block->m_static_objects.insert(0, s_obj);
1340 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1344 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1345 <<"Could not find or generate "
1346 <<"a block for storing static object"<<std::endl;
1356 Finds out what new objects have been added to
1357 inside a radius around a position
1359 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1360 core::map<u16, bool> ¤t_objects,
1361 core::map<u16, bool> &added_objects)
1363 v3f pos_f = intToFloat(pos, BS);
1364 f32 radius_f = radius * BS;
1366 Go through the object list,
1367 - discard m_removed objects,
1368 - discard objects that are too far away,
1369 - discard objects that are found in current_objects.
1370 - add remaining objects to added_objects
1372 for(core::map<u16, ServerActiveObject*>::Iterator
1373 i = m_active_objects.getIterator();
1374 i.atEnd()==false; i++)
1376 u16 id = i.getNode()->getKey();
1378 ServerActiveObject *object = i.getNode()->getValue();
1381 // Discard if removed
1382 if(object->m_removed)
1384 // Discard if too far
1385 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1386 if(distance_f > radius_f)
1388 // Discard if already on current_objects
1389 core::map<u16, bool>::Node *n;
1390 n = current_objects.find(id);
1393 // Add to added_objects
1394 added_objects.insert(id, false);
1399 Finds out what objects have been removed from
1400 inside a radius around a position
1402 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1403 core::map<u16, bool> ¤t_objects,
1404 core::map<u16, bool> &removed_objects)
1406 v3f pos_f = intToFloat(pos, BS);
1407 f32 radius_f = radius * BS;
1409 Go through current_objects; object is removed if:
1410 - object is not found in m_active_objects (this is actually an
1411 error condition; objects should be set m_removed=true and removed
1412 only after all clients have been informed about removal), or
1413 - object has m_removed=true, or
1414 - object is too far away
1416 for(core::map<u16, bool>::Iterator
1417 i = current_objects.getIterator();
1418 i.atEnd()==false; i++)
1420 u16 id = i.getNode()->getKey();
1421 ServerActiveObject *object = getActiveObject(id);
1424 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1425 <<" object in current_objects is NULL"<<std::endl;
1427 else if(object->m_removed == false)
1429 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1430 /*infostream<<"removed == false"
1431 <<"distance_f = "<<distance_f
1432 <<", radius_f = "<<radius_f<<std::endl;*/
1433 if(distance_f < radius_f)
1439 removed_objects.insert(id, false);
1443 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1445 if(m_active_object_messages.size() == 0)
1446 return ActiveObjectMessage(0);
1448 return m_active_object_messages.pop_front();
1452 ************ Private methods *************
1455 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1459 if(object->getId() == 0){
1460 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1463 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1464 <<"no free ids available"<<std::endl;
1468 object->setId(new_id);
1471 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1472 <<"supplied with id "<<object->getId()<<std::endl;
1474 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1476 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1481 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1482 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1484 m_active_objects.insert(object->getId(), object);
1486 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1487 <<"Added id="<<object->getId()<<"; there are now "
1488 <<m_active_objects.size()<<" active objects."
1491 // Add static object to active static list of the block
1492 v3f objectpos = object->getBasePosition();
1493 std::string staticdata = object->getStaticData();
1494 StaticObject s_obj(object->getType(), objectpos, staticdata);
1495 // Add to the block where the object is located in
1496 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1497 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1500 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1501 object->m_static_exists = true;
1502 object->m_static_block = blockpos;
1505 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1508 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1509 <<"could not find block for storing id="<<object->getId()
1510 <<" statically"<<std::endl;
1513 // Register reference in scripting api (must be done before post-init)
1514 scriptapi_add_object_reference(m_lua, object);
1515 // Post-initialize object
1516 object->addedToEnvironment();
1518 return object->getId();
1522 Remove objects that satisfy (m_removed && m_known_by_count==0)
1524 void ServerEnvironment::removeRemovedObjects()
1526 core::list<u16> objects_to_remove;
1527 for(core::map<u16, ServerActiveObject*>::Iterator
1528 i = m_active_objects.getIterator();
1529 i.atEnd()==false; i++)
1531 u16 id = i.getNode()->getKey();
1532 ServerActiveObject* obj = i.getNode()->getValue();
1533 // This shouldn't happen but check it
1536 infostream<<"NULL object found in ServerEnvironment"
1537 <<" while finding removed objects. id="<<id<<std::endl;
1538 // Id to be removed from m_active_objects
1539 objects_to_remove.push_back(id);
1544 We will delete objects that are marked as removed or thatare
1545 waiting for deletion after deactivation
1547 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1551 Delete static data from block if is marked as removed
1553 if(obj->m_static_exists && obj->m_removed)
1555 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1558 block->m_static_objects.remove(id);
1559 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1560 obj->m_static_exists = false;
1564 // If m_known_by_count > 0, don't actually remove.
1565 if(obj->m_known_by_count > 0)
1568 // Deregister in scripting api
1569 scriptapi_rm_object_reference(m_lua, obj);
1573 // Id to be removed from m_active_objects
1574 objects_to_remove.push_back(id);
1576 // Remove references from m_active_objects
1577 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1578 i != objects_to_remove.end(); i++)
1580 m_active_objects.remove(*i);
1584 static void print_hexdump(std::ostream &o, const std::string &data)
1586 const int linelength = 16;
1587 for(int l=0; ; l++){
1588 int i0 = linelength * l;
1589 bool at_end = false;
1590 int thislinelength = linelength;
1591 if(i0 + thislinelength > (int)data.size()){
1592 thislinelength = data.size() - i0;
1595 for(int di=0; di<linelength; di++){
1598 if(di<thislinelength)
1599 snprintf(buf, 4, "%.2x ", data[i]);
1601 snprintf(buf, 4, " ");
1605 for(int di=0; di<thislinelength; di++){
1619 Convert stored objects from blocks near the players to active.
1621 void ServerEnvironment::activateObjects(MapBlock *block)
1625 // Ignore if no stored objects (to not set changed flag)
1626 if(block->m_static_objects.m_stored.size() == 0)
1628 verbosestream<<"ServerEnvironment::activateObjects(): "
1629 <<"activating objects of block "<<PP(block->getPos())
1630 <<" ("<<block->m_static_objects.m_stored.size()
1631 <<" objects)"<<std::endl;
1632 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1634 errorstream<<"suspiciously large amount of objects detected: "
1635 <<block->m_static_objects.m_stored.size()<<" in "
1636 <<PP(block->getPos())
1637 <<"; removing all of them."<<std::endl;
1638 // Clear stored list
1639 block->m_static_objects.m_stored.clear();
1640 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1643 // A list for objects that couldn't be converted to static for some
1644 // reason. They will be stored back.
1645 core::list<StaticObject> new_stored;
1646 // Loop through stored static objects
1647 for(core::list<StaticObject>::Iterator
1648 i = block->m_static_objects.m_stored.begin();
1649 i != block->m_static_objects.m_stored.end(); i++)
1651 /*infostream<<"Server: Creating an active object from "
1652 <<"static data"<<std::endl;*/
1653 StaticObject &s_obj = *i;
1654 // Create an active object from the data
1655 ServerActiveObject *obj = ServerActiveObject::create
1656 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1657 // If couldn't create object, store static data back.
1660 errorstream<<"ServerEnvironment::activateObjects(): "
1661 <<"failed to create active object from static object "
1662 <<"in block "<<PP(s_obj.pos/BS)
1663 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1664 print_hexdump(verbosestream, s_obj.data);
1666 new_stored.push_back(s_obj);
1669 verbosestream<<"ServerEnvironment::activateObjects(): "
1670 <<"activated static object pos="<<PP(s_obj.pos/BS)
1671 <<" type="<<(int)s_obj.type<<std::endl;
1672 // This will also add the object to the active static list
1673 addActiveObjectRaw(obj, false);
1675 // Clear stored list
1676 block->m_static_objects.m_stored.clear();
1677 // Add leftover failed stuff to stored list
1678 for(core::list<StaticObject>::Iterator
1679 i = new_stored.begin();
1680 i != new_stored.end(); i++)
1682 StaticObject &s_obj = *i;
1683 block->m_static_objects.m_stored.push_back(s_obj);
1686 Note: Block hasn't really been modified here.
1687 The objects have just been activated and moved from the stored
1688 static list to the active static list.
1689 As such, the block is essentially the same.
1690 Thus, do not call block->setChangedFlag().
1691 Otherwise there would be a huge amount of unnecessary I/O.
1696 Convert objects that are not standing inside active blocks to static.
1698 If m_known_by_count != 0, active object is not deleted, but static
1699 data is still updated.
1701 If force_delete is set, active object is deleted nevertheless. It
1702 shall only be set so in the destructor of the environment.
1704 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1706 core::list<u16> objects_to_remove;
1707 for(core::map<u16, ServerActiveObject*>::Iterator
1708 i = m_active_objects.getIterator();
1709 i.atEnd()==false; i++)
1711 ServerActiveObject* obj = i.getNode()->getValue();
1713 // This shouldn't happen but check it
1716 errorstream<<"NULL object found in ServerEnvironment"
1722 // If pending deactivation, let removeRemovedObjects() do it
1723 if(obj->m_pending_deactivation)
1726 u16 id = i.getNode()->getKey();
1727 v3f objectpos = obj->getBasePosition();
1729 // The block in which the object resides in
1730 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1732 // If block is active, don't remove
1733 if(m_active_blocks.contains(blockpos_o))
1736 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1737 <<"deactivating object id="<<id<<" on inactive block "
1738 <<PP(blockpos_o)<<std::endl;
1740 // If known by some client, don't immediately delete.
1741 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1744 Update the static data
1747 // Create new static object
1748 std::string staticdata_new = obj->getStaticData();
1749 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1751 bool stays_in_same_block = false;
1752 bool data_changed = true;
1754 if(obj->m_static_exists){
1755 if(obj->m_static_block == blockpos_o)
1756 stays_in_same_block = true;
1758 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1760 core::map<u16, StaticObject>::Node *n =
1761 block->m_static_objects.m_active.find(id);
1763 StaticObject static_old = n->getValue();
1765 float save_movem = obj->getMinimumSavedMovement();
1767 if(static_old.data == staticdata_new &&
1768 (static_old.pos - objectpos).getLength() < save_movem)
1769 data_changed = false;
1771 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1772 <<"id="<<id<<" m_static_exists=true but "
1773 <<"static data doesn't actually exist in "
1774 <<PP(obj->m_static_block)<<std::endl;
1778 bool shall_be_written = (!stays_in_same_block || data_changed);
1780 // Delete old static object
1781 if(obj->m_static_exists)
1783 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1786 block->m_static_objects.remove(id);
1787 obj->m_static_exists = false;
1788 // Only mark block as modified if data changed considerably
1789 if(shall_be_written)
1790 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1794 // Add to the block where the object is located in
1795 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1796 // Get or generate the block
1797 MapBlock *block = m_map->emergeBlock(blockpos);
1801 if(block->m_static_objects.m_stored.size() >= 49){
1802 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1803 <<" statically but block "<<PP(blockpos)
1804 <<" already contains "
1805 <<block->m_static_objects.m_stored.size()
1806 <<" (over 49) objects."
1807 <<" Forcing delete."<<std::endl;
1808 force_delete = true;
1810 u16 new_id = pending_delete ? id : 0;
1811 block->m_static_objects.insert(new_id, s_obj);
1813 // Only mark block as modified if data changed considerably
1814 if(shall_be_written)
1815 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1817 obj->m_static_exists = true;
1818 obj->m_static_block = block->getPos();
1822 errorstream<<"ServerEnv: Could not find or generate "
1823 <<"a block for storing id="<<obj->getId()
1824 <<" statically"<<std::endl;
1829 If known by some client, set pending deactivation.
1830 Otherwise delete it immediately.
1835 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1836 <<"object id="<<id<<" is known by clients"
1837 <<"; not deleting yet"<<std::endl;
1839 obj->m_pending_deactivation = true;
1843 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1844 <<"object id="<<id<<" is not known by clients"
1845 <<"; deleting"<<std::endl;
1847 // Deregister in scripting api
1848 scriptapi_rm_object_reference(m_lua, obj);
1850 // Delete active object
1852 // Id to be removed from m_active_objects
1853 objects_to_remove.push_back(id);
1856 // Remove references from m_active_objects
1857 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1858 i != objects_to_remove.end(); i++)
1860 m_active_objects.remove(*i);
1871 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1872 ITextureSource *texturesource, IGameDef *gamedef):
1875 m_texturesource(texturesource),
1882 ClientEnvironment::~ClientEnvironment()
1884 // delete active objects
1885 for(core::map<u16, ClientActiveObject*>::Iterator
1886 i = m_active_objects.getIterator();
1887 i.atEnd()==false; i++)
1889 delete i.getNode()->getValue();
1896 void ClientEnvironment::addPlayer(Player *player)
1898 DSTACK(__FUNCTION_NAME);
1900 It is a failure if player is local and there already is a local
1903 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1905 Environment::addPlayer(player);
1908 LocalPlayer * ClientEnvironment::getLocalPlayer()
1910 for(core::list<Player*>::Iterator i = m_players.begin();
1911 i != m_players.end(); i++)
1913 Player *player = *i;
1914 if(player->isLocal())
1915 return (LocalPlayer*)player;
1920 void ClientEnvironment::step(float dtime)
1922 DSTACK(__FUNCTION_NAME);
1924 // Get some settings
1925 bool free_move = g_settings->getBool("free_move");
1926 bool footprints = g_settings->getBool("footprints");
1929 LocalPlayer *lplayer = getLocalPlayer();
1931 // collision info queue
1932 core::list<CollisionInfo> player_collisions;
1935 Get the speed the player is going
1937 bool is_climbing = lplayer->is_climbing;
1939 f32 player_speed = 0.001; // just some small value
1940 player_speed = lplayer->getSpeed().getLength();
1943 Maximum position increment
1945 //f32 position_max_increment = 0.05*BS;
1946 f32 position_max_increment = 0.1*BS;
1948 // Maximum time increment (for collision detection etc)
1949 // time = distance / speed
1950 f32 dtime_max_increment = position_max_increment / player_speed;
1952 // Maximum time increment is 10ms or lower
1953 if(dtime_max_increment > 0.01)
1954 dtime_max_increment = 0.01;
1956 // Don't allow overly huge dtime
1960 f32 dtime_downcount = dtime;
1963 Stuff that has a maximum time increment
1972 if(dtime_downcount > dtime_max_increment)
1974 dtime_part = dtime_max_increment;
1975 dtime_downcount -= dtime_part;
1979 dtime_part = dtime_downcount;
1981 Setting this to 0 (no -=dtime_part) disables an infinite loop
1982 when dtime_part is so small that dtime_downcount -= dtime_part
1985 dtime_downcount = 0;
1993 v3f lplayerpos = lplayer->getPosition();
1996 if(free_move == false && is_climbing == false)
1999 v3f speed = lplayer->getSpeed();
2000 if(lplayer->swimming_up == false)
2001 speed.Y -= 9.81 * BS * dtime_part * 2;
2004 if(lplayer->in_water_stable || lplayer->in_water)
2006 f32 max_down = 2.0*BS;
2007 if(speed.Y < -max_down) speed.Y = -max_down;
2010 if(speed.getLength() > max)
2012 speed = speed / speed.getLength() * max;
2016 lplayer->setSpeed(speed);
2021 This also does collision detection.
2023 lplayer->move(dtime_part, *m_map, position_max_increment,
2024 &player_collisions);
2027 while(dtime_downcount > 0.001);
2029 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2031 for(core::list<CollisionInfo>::Iterator
2032 i = player_collisions.begin();
2033 i != player_collisions.end(); i++)
2035 CollisionInfo &info = *i;
2036 if(info.t == COLLISION_FALL)
2038 //f32 tolerance = BS*10; // 2 without damage
2039 f32 tolerance = BS*12; // 3 without damage
2041 if(info.speed > tolerance)
2043 f32 damage_f = (info.speed - tolerance)/BS*factor;
2044 u16 damage = (u16)(damage_f+0.5);
2045 if(lplayer->hp > damage)
2046 lplayer->hp -= damage;
2050 ClientEnvEvent event;
2051 event.type = CEE_PLAYER_DAMAGE;
2052 event.player_damage.amount = damage;
2053 m_client_event_queue.push_back(event);
2059 A quick draft of lava damage
2061 if(m_lava_hurt_interval.step(dtime, 1.0))
2063 v3f pf = lplayer->getPosition();
2065 // Feet, middle and head
2066 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2067 MapNode n1 = m_map->getNodeNoEx(p1);
2068 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2069 MapNode n2 = m_map->getNodeNoEx(p2);
2070 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2071 MapNode n3 = m_map->getNodeNoEx(p2);
2073 u32 damage_per_second = 0;
2074 damage_per_second = MYMAX(damage_per_second,
2075 m_gamedef->ndef()->get(n1).damage_per_second);
2076 damage_per_second = MYMAX(damage_per_second,
2077 m_gamedef->ndef()->get(n2).damage_per_second);
2078 damage_per_second = MYMAX(damage_per_second,
2079 m_gamedef->ndef()->get(n3).damage_per_second);
2081 if(damage_per_second != 0)
2083 ClientEnvEvent event;
2084 event.type = CEE_PLAYER_DAMAGE;
2085 event.player_damage.amount = damage_per_second;
2086 m_client_event_queue.push_back(event);
2091 Stuff that can be done in an arbitarily large dtime
2093 for(core::list<Player*>::Iterator i = m_players.begin();
2094 i != m_players.end(); i++)
2096 Player *player = *i;
2097 v3f playerpos = player->getPosition();
2100 Handle non-local players
2102 if(player->isLocal() == false)
2105 player->move(dtime, *m_map, 100*BS);
2109 // Update lighting on all players on client
2110 u8 light = LIGHT_MAX;
2113 v3s16 p = player->getLightPosition();
2114 MapNode n = m_map->getNode(p);
2115 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2117 catch(InvalidPositionException &e) {}
2118 player->updateLight(light);
2121 Add footsteps to grass
2125 // Get node that is at BS/4 under player
2126 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2128 MapNode n = m_map->getNode(bottompos);
2129 if(n.getContent() == CONTENT_GRASS)
2131 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2132 m_map->setNode(bottompos, n);
2133 // Update mesh on client
2134 if(m_map->mapType() == MAPTYPE_CLIENT)
2136 v3s16 p_blocks = getNodeBlockPos(bottompos);
2137 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2138 //b->updateMesh(getDayNightRatio());
2139 b->setMeshExpired(true);
2143 catch(InvalidPositionException &e)
2150 Step active objects and update lighting of them
2153 for(core::map<u16, ClientActiveObject*>::Iterator
2154 i = m_active_objects.getIterator();
2155 i.atEnd()==false; i++)
2157 ClientActiveObject* obj = i.getNode()->getValue();
2159 obj->step(dtime, this);
2161 if(m_active_object_light_update_interval.step(dtime, 0.21))
2164 //u8 light = LIGHT_MAX;
2168 v3s16 p = obj->getLightPosition();
2169 MapNode n = m_map->getNode(p);
2170 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2172 catch(InvalidPositionException &e) {}
2173 obj->updateLight(light);
2178 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2180 m_map->updateMeshes(blockpos, getDayNightRatio());
2183 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2185 m_map->expireMeshes(only_daynight_diffed);
2188 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2190 core::map<u16, ClientActiveObject*>::Node *n;
2191 n = m_active_objects.find(id);
2194 return n->getValue();
2197 bool isFreeClientActiveObjectId(u16 id,
2198 core::map<u16, ClientActiveObject*> &objects)
2203 for(core::map<u16, ClientActiveObject*>::Iterator
2204 i = objects.getIterator();
2205 i.atEnd()==false; i++)
2207 if(i.getNode()->getKey() == id)
2213 u16 getFreeClientActiveObjectId(
2214 core::map<u16, ClientActiveObject*> &objects)
2219 if(isFreeClientActiveObjectId(new_id, objects))
2229 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2232 if(object->getId() == 0)
2234 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2237 infostream<<"ClientEnvironment::addActiveObject(): "
2238 <<"no free ids available"<<std::endl;
2242 object->setId(new_id);
2244 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2246 infostream<<"ClientEnvironment::addActiveObject(): "
2247 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2251 infostream<<"ClientEnvironment::addActiveObject(): "
2252 <<"added (id="<<object->getId()<<")"<<std::endl;
2253 m_active_objects.insert(object->getId(), object);
2254 // TODO: Make g_texturesource non-global
2255 object->addToScene(m_smgr, m_texturesource);
2256 return object->getId();
2259 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2260 const std::string &init_data)
2262 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2265 infostream<<"ClientEnvironment::addActiveObject(): "
2266 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2273 obj->initialize(init_data);
2275 addActiveObject(obj);
2278 void ClientEnvironment::removeActiveObject(u16 id)
2280 infostream<<"ClientEnvironment::removeActiveObject(): "
2281 <<"id="<<id<<std::endl;
2282 ClientActiveObject* obj = getActiveObject(id);
2285 infostream<<"ClientEnvironment::removeActiveObject(): "
2286 <<"id="<<id<<" not found"<<std::endl;
2289 obj->removeFromScene();
2291 m_active_objects.remove(id);
2294 void ClientEnvironment::processActiveObjectMessage(u16 id,
2295 const std::string &data)
2297 ClientActiveObject* obj = getActiveObject(id);
2300 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2301 <<" got message for id="<<id<<", which doesn't exist."
2305 obj->processMessage(data);
2309 Callbacks for activeobjects
2312 void ClientEnvironment::damageLocalPlayer(u8 damage)
2314 LocalPlayer *lplayer = getLocalPlayer();
2317 if(lplayer->hp > damage)
2318 lplayer->hp -= damage;
2322 ClientEnvEvent event;
2323 event.type = CEE_PLAYER_DAMAGE;
2324 event.player_damage.amount = damage;
2325 m_client_event_queue.push_back(event);
2329 Client likes to call these
2332 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2333 core::array<DistanceSortedActiveObject> &dest)
2335 for(core::map<u16, ClientActiveObject*>::Iterator
2336 i = m_active_objects.getIterator();
2337 i.atEnd()==false; i++)
2339 ClientActiveObject* obj = i.getNode()->getValue();
2341 f32 d = (obj->getPosition() - origin).getLength();
2346 DistanceSortedActiveObject dso(obj, d);
2348 dest.push_back(dso);
2352 ClientEnvEvent ClientEnvironment::getClientEvent()
2354 if(m_client_event_queue.size() == 0)
2356 ClientEnvEvent event;
2357 event.type = CEE_NONE;
2360 return m_client_event_queue.pop_front();
2363 #endif // #ifndef SERVER