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"
33 #include "mapnode_contentfeatures.h"
35 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
37 Environment::Environment():
42 Environment::~Environment()
45 for(core::list<Player*>::Iterator i = m_players.begin();
46 i != m_players.end(); i++)
52 void Environment::addPlayer(Player *player)
54 DSTACK(__FUNCTION_NAME);
56 Check that peer_ids are unique.
57 Also check that names are unique.
58 Exception: there can be multiple players with peer_id=0
60 // If peer id is non-zero, it has to be unique.
61 if(player->peer_id != 0)
62 assert(getPlayer(player->peer_id) == NULL);
63 // Name has to be unique.
64 assert(getPlayer(player->getName()) == NULL);
66 m_players.push_back(player);
69 void Environment::removePlayer(u16 peer_id)
71 DSTACK(__FUNCTION_NAME);
73 for(core::list<Player*>::Iterator i = m_players.begin();
74 i != m_players.end(); i++)
77 if(player->peer_id != peer_id)
82 // See if there is an another one
83 // (shouldn't be, but just to be sure)
88 Player * Environment::getPlayer(u16 peer_id)
90 for(core::list<Player*>::Iterator i = m_players.begin();
91 i != m_players.end(); i++)
94 if(player->peer_id == peer_id)
100 Player * Environment::getPlayer(const char *name)
102 for(core::list<Player*>::Iterator i = m_players.begin();
103 i != m_players.end(); i++)
106 if(strcmp(player->getName(), name) == 0)
112 Player * Environment::getRandomConnectedPlayer()
114 core::list<Player*> connected_players = getPlayers(true);
115 u32 chosen_one = myrand() % connected_players.size();
117 for(core::list<Player*>::Iterator
118 i = connected_players.begin();
119 i != connected_players.end(); i++)
131 Player * Environment::getNearestConnectedPlayer(v3f pos)
133 core::list<Player*> connected_players = getPlayers(true);
135 Player *nearest_player = NULL;
136 for(core::list<Player*>::Iterator
137 i = connected_players.begin();
138 i != connected_players.end(); i++)
141 f32 d = player->getPosition().getDistanceFrom(pos);
142 if(d < nearest_d || nearest_player == NULL)
145 nearest_player = player;
148 return nearest_player;
151 core::list<Player*> Environment::getPlayers()
156 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
158 core::list<Player*> newlist;
159 for(core::list<Player*>::Iterator
160 i = m_players.begin();
161 i != m_players.end(); i++)
165 if(ignore_disconnected)
167 // Ignore disconnected players
168 if(player->peer_id == 0)
172 newlist.push_back(player);
177 void Environment::printPlayers(std::ostream &o)
179 o<<"Players in environment:"<<std::endl;
180 for(core::list<Player*>::Iterator i = m_players.begin();
181 i != m_players.end(); i++)
184 o<<"Player peer_id="<<player->peer_id<<std::endl;
188 /*void Environment::setDayNightRatio(u32 r)
190 getDayNightRatio() = r;
193 u32 Environment::getDayNightRatio()
195 //return getDayNightRatio();
196 return time_to_daynight_ratio(m_time_of_day);
203 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
206 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
207 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
208 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
215 void ActiveBlockList::update(core::list<v3s16> &active_positions,
217 core::map<v3s16, bool> &blocks_removed,
218 core::map<v3s16, bool> &blocks_added)
223 core::map<v3s16, bool> newlist;
224 for(core::list<v3s16>::Iterator i = active_positions.begin();
225 i != active_positions.end(); i++)
227 fillRadiusBlock(*i, radius, newlist);
231 Find out which blocks on the old list are not on the new list
233 // Go through old list
234 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
235 i.atEnd()==false; i++)
237 v3s16 p = i.getNode()->getKey();
238 // If not on new list, it's been removed
239 if(newlist.find(p) == NULL)
240 blocks_removed.insert(p, true);
244 Find out which blocks on the new list are not on the old list
246 // Go through new list
247 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
248 i.atEnd()==false; i++)
250 v3s16 p = i.getNode()->getKey();
251 // If not on old list, it's been added
252 if(m_list.find(p) == NULL)
253 blocks_added.insert(p, true);
260 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
261 i.atEnd()==false; i++)
263 v3s16 p = i.getNode()->getKey();
264 m_list.insert(p, true);
272 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L):
275 m_random_spawn_timer(3),
276 m_send_recommended_timer(0),
278 m_game_time_fraction_counter(0)
282 ServerEnvironment::~ServerEnvironment()
284 // Clear active block list.
285 // This makes the next one delete all active objects.
286 m_active_blocks.clear();
288 // Convert all objects to static and delete the active objects
289 deactivateFarObjects(true);
295 void ServerEnvironment::serializePlayers(const std::string &savedir)
297 std::string players_path = savedir + "/players";
298 fs::CreateDir(players_path);
300 core::map<Player*, bool> saved_players;
302 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
303 for(u32 i=0; i<player_files.size(); i++)
305 if(player_files[i].dir)
308 // Full path to this file
309 std::string path = players_path + "/" + player_files[i].name;
311 //infostream<<"Checking player file "<<path<<std::endl;
313 // Load player to see what is its name
314 ServerRemotePlayer testplayer;
316 // Open file and deserialize
317 std::ifstream is(path.c_str(), std::ios_base::binary);
318 if(is.good() == false)
320 infostream<<"Failed to read "<<path<<std::endl;
323 testplayer.deSerialize(is);
326 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
328 // Search for the player
329 std::string playername = testplayer.getName();
330 Player *player = getPlayer(playername.c_str());
333 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
337 //infostream<<"Found matching player, overwriting."<<std::endl;
339 // OK, found. Save player there.
341 // Open file and serialize
342 std::ofstream os(path.c_str(), std::ios_base::binary);
343 if(os.good() == false)
345 infostream<<"Failed to overwrite "<<path<<std::endl;
348 player->serialize(os);
349 saved_players.insert(player, true);
353 for(core::list<Player*>::Iterator i = m_players.begin();
354 i != m_players.end(); i++)
357 if(saved_players.find(player) != NULL)
359 /*infostream<<"Player "<<player->getName()
360 <<" was already saved."<<std::endl;*/
363 std::string playername = player->getName();
364 // Don't save unnamed player
367 //infostream<<"Not saving unnamed player."<<std::endl;
373 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
374 playername = "player";
375 std::string path = players_path + "/" + playername;
377 for(u32 i=0; i<1000; i++)
379 if(fs::PathExists(path) == false)
384 path = players_path + "/" + playername + itos(i);
388 infostream<<"Didn't find free file for player"<<std::endl;
393 /*infostream<<"Saving player "<<player->getName()<<" to "
395 // Open file and serialize
396 std::ofstream os(path.c_str(), std::ios_base::binary);
397 if(os.good() == false)
399 infostream<<"Failed to overwrite "<<path<<std::endl;
402 player->serialize(os);
403 saved_players.insert(player, true);
407 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
410 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
412 std::string players_path = savedir + "/players";
414 core::map<Player*, bool> saved_players;
416 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
417 for(u32 i=0; i<player_files.size(); i++)
419 if(player_files[i].dir)
422 // Full path to this file
423 std::string path = players_path + "/" + player_files[i].name;
425 infostream<<"Checking player file "<<path<<std::endl;
427 // Load player to see what is its name
428 ServerRemotePlayer testplayer;
430 // Open file and deserialize
431 std::ifstream is(path.c_str(), std::ios_base::binary);
432 if(is.good() == false)
434 infostream<<"Failed to read "<<path<<std::endl;
437 testplayer.deSerialize(is);
440 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
442 infostream<<"Not loading player with invalid name: "
443 <<testplayer.getName()<<std::endl;
446 infostream<<"Loaded test player with name "<<testplayer.getName()
449 // Search for the player
450 std::string playername = testplayer.getName();
451 Player *player = getPlayer(playername.c_str());
452 bool newplayer = false;
455 infostream<<"Is a new player"<<std::endl;
456 player = new ServerRemotePlayer();
462 infostream<<"Reading player "<<testplayer.getName()<<" from "
464 // Open file and deserialize
465 std::ifstream is(path.c_str(), std::ios_base::binary);
466 if(is.good() == false)
468 infostream<<"Failed to read "<<path<<std::endl;
471 player->deSerialize(is);
479 void ServerEnvironment::saveMeta(const std::string &savedir)
481 std::string path = savedir + "/env_meta.txt";
483 // Open file and serialize
484 std::ofstream os(path.c_str(), std::ios_base::binary);
485 if(os.good() == false)
487 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
489 throw SerializationError("Couldn't save env meta");
493 args.setU64("game_time", m_game_time);
494 args.setU64("time_of_day", getTimeOfDay());
499 void ServerEnvironment::loadMeta(const std::string &savedir)
501 std::string path = savedir + "/env_meta.txt";
503 // Open file and deserialize
504 std::ifstream is(path.c_str(), std::ios_base::binary);
505 if(is.good() == false)
507 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
509 throw SerializationError("Couldn't load env meta");
517 throw SerializationError
518 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
520 std::getline(is, line);
521 std::string trimmedline = trim(line);
522 if(trimmedline == "EnvArgsEnd")
524 args.parseConfigLine(line);
528 m_game_time = args.getU64("game_time");
529 }catch(SettingNotFoundException &e){
530 // Getting this is crucial, otherwise timestamps are useless
531 throw SerializationError("Couldn't load env meta game_time");
535 m_time_of_day = args.getU64("time_of_day");
536 }catch(SettingNotFoundException &e){
537 // This is not as important
538 m_time_of_day = 9000;
543 // This is probably very useless
544 void spawnRandomObjects(MapBlock *block)
546 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
547 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
549 bool last_node_walkable = false;
550 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
553 MapNode n = block->getNodeNoEx(p);
554 if(n.getContent() == CONTENT_IGNORE)
556 if(content_features(n).liquid_type != LIQUID_NONE)
558 if(content_features(n).walkable)
560 last_node_walkable = true;
563 if(last_node_walkable)
565 // If block contains light information
566 if(content_features(n).param_type == CPT_LIGHT)
568 if(n.getLight(LIGHTBANK_DAY) <= 5)
570 if(myrand() % 1000 == 0)
572 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
574 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
575 std::string data = obj->getStaticData();
576 StaticObject s_obj(obj->getType(),
577 obj->getBasePosition(), data);
579 block->m_static_objects.insert(0, s_obj);
581 block->setChangedFlag();
586 last_node_walkable = false;
592 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
594 // Get time difference
596 u32 stamp = block->getTimestamp();
597 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
598 dtime_s = m_game_time - block->getTimestamp();
599 dtime_s += additional_dtime;
601 // Set current time as timestamp (and let it set ChangedFlag)
602 block->setTimestamp(m_game_time);
604 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
606 // Activate stored objects
607 activateObjects(block);
610 bool changed = block->m_node_metadata.step((float)dtime_s);
614 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
615 event.p = block->getPos();
616 m_map->dispatchEvent(&event);
618 block->setChangedFlag();
621 // TODO: Do something
622 // TODO: Implement usage of ActiveBlockModifier
624 // Here's a quick demonstration
626 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
627 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
628 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
630 v3s16 p = p0 + block->getPosRelative();
631 MapNode n = block->getNodeNoEx(p0);
634 // Convert all mud under proper day lighting to grass
635 if(n.getContent() == CONTENT_MUD)
639 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
640 if(content_features(n_top).air_equivalent &&
641 n_top.getLight(LIGHTBANK_DAY) >= 13)
643 n.setContent(CONTENT_GRASS);
644 m_map->addNodeWithEvent(p, n);
652 void ServerEnvironment::clearAllObjects()
654 infostream<<"ServerEnvironment::clearAllObjects(): "
655 <<"Removing all active objects"<<std::endl;
656 core::list<u16> objects_to_remove;
657 for(core::map<u16, ServerActiveObject*>::Iterator
658 i = m_active_objects.getIterator();
659 i.atEnd()==false; i++)
661 ServerActiveObject* obj = i.getNode()->getValue();
662 u16 id = i.getNode()->getKey();
663 v3f objectpos = obj->getBasePosition();
664 // Delete static object if block is loaded
665 if(obj->m_static_exists){
666 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
668 block->m_static_objects.remove(id);
669 block->raiseModified(MOD_STATE_WRITE_NEEDED);
670 obj->m_static_exists = false;
673 // If known by some client, don't delete immediately
674 if(obj->m_known_by_count > 0){
675 obj->m_pending_deactivation = true;
676 obj->m_removed = true;
679 // Deregister in scripting api
680 scriptapi_rm_object_reference(m_lua, obj);
681 // Delete active object
683 // Id to be removed from m_active_objects
684 objects_to_remove.push_back(id);
686 // Remove references from m_active_objects
687 for(core::list<u16>::Iterator i = objects_to_remove.begin();
688 i != objects_to_remove.end(); i++)
690 m_active_objects.remove(*i);
693 core::list<v3s16> loadable_blocks;
694 infostream<<"ServerEnvironment::clearAllObjects(): "
695 <<"Listing all loadable blocks"<<std::endl;
696 m_map->listAllLoadableBlocks(loadable_blocks);
697 infostream<<"ServerEnvironment::clearAllObjects(): "
698 <<"Done listing all loadable blocks: "
699 <<loadable_blocks.size()
700 <<", now clearing"<<std::endl;
701 u32 report_interval = loadable_blocks.size() / 10;
702 u32 num_blocks_checked = 0;
703 u32 num_blocks_cleared = 0;
704 u32 num_objs_cleared = 0;
705 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
706 i != loadable_blocks.end(); i++)
709 MapBlock *block = m_map->emergeBlock(p, false);
711 errorstream<<"ServerEnvironment::clearAllObjects(): "
712 <<"Failed to emerge block "<<PP(p)<<std::endl;
715 u32 num_stored = block->m_static_objects.m_stored.size();
716 u32 num_active = block->m_static_objects.m_active.size();
717 if(num_stored != 0 || num_active != 0){
718 block->m_static_objects.m_stored.clear();
719 block->m_static_objects.m_active.clear();
720 block->raiseModified(MOD_STATE_WRITE_NEEDED);
721 num_objs_cleared += num_stored + num_active;
722 num_blocks_cleared++;
724 num_blocks_checked++;
726 if(num_blocks_checked % report_interval == 0){
727 float percent = 100.0 * (float)num_blocks_checked /
728 loadable_blocks.size();
729 infostream<<"ServerEnvironment::clearAllObjects(): "
730 <<"Cleared "<<num_objs_cleared<<" objects"
731 <<" in "<<num_blocks_cleared<<" blocks ("
732 <<percent<<"%)"<<std::endl;
735 infostream<<"ServerEnvironment::clearAllObjects(): "
736 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
737 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
740 static void getMob_dungeon_master(Settings &properties)
742 properties.set("looks", "dungeon_master");
743 properties.setFloat("yaw", 1.57);
744 properties.setFloat("hp", 30);
745 properties.setBool("bright_shooting", true);
746 properties.set("shoot_type", "fireball");
747 properties.set("shoot_y", "0.7");
748 properties.set("player_hit_damage", "1");
749 properties.set("player_hit_distance", "1.0");
750 properties.set("player_hit_interval", "0.5");
751 properties.setBool("mindless_rage", myrand_range(0,100)==0);
754 void ServerEnvironment::step(float dtime)
756 DSTACK(__FUNCTION_NAME);
758 //TimeTaker timer("ServerEnv step");
761 bool footprints = g_settings->getBool("footprints");
767 m_game_time_fraction_counter += dtime;
768 u32 inc_i = (u32)m_game_time_fraction_counter;
769 m_game_time += inc_i;
770 m_game_time_fraction_counter -= (float)inc_i;
777 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
778 for(core::list<Player*>::Iterator i = m_players.begin();
779 i != m_players.end(); i++)
783 // Ignore disconnected players
784 if(player->peer_id == 0)
787 v3f playerpos = player->getPosition();
790 player->move(dtime, *m_map, 100*BS);
793 Add footsteps to grass
797 // Get node that is at BS/4 under player
798 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
800 MapNode n = m_map->getNode(bottompos);
801 if(n.getContent() == CONTENT_GRASS)
803 n.setContent(CONTENT_GRASS_FOOTSTEPS);
804 m_map->setNode(bottompos, n);
807 catch(InvalidPositionException &e)
815 Manage active block list
817 if(m_active_blocks_management_interval.step(dtime, 2.0))
819 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
821 Get player block positions
823 core::list<v3s16> players_blockpos;
824 for(core::list<Player*>::Iterator
825 i = m_players.begin();
826 i != m_players.end(); i++)
829 // Ignore disconnected players
830 if(player->peer_id == 0)
832 v3s16 blockpos = getNodeBlockPos(
833 floatToInt(player->getPosition(), BS));
834 players_blockpos.push_back(blockpos);
838 Update list of active blocks, collecting changes
840 const s16 active_block_range = g_settings->getS16("active_block_range");
841 core::map<v3s16, bool> blocks_removed;
842 core::map<v3s16, bool> blocks_added;
843 m_active_blocks.update(players_blockpos, active_block_range,
844 blocks_removed, blocks_added);
847 Handle removed blocks
850 // Convert active objects that are no more in active blocks to static
851 deactivateFarObjects(false);
853 for(core::map<v3s16, bool>::Iterator
854 i = blocks_removed.getIterator();
855 i.atEnd()==false; i++)
857 v3s16 p = i.getNode()->getKey();
859 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
860 <<") became inactive"<<std::endl;*/
862 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
866 // Set current time as timestamp (and let it set ChangedFlag)
867 block->setTimestamp(m_game_time);
874 for(core::map<v3s16, bool>::Iterator
875 i = blocks_added.getIterator();
876 i.atEnd()==false; i++)
878 v3s16 p = i.getNode()->getKey();
880 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
881 <<") became active"<<std::endl;*/
883 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
887 activateBlock(block);
892 Mess around in active blocks
894 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
896 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
900 for(core::map<v3s16, bool>::Iterator
901 i = m_active_blocks.m_list.getIterator();
902 i.atEnd()==false; i++)
904 v3s16 p = i.getNode()->getKey();
906 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
907 <<") being handled"<<std::endl;*/
909 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
913 // Reset block usage timer
914 block->resetUsageTimer();
916 // Set current time as timestamp
917 block->setTimestampNoChangedFlag(m_game_time);
920 bool changed = block->m_node_metadata.step(dtime);
924 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
926 m_map->dispatchEvent(&event);
928 block->setChangedFlag();
933 if(m_active_blocks_test_interval.step(dtime, 10.0))
935 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
936 //float dtime = 10.0;
938 for(core::map<v3s16, bool>::Iterator
939 i = m_active_blocks.m_list.getIterator();
940 i.atEnd()==false; i++)
942 v3s16 p = i.getNode()->getKey();
944 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
945 <<") being handled"<<std::endl;*/
947 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
951 // Set current time as timestamp
952 block->setTimestampNoChangedFlag(m_game_time);
957 Note that map modifications should be done using the event-
958 making map methods so that the server gets information
961 Reading can be done quickly directly from the block.
963 Everything should bind to inside this single content
964 searching loop to keep things fast.
966 // TODO: Implement usage of ActiveBlockModifier
968 // Find out how many objects the block contains
969 //u32 active_object_count = block->m_static_objects.m_active.size();
970 // Find out how many objects this and all the neighbors contain
971 u32 active_object_count_wider = 0;
972 for(s16 x=-1; x<=1; x++)
973 for(s16 y=-1; y<=1; y++)
974 for(s16 z=-1; z<=1; z++)
976 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
979 active_object_count_wider +=
980 block->m_static_objects.m_active.size()
981 + block->m_static_objects.m_stored.size();
983 /*if(block->m_static_objects.m_stored.size() != 0){
984 errorstream<<"ServerEnvironment::step(): "
985 <<PP(block->getPos())<<" contains "
986 <<block->m_static_objects.m_stored.size()
987 <<" stored objects; "
988 <<"when spawning objects, when counting active "
989 <<"objects in wide area. relative position: "
990 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
995 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
996 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
997 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
999 v3s16 p = p0 + block->getPosRelative();
1000 MapNode n = block->getNodeNoEx(p0);
1004 Convert mud under proper lighting to grass
1006 if(n.getContent() == CONTENT_MUD)
1008 if(myrand()%20 == 0)
1010 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1011 if(content_features(n_top).air_equivalent &&
1012 n_top.getLightBlend(getDayNightRatio()) >= 13)
1014 n.setContent(CONTENT_GRASS);
1015 m_map->addNodeWithEvent(p, n);
1020 Convert grass into mud if under something else than air
1022 if(n.getContent() == CONTENT_GRASS)
1024 //if(myrand()%20 == 0)
1026 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1027 if(content_features(n_top).air_equivalent == false)
1029 n.setContent(CONTENT_MUD);
1030 m_map->addNodeWithEvent(p, n);
1035 Rats spawn around regular trees
1037 if(n.getContent() == CONTENT_TREE ||
1038 n.getContent() == CONTENT_JUNGLETREE)
1040 if(myrand()%200 == 0 && active_object_count_wider == 0)
1042 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1043 0, myrand_range(-2, 2));
1044 MapNode n1 = m_map->getNodeNoEx(p1);
1045 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1046 if(n1b.getContent() == CONTENT_GRASS &&
1047 n1.getContent() == CONTENT_AIR)
1049 v3f pos = intToFloat(p1, BS);
1050 ServerActiveObject *obj = new RatSAO(this, pos);
1051 addActiveObject(obj);
1056 Fun things spawn in caves and dungeons
1058 if(n.getContent() == CONTENT_STONE ||
1059 n.getContent() == CONTENT_MOSSYCOBBLE)
1061 if(myrand()%200 == 0 && active_object_count_wider == 0)
1063 v3s16 p1 = p + v3s16(0,1,0);
1064 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1065 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
1066 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1067 if(n1a.getContent() == CONTENT_AIR &&
1068 n1b.getContent() == CONTENT_AIR)
1070 v3f pos = intToFloat(p1, BS);
1072 if(i == 0 || i == 1){
1073 actionstream<<"A dungeon master spawns at "
1074 <<PP(p1)<<std::endl;
1075 Settings properties;
1076 getMob_dungeon_master(properties);
1077 ServerActiveObject *obj = new MobV2SAO(
1078 this, pos, &properties);
1079 addActiveObject(obj);
1080 } else if(i == 2 || i == 3){
1081 actionstream<<"Rats spawn at "
1082 <<PP(p1)<<std::endl;
1083 for(int j=0; j<3; j++){
1084 ServerActiveObject *obj = new RatSAO(
1086 addActiveObject(obj);
1089 actionstream<<"An oerkki spawns at "
1090 <<PP(p1)<<std::endl;
1091 ServerActiveObject *obj = new Oerkki1SAO(
1093 addActiveObject(obj);
1100 Make trees from saplings!
1102 if(n.getContent() == CONTENT_SAPLING)
1104 if(myrand()%50 == 0)
1106 actionstream<<"A sapling grows into a tree at "
1109 core::map<v3s16, MapBlock*> modified_blocks;
1111 ManualMapVoxelManipulator vmanip(m_map);
1112 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1113 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1114 bool is_apple_tree = myrand()%4 == 0;
1115 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1116 vmanip.blitBackAll(&modified_blocks);
1119 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1120 for(core::map<v3s16, MapBlock*>::Iterator
1121 i = modified_blocks.getIterator();
1122 i.atEnd() == false; i++)
1124 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1126 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1128 // Send a MEET_OTHER event
1130 event.type = MEET_OTHER;
1131 for(core::map<v3s16, MapBlock*>::Iterator
1132 i = modified_blocks.getIterator();
1133 i.atEnd() == false; i++)
1135 v3s16 p = i.getNode()->getKey();
1136 event.modified_blocks.insert(p, true);
1138 m_map->dispatchEvent(&event);
1146 Step script environment (run global on_step())
1148 scriptapi_environment_step(m_lua, dtime);
1154 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1155 //TimeTaker timer("Step active objects");
1157 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1159 // This helps the objects to send data at the same time
1160 bool send_recommended = false;
1161 m_send_recommended_timer += dtime;
1162 if(m_send_recommended_timer > getSendRecommendedInterval())
1164 m_send_recommended_timer -= getSendRecommendedInterval();
1165 send_recommended = true;
1168 for(core::map<u16, ServerActiveObject*>::Iterator
1169 i = m_active_objects.getIterator();
1170 i.atEnd()==false; i++)
1172 ServerActiveObject* obj = i.getNode()->getValue();
1173 // Remove non-peaceful mobs on peaceful mode
1174 if(g_settings->getBool("only_peaceful_mobs")){
1175 if(!obj->isPeaceful())
1176 obj->m_removed = true;
1178 // Don't step if is to be removed or stored statically
1179 if(obj->m_removed || obj->m_pending_deactivation)
1182 obj->step(dtime, send_recommended);
1183 // Read messages from object
1184 while(obj->m_messages_out.size() > 0)
1186 m_active_object_messages.push_back(
1187 obj->m_messages_out.pop_front());
1193 Manage active objects
1195 if(m_object_management_interval.step(dtime, 0.5))
1197 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1199 Remove objects that satisfy (m_removed && m_known_by_count==0)
1201 removeRemovedObjects();
1204 if(g_settings->getBool("enable_experimental"))
1211 m_random_spawn_timer -= dtime;
1212 if(m_random_spawn_timer < 0)
1214 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1215 //m_random_spawn_timer += 2.0;
1216 m_random_spawn_timer += 200.0;
1222 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1223 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1224 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1226 Player *player = getRandomConnectedPlayer();
1229 pos = player->getPosition();
1231 myrand_range(-3,3)*BS,
1233 myrand_range(-3,3)*BS
1237 Create a ServerActiveObject
1240 //TestSAO *obj = new TestSAO(this, pos);
1241 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1242 //ServerActiveObject *obj = new RatSAO(this, pos);
1243 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1244 //ServerActiveObject *obj = new FireflySAO(this, pos);
1246 infostream<<"Server: Spawning MobV2SAO at "
1247 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1249 Settings properties;
1250 getMob_dungeon_master(properties);
1251 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1252 addActiveObject(obj);
1256 } // enable_experimental
1259 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1261 core::map<u16, ServerActiveObject*>::Node *n;
1262 n = m_active_objects.find(id);
1265 return n->getValue();
1268 bool isFreeServerActiveObjectId(u16 id,
1269 core::map<u16, ServerActiveObject*> &objects)
1274 for(core::map<u16, ServerActiveObject*>::Iterator
1275 i = objects.getIterator();
1276 i.atEnd()==false; i++)
1278 if(i.getNode()->getKey() == id)
1284 u16 getFreeServerActiveObjectId(
1285 core::map<u16, ServerActiveObject*> &objects)
1290 if(isFreeServerActiveObjectId(new_id, objects))
1300 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1303 u16 id = addActiveObjectRaw(object, true);
1307 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1311 v3f objectpos = obj->getBasePosition();
1313 // The block in which the object resides in
1314 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1317 Update the static data
1320 // Create new static object
1321 std::string staticdata = obj->getStaticData();
1322 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1323 // Add to the block where the object is located in
1324 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1325 // Get or generate the block
1326 MapBlock *block = m_map->emergeBlock(blockpos);
1328 bool succeeded = false;
1332 block->m_static_objects.insert(0, s_obj);
1333 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1337 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1338 <<"Could not find or generate "
1339 <<"a block for storing static object"<<std::endl;
1349 Finds out what new objects have been added to
1350 inside a radius around a position
1352 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1353 core::map<u16, bool> ¤t_objects,
1354 core::map<u16, bool> &added_objects)
1356 v3f pos_f = intToFloat(pos, BS);
1357 f32 radius_f = radius * BS;
1359 Go through the object list,
1360 - discard m_removed objects,
1361 - discard objects that are too far away,
1362 - discard objects that are found in current_objects.
1363 - add remaining objects to added_objects
1365 for(core::map<u16, ServerActiveObject*>::Iterator
1366 i = m_active_objects.getIterator();
1367 i.atEnd()==false; i++)
1369 u16 id = i.getNode()->getKey();
1371 ServerActiveObject *object = i.getNode()->getValue();
1374 // Discard if removed
1375 if(object->m_removed)
1377 // Discard if too far
1378 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1379 if(distance_f > radius_f)
1381 // Discard if already on current_objects
1382 core::map<u16, bool>::Node *n;
1383 n = current_objects.find(id);
1386 // Add to added_objects
1387 added_objects.insert(id, false);
1392 Finds out what objects have been removed from
1393 inside a radius around a position
1395 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1396 core::map<u16, bool> ¤t_objects,
1397 core::map<u16, bool> &removed_objects)
1399 v3f pos_f = intToFloat(pos, BS);
1400 f32 radius_f = radius * BS;
1402 Go through current_objects; object is removed if:
1403 - object is not found in m_active_objects (this is actually an
1404 error condition; objects should be set m_removed=true and removed
1405 only after all clients have been informed about removal), or
1406 - object has m_removed=true, or
1407 - object is too far away
1409 for(core::map<u16, bool>::Iterator
1410 i = current_objects.getIterator();
1411 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 ServerActiveObject *object = getActiveObject(id);
1417 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1418 <<" object in current_objects is NULL"<<std::endl;
1420 else if(object->m_removed == false)
1422 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1423 /*infostream<<"removed == false"
1424 <<"distance_f = "<<distance_f
1425 <<", radius_f = "<<radius_f<<std::endl;*/
1426 if(distance_f < radius_f)
1432 removed_objects.insert(id, false);
1436 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1438 if(m_active_object_messages.size() == 0)
1439 return ActiveObjectMessage(0);
1441 return m_active_object_messages.pop_front();
1445 ************ Private methods *************
1448 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1452 if(object->getId() == 0){
1453 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1456 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1457 <<"no free ids available"<<std::endl;
1461 object->setId(new_id);
1464 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1465 <<"supplied with id "<<object->getId()<<std::endl;
1467 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1469 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1470 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1474 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1475 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1477 m_active_objects.insert(object->getId(), object);
1479 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1480 <<"Added id="<<object->getId()<<"; there are now "
1481 <<m_active_objects.size()<<" active objects."
1484 // Add static object to active static list of the block
1485 v3f objectpos = object->getBasePosition();
1486 std::string staticdata = object->getStaticData();
1487 StaticObject s_obj(object->getType(), objectpos, staticdata);
1488 // Add to the block where the object is located in
1489 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1490 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1493 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1494 object->m_static_exists = true;
1495 object->m_static_block = blockpos;
1498 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1501 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1502 <<"could not find block for storing id="<<object->getId()
1503 <<" statically"<<std::endl;
1506 // Register reference in scripting api (must be done before post-init)
1507 scriptapi_add_object_reference(m_lua, object);
1508 // Post-initialize object
1509 object->addedToEnvironment(object->getId());
1511 return object->getId();
1515 Remove objects that satisfy (m_removed && m_known_by_count==0)
1517 void ServerEnvironment::removeRemovedObjects()
1519 core::list<u16> objects_to_remove;
1520 for(core::map<u16, ServerActiveObject*>::Iterator
1521 i = m_active_objects.getIterator();
1522 i.atEnd()==false; i++)
1524 u16 id = i.getNode()->getKey();
1525 ServerActiveObject* obj = i.getNode()->getValue();
1526 // This shouldn't happen but check it
1529 infostream<<"NULL object found in ServerEnvironment"
1530 <<" while finding removed objects. id="<<id<<std::endl;
1531 // Id to be removed from m_active_objects
1532 objects_to_remove.push_back(id);
1537 We will delete objects that are marked as removed or thatare
1538 waiting for deletion after deactivation
1540 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1544 Delete static data from block if is marked as removed
1546 if(obj->m_static_exists && obj->m_removed)
1548 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1551 block->m_static_objects.remove(id);
1552 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1553 obj->m_static_exists = false;
1557 // If m_known_by_count > 0, don't actually remove.
1558 if(obj->m_known_by_count > 0)
1561 // Deregister in scripting api
1562 scriptapi_rm_object_reference(m_lua, obj);
1566 // Id to be removed from m_active_objects
1567 objects_to_remove.push_back(id);
1569 // Remove references from m_active_objects
1570 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1571 i != objects_to_remove.end(); i++)
1573 m_active_objects.remove(*i);
1577 static void print_hexdump(std::ostream &o, const std::string &data)
1579 const int linelength = 16;
1580 for(int l=0; ; l++){
1581 int i0 = linelength * l;
1582 bool at_end = false;
1583 int thislinelength = linelength;
1584 if(i0 + thislinelength > (int)data.size()){
1585 thislinelength = data.size() - i0;
1588 for(int di=0; di<linelength; di++){
1591 if(di<thislinelength)
1592 snprintf(buf, 4, "%.2x ", data[i]);
1594 snprintf(buf, 4, " ");
1598 for(int di=0; di<thislinelength; di++){
1612 Convert stored objects from blocks near the players to active.
1614 void ServerEnvironment::activateObjects(MapBlock *block)
1618 // Ignore if no stored objects (to not set changed flag)
1619 if(block->m_static_objects.m_stored.size() == 0)
1621 verbosestream<<"ServerEnvironment::activateObjects(): "
1622 <<"activating objects of block "<<PP(block->getPos())
1623 <<" ("<<block->m_static_objects.m_stored.size()
1624 <<" objects)"<<std::endl;
1625 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1627 errorstream<<"suspiciously large amount of objects detected: "
1628 <<block->m_static_objects.m_stored.size()<<" in "
1629 <<PP(block->getPos())
1630 <<"; removing all of them."<<std::endl;
1631 // Clear stored list
1632 block->m_static_objects.m_stored.clear();
1633 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1636 // A list for objects that couldn't be converted to static for some
1637 // reason. They will be stored back.
1638 core::list<StaticObject> new_stored;
1639 // Loop through stored static objects
1640 for(core::list<StaticObject>::Iterator
1641 i = block->m_static_objects.m_stored.begin();
1642 i != block->m_static_objects.m_stored.end(); i++)
1644 /*infostream<<"Server: Creating an active object from "
1645 <<"static data"<<std::endl;*/
1646 StaticObject &s_obj = *i;
1647 // Create an active object from the data
1648 ServerActiveObject *obj = ServerActiveObject::create
1649 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1650 // If couldn't create object, store static data back.
1653 errorstream<<"ServerEnvironment::activateObjects(): "
1654 <<"failed to create active object from static object "
1655 <<"in block "<<PP(s_obj.pos/BS)
1656 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1657 print_hexdump(verbosestream, s_obj.data);
1659 new_stored.push_back(s_obj);
1662 verbosestream<<"ServerEnvironment::activateObjects(): "
1663 <<"activated static object pos="<<PP(s_obj.pos/BS)
1664 <<" type="<<(int)s_obj.type<<std::endl;
1665 // This will also add the object to the active static list
1666 addActiveObjectRaw(obj, false);
1668 // Clear stored list
1669 block->m_static_objects.m_stored.clear();
1670 // Add leftover failed stuff to stored list
1671 for(core::list<StaticObject>::Iterator
1672 i = new_stored.begin();
1673 i != new_stored.end(); i++)
1675 StaticObject &s_obj = *i;
1676 block->m_static_objects.m_stored.push_back(s_obj);
1679 Note: Block hasn't really been modified here.
1680 The objects have just been activated and moved from the stored
1681 static list to the active static list.
1682 As such, the block is essentially the same.
1683 Thus, do not call block->setChangedFlag().
1684 Otherwise there would be a huge amount of unnecessary I/O.
1689 Convert objects that are not standing inside active blocks to static.
1691 If m_known_by_count != 0, active object is not deleted, but static
1692 data is still updated.
1694 If force_delete is set, active object is deleted nevertheless. It
1695 shall only be set so in the destructor of the environment.
1697 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1699 core::list<u16> objects_to_remove;
1700 for(core::map<u16, ServerActiveObject*>::Iterator
1701 i = m_active_objects.getIterator();
1702 i.atEnd()==false; i++)
1704 ServerActiveObject* obj = i.getNode()->getValue();
1706 // This shouldn't happen but check it
1709 errorstream<<"NULL object found in ServerEnvironment"
1715 // If pending deactivation, let removeRemovedObjects() do it
1716 if(obj->m_pending_deactivation)
1719 u16 id = i.getNode()->getKey();
1720 v3f objectpos = obj->getBasePosition();
1722 // The block in which the object resides in
1723 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1725 // If block is active, don't remove
1726 if(m_active_blocks.contains(blockpos_o))
1729 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1730 <<"deactivating object id="<<id<<" on inactive block "
1731 <<PP(blockpos_o)<<std::endl;
1733 // If known by some client, don't immediately delete.
1734 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1737 Update the static data
1740 // Create new static object
1741 std::string staticdata_new = obj->getStaticData();
1742 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1744 bool stays_in_same_block = false;
1745 bool data_changed = true;
1747 if(obj->m_static_exists){
1748 if(obj->m_static_block == blockpos_o)
1749 stays_in_same_block = true;
1751 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1753 core::map<u16, StaticObject>::Node *n =
1754 block->m_static_objects.m_active.find(id);
1756 StaticObject static_old = n->getValue();
1758 float save_movem = obj->getMinimumSavedMovement();
1760 if(static_old.data == staticdata_new &&
1761 (static_old.pos - objectpos).getLength() < save_movem)
1762 data_changed = false;
1764 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1765 <<"id="<<id<<" m_static_exists=true but "
1766 <<"static data doesn't actually exist in "
1767 <<PP(obj->m_static_block)<<std::endl;
1771 bool shall_be_written = (!stays_in_same_block || data_changed);
1773 // Delete old static object
1774 if(obj->m_static_exists)
1776 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1779 block->m_static_objects.remove(id);
1780 obj->m_static_exists = false;
1781 // Only mark block as modified if data changed considerably
1782 if(shall_be_written)
1783 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1787 // Add to the block where the object is located in
1788 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1789 // Get or generate the block
1790 MapBlock *block = m_map->emergeBlock(blockpos);
1794 if(block->m_static_objects.m_stored.size() >= 49){
1795 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1796 <<" statically but block "<<PP(blockpos)
1797 <<" already contains "
1798 <<block->m_static_objects.m_stored.size()
1799 <<" (over 49) objects."
1800 <<" Forcing delete."<<std::endl;
1801 force_delete = true;
1803 u16 new_id = pending_delete ? id : 0;
1804 block->m_static_objects.insert(new_id, s_obj);
1806 // Only mark block as modified if data changed considerably
1807 if(shall_be_written)
1808 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1810 obj->m_static_exists = true;
1811 obj->m_static_block = block->getPos();
1815 errorstream<<"ServerEnv: Could not find or generate "
1816 <<"a block for storing id="<<obj->getId()
1817 <<" statically"<<std::endl;
1822 If known by some client, set pending deactivation.
1823 Otherwise delete it immediately.
1828 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1829 <<"object id="<<id<<" is known by clients"
1830 <<"; not deleting yet"<<std::endl;
1832 obj->m_pending_deactivation = true;
1836 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1837 <<"object id="<<id<<" is not known by clients"
1838 <<"; deleting"<<std::endl;
1840 // Deregister in scripting api
1841 scriptapi_rm_object_reference(m_lua, obj);
1843 // Delete active object
1845 // Id to be removed from m_active_objects
1846 objects_to_remove.push_back(id);
1849 // Remove references from m_active_objects
1850 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1851 i != objects_to_remove.end(); i++)
1853 m_active_objects.remove(*i);
1864 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1872 ClientEnvironment::~ClientEnvironment()
1874 // delete active objects
1875 for(core::map<u16, ClientActiveObject*>::Iterator
1876 i = m_active_objects.getIterator();
1877 i.atEnd()==false; i++)
1879 delete i.getNode()->getValue();
1886 void ClientEnvironment::addPlayer(Player *player)
1888 DSTACK(__FUNCTION_NAME);
1890 It is a failure if player is local and there already is a local
1893 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1895 Environment::addPlayer(player);
1898 LocalPlayer * ClientEnvironment::getLocalPlayer()
1900 for(core::list<Player*>::Iterator i = m_players.begin();
1901 i != m_players.end(); i++)
1903 Player *player = *i;
1904 if(player->isLocal())
1905 return (LocalPlayer*)player;
1910 void ClientEnvironment::step(float dtime)
1912 DSTACK(__FUNCTION_NAME);
1914 // Get some settings
1915 bool free_move = g_settings->getBool("free_move");
1916 bool footprints = g_settings->getBool("footprints");
1919 LocalPlayer *lplayer = getLocalPlayer();
1921 // collision info queue
1922 core::list<CollisionInfo> player_collisions;
1925 Get the speed the player is going
1927 bool is_climbing = lplayer->is_climbing;
1929 f32 player_speed = 0.001; // just some small value
1930 player_speed = lplayer->getSpeed().getLength();
1933 Maximum position increment
1935 //f32 position_max_increment = 0.05*BS;
1936 f32 position_max_increment = 0.1*BS;
1938 // Maximum time increment (for collision detection etc)
1939 // time = distance / speed
1940 f32 dtime_max_increment = position_max_increment / player_speed;
1942 // Maximum time increment is 10ms or lower
1943 if(dtime_max_increment > 0.01)
1944 dtime_max_increment = 0.01;
1946 // Don't allow overly huge dtime
1950 f32 dtime_downcount = dtime;
1953 Stuff that has a maximum time increment
1962 if(dtime_downcount > dtime_max_increment)
1964 dtime_part = dtime_max_increment;
1965 dtime_downcount -= dtime_part;
1969 dtime_part = dtime_downcount;
1971 Setting this to 0 (no -=dtime_part) disables an infinite loop
1972 when dtime_part is so small that dtime_downcount -= dtime_part
1975 dtime_downcount = 0;
1983 v3f lplayerpos = lplayer->getPosition();
1986 if(free_move == false && is_climbing == false)
1989 v3f speed = lplayer->getSpeed();
1990 if(lplayer->swimming_up == false)
1991 speed.Y -= 9.81 * BS * dtime_part * 2;
1994 if(lplayer->in_water_stable || lplayer->in_water)
1996 f32 max_down = 2.0*BS;
1997 if(speed.Y < -max_down) speed.Y = -max_down;
2000 if(speed.getLength() > max)
2002 speed = speed / speed.getLength() * max;
2006 lplayer->setSpeed(speed);
2011 This also does collision detection.
2013 lplayer->move(dtime_part, *m_map, position_max_increment,
2014 &player_collisions);
2017 while(dtime_downcount > 0.001);
2019 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2021 for(core::list<CollisionInfo>::Iterator
2022 i = player_collisions.begin();
2023 i != player_collisions.end(); i++)
2025 CollisionInfo &info = *i;
2026 if(info.t == COLLISION_FALL)
2028 //f32 tolerance = BS*10; // 2 without damage
2029 f32 tolerance = BS*12; // 3 without damage
2031 if(info.speed > tolerance)
2033 f32 damage_f = (info.speed - tolerance)/BS*factor;
2034 u16 damage = (u16)(damage_f+0.5);
2035 if(lplayer->hp > damage)
2036 lplayer->hp -= damage;
2040 ClientEnvEvent event;
2041 event.type = CEE_PLAYER_DAMAGE;
2042 event.player_damage.amount = damage;
2043 m_client_event_queue.push_back(event);
2049 A quick draft of lava damage
2051 if(m_lava_hurt_interval.step(dtime, 1.0))
2053 v3f pf = lplayer->getPosition();
2055 // Feet, middle and head
2056 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2057 MapNode n1 = m_map->getNodeNoEx(p1);
2058 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2059 MapNode n2 = m_map->getNodeNoEx(p2);
2060 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2061 MapNode n3 = m_map->getNodeNoEx(p2);
2063 u32 damage_per_second = 0;
2064 damage_per_second = MYMAX(damage_per_second,
2065 content_features(n1).damage_per_second);
2066 damage_per_second = MYMAX(damage_per_second,
2067 content_features(n2).damage_per_second);
2068 damage_per_second = MYMAX(damage_per_second,
2069 content_features(n3).damage_per_second);
2071 if(damage_per_second != 0)
2073 ClientEnvEvent event;
2074 event.type = CEE_PLAYER_DAMAGE;
2075 event.player_damage.amount = damage_per_second;
2076 m_client_event_queue.push_back(event);
2081 Stuff that can be done in an arbitarily large dtime
2083 for(core::list<Player*>::Iterator i = m_players.begin();
2084 i != m_players.end(); i++)
2086 Player *player = *i;
2087 v3f playerpos = player->getPosition();
2090 Handle non-local players
2092 if(player->isLocal() == false)
2095 player->move(dtime, *m_map, 100*BS);
2099 // Update lighting on all players on client
2100 u8 light = LIGHT_MAX;
2103 v3s16 p = player->getLightPosition();
2104 MapNode n = m_map->getNode(p);
2105 light = n.getLightBlend(getDayNightRatio());
2107 catch(InvalidPositionException &e) {}
2108 player->updateLight(light);
2111 Add footsteps to grass
2115 // Get node that is at BS/4 under player
2116 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2118 MapNode n = m_map->getNode(bottompos);
2119 if(n.getContent() == CONTENT_GRASS)
2121 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2122 m_map->setNode(bottompos, n);
2123 // Update mesh on client
2124 if(m_map->mapType() == MAPTYPE_CLIENT)
2126 v3s16 p_blocks = getNodeBlockPos(bottompos);
2127 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2128 //b->updateMesh(getDayNightRatio());
2129 b->setMeshExpired(true);
2133 catch(InvalidPositionException &e)
2140 Step active objects and update lighting of them
2143 for(core::map<u16, ClientActiveObject*>::Iterator
2144 i = m_active_objects.getIterator();
2145 i.atEnd()==false; i++)
2147 ClientActiveObject* obj = i.getNode()->getValue();
2149 obj->step(dtime, this);
2151 if(m_active_object_light_update_interval.step(dtime, 0.21))
2154 //u8 light = LIGHT_MAX;
2158 v3s16 p = obj->getLightPosition();
2159 MapNode n = m_map->getNode(p);
2160 light = n.getLightBlend(getDayNightRatio());
2162 catch(InvalidPositionException &e) {}
2163 obj->updateLight(light);
2168 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2170 m_map->updateMeshes(blockpos, getDayNightRatio());
2173 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2175 m_map->expireMeshes(only_daynight_diffed);
2178 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2180 core::map<u16, ClientActiveObject*>::Node *n;
2181 n = m_active_objects.find(id);
2184 return n->getValue();
2187 bool isFreeClientActiveObjectId(u16 id,
2188 core::map<u16, ClientActiveObject*> &objects)
2193 for(core::map<u16, ClientActiveObject*>::Iterator
2194 i = objects.getIterator();
2195 i.atEnd()==false; i++)
2197 if(i.getNode()->getKey() == id)
2203 u16 getFreeClientActiveObjectId(
2204 core::map<u16, ClientActiveObject*> &objects)
2209 if(isFreeClientActiveObjectId(new_id, objects))
2219 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2222 if(object->getId() == 0)
2224 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2227 infostream<<"ClientEnvironment::addActiveObject(): "
2228 <<"no free ids available"<<std::endl;
2232 object->setId(new_id);
2234 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2236 infostream<<"ClientEnvironment::addActiveObject(): "
2237 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2241 infostream<<"ClientEnvironment::addActiveObject(): "
2242 <<"added (id="<<object->getId()<<")"<<std::endl;
2243 m_active_objects.insert(object->getId(), object);
2244 object->addToScene(m_smgr);
2245 return object->getId();
2248 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2249 const std::string &init_data)
2251 ClientActiveObject* obj = ClientActiveObject::create(type);
2254 infostream<<"ClientEnvironment::addActiveObject(): "
2255 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2262 obj->initialize(init_data);
2264 addActiveObject(obj);
2267 void ClientEnvironment::removeActiveObject(u16 id)
2269 infostream<<"ClientEnvironment::removeActiveObject(): "
2270 <<"id="<<id<<std::endl;
2271 ClientActiveObject* obj = getActiveObject(id);
2274 infostream<<"ClientEnvironment::removeActiveObject(): "
2275 <<"id="<<id<<" not found"<<std::endl;
2278 obj->removeFromScene();
2280 m_active_objects.remove(id);
2283 void ClientEnvironment::processActiveObjectMessage(u16 id,
2284 const std::string &data)
2286 ClientActiveObject* obj = getActiveObject(id);
2289 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2290 <<" got message for id="<<id<<", which doesn't exist."
2294 obj->processMessage(data);
2298 Callbacks for activeobjects
2301 void ClientEnvironment::damageLocalPlayer(u8 damage)
2303 LocalPlayer *lplayer = getLocalPlayer();
2306 if(lplayer->hp > damage)
2307 lplayer->hp -= damage;
2311 ClientEnvEvent event;
2312 event.type = CEE_PLAYER_DAMAGE;
2313 event.player_damage.amount = damage;
2314 m_client_event_queue.push_back(event);
2318 Client likes to call these
2321 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2322 core::array<DistanceSortedActiveObject> &dest)
2324 for(core::map<u16, ClientActiveObject*>::Iterator
2325 i = m_active_objects.getIterator();
2326 i.atEnd()==false; i++)
2328 ClientActiveObject* obj = i.getNode()->getValue();
2330 f32 d = (obj->getPosition() - origin).getLength();
2335 DistanceSortedActiveObject dso(obj, d);
2337 dest.push_back(dso);
2341 ClientEnvEvent ClientEnvironment::getClientEvent()
2343 if(m_client_event_queue.size() == 0)
2345 ClientEnvEvent event;
2346 event.type = CEE_NONE;
2349 return m_client_event_queue.pop_front();
2352 #endif // #ifndef SERVER