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"
34 #include "nodemetadata.h"
35 #include "main.h" // For g_settings, g_profiler
37 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39 Environment::Environment():
44 Environment::~Environment()
47 for(core::list<Player*>::Iterator i = m_players.begin();
48 i != m_players.end(); i++)
54 void Environment::addPlayer(Player *player)
56 DSTACK(__FUNCTION_NAME);
58 Check that peer_ids are unique.
59 Also check that names are unique.
60 Exception: there can be multiple players with peer_id=0
62 // If peer id is non-zero, it has to be unique.
63 if(player->peer_id != 0)
64 assert(getPlayer(player->peer_id) == NULL);
65 // Name has to be unique.
66 assert(getPlayer(player->getName()) == NULL);
68 m_players.push_back(player);
71 void Environment::removePlayer(u16 peer_id)
73 DSTACK(__FUNCTION_NAME);
75 for(core::list<Player*>::Iterator i = m_players.begin();
76 i != m_players.end(); i++)
79 if(player->peer_id != peer_id)
84 // See if there is an another one
85 // (shouldn't be, but just to be sure)
90 Player * Environment::getPlayer(u16 peer_id)
92 for(core::list<Player*>::Iterator i = m_players.begin();
93 i != m_players.end(); i++)
96 if(player->peer_id == peer_id)
102 Player * Environment::getPlayer(const char *name)
104 for(core::list<Player*>::Iterator i = m_players.begin();
105 i != m_players.end(); i++)
108 if(strcmp(player->getName(), name) == 0)
114 Player * Environment::getRandomConnectedPlayer()
116 core::list<Player*> connected_players = getPlayers(true);
117 u32 chosen_one = myrand() % connected_players.size();
119 for(core::list<Player*>::Iterator
120 i = connected_players.begin();
121 i != connected_players.end(); i++)
133 Player * Environment::getNearestConnectedPlayer(v3f pos)
135 core::list<Player*> connected_players = getPlayers(true);
137 Player *nearest_player = NULL;
138 for(core::list<Player*>::Iterator
139 i = connected_players.begin();
140 i != connected_players.end(); i++)
143 f32 d = player->getPosition().getDistanceFrom(pos);
144 if(d < nearest_d || nearest_player == NULL)
147 nearest_player = player;
150 return nearest_player;
153 core::list<Player*> Environment::getPlayers()
158 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
160 core::list<Player*> newlist;
161 for(core::list<Player*>::Iterator
162 i = m_players.begin();
163 i != m_players.end(); i++)
167 if(ignore_disconnected)
169 // Ignore disconnected players
170 if(player->peer_id == 0)
174 newlist.push_back(player);
179 void Environment::printPlayers(std::ostream &o)
181 o<<"Players in environment:"<<std::endl;
182 for(core::list<Player*>::Iterator i = m_players.begin();
183 i != m_players.end(); i++)
186 o<<"Player peer_id="<<player->peer_id<<std::endl;
190 /*void Environment::setDayNightRatio(u32 r)
192 getDayNightRatio() = r;
195 u32 Environment::getDayNightRatio()
197 //return getDayNightRatio();
198 return time_to_daynight_ratio(m_time_of_day);
205 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
208 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
209 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
210 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
217 void ActiveBlockList::update(core::list<v3s16> &active_positions,
219 core::map<v3s16, bool> &blocks_removed,
220 core::map<v3s16, bool> &blocks_added)
225 core::map<v3s16, bool> newlist;
226 for(core::list<v3s16>::Iterator i = active_positions.begin();
227 i != active_positions.end(); i++)
229 fillRadiusBlock(*i, radius, newlist);
233 Find out which blocks on the old list are not on the new list
235 // Go through old list
236 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
237 i.atEnd()==false; i++)
239 v3s16 p = i.getNode()->getKey();
240 // If not on new list, it's been removed
241 if(newlist.find(p) == NULL)
242 blocks_removed.insert(p, true);
246 Find out which blocks on the new list are not on the old list
248 // Go through new list
249 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
250 i.atEnd()==false; i++)
252 v3s16 p = i.getNode()->getKey();
253 // If not on old list, it's been added
254 if(m_list.find(p) == NULL)
255 blocks_added.insert(p, true);
262 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
263 i.atEnd()==false; i++)
265 v3s16 p = i.getNode()->getKey();
266 m_list.insert(p, true);
274 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
279 m_random_spawn_timer(3),
280 m_send_recommended_timer(0),
282 m_game_time_fraction_counter(0)
286 ServerEnvironment::~ServerEnvironment()
288 // Clear active block list.
289 // This makes the next one delete all active objects.
290 m_active_blocks.clear();
292 // Convert all objects to static and delete the active objects
293 deactivateFarObjects(true);
299 void ServerEnvironment::serializePlayers(const std::string &savedir)
301 std::string players_path = savedir + "/players";
302 fs::CreateDir(players_path);
304 core::map<Player*, bool> saved_players;
306 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
307 for(u32 i=0; i<player_files.size(); i++)
309 if(player_files[i].dir)
312 // Full path to this file
313 std::string path = players_path + "/" + player_files[i].name;
315 //infostream<<"Checking player file "<<path<<std::endl;
317 // Load player to see what is its name
318 ServerRemotePlayer testplayer(this);
320 // Open file and deserialize
321 std::ifstream is(path.c_str(), std::ios_base::binary);
322 if(is.good() == false)
324 infostream<<"Failed to read "<<path<<std::endl;
327 testplayer.deSerialize(is, m_gamedef);
330 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
332 // Search for the player
333 std::string playername = testplayer.getName();
334 Player *player = getPlayer(playername.c_str());
337 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
341 //infostream<<"Found matching player, overwriting."<<std::endl;
343 // OK, found. Save player there.
345 // Open file and serialize
346 std::ofstream os(path.c_str(), std::ios_base::binary);
347 if(os.good() == false)
349 infostream<<"Failed to overwrite "<<path<<std::endl;
352 player->serialize(os);
353 saved_players.insert(player, true);
357 for(core::list<Player*>::Iterator i = m_players.begin();
358 i != m_players.end(); i++)
361 if(saved_players.find(player) != NULL)
363 /*infostream<<"Player "<<player->getName()
364 <<" was already saved."<<std::endl;*/
367 std::string playername = player->getName();
368 // Don't save unnamed player
371 //infostream<<"Not saving unnamed player."<<std::endl;
377 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
378 playername = "player";
379 std::string path = players_path + "/" + playername;
381 for(u32 i=0; i<1000; i++)
383 if(fs::PathExists(path) == false)
388 path = players_path + "/" + playername + itos(i);
392 infostream<<"Didn't find free file for player"<<std::endl;
397 /*infostream<<"Saving player "<<player->getName()<<" to "
399 // Open file and serialize
400 std::ofstream os(path.c_str(), std::ios_base::binary);
401 if(os.good() == false)
403 infostream<<"Failed to overwrite "<<path<<std::endl;
406 player->serialize(os);
407 saved_players.insert(player, true);
411 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
414 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
416 std::string players_path = savedir + "/players";
418 core::map<Player*, bool> saved_players;
420 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
421 for(u32 i=0; i<player_files.size(); i++)
423 if(player_files[i].dir)
426 // Full path to this file
427 std::string path = players_path + "/" + player_files[i].name;
429 infostream<<"Checking player file "<<path<<std::endl;
431 // Load player to see what is its name
432 ServerRemotePlayer testplayer(this);
434 // Open file and deserialize
435 std::ifstream is(path.c_str(), std::ios_base::binary);
436 if(is.good() == false)
438 infostream<<"Failed to read "<<path<<std::endl;
441 testplayer.deSerialize(is, m_gamedef);
444 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
446 infostream<<"Not loading player with invalid name: "
447 <<testplayer.getName()<<std::endl;
450 infostream<<"Loaded test player with name "<<testplayer.getName()
453 // Search for the player
454 std::string playername = testplayer.getName();
455 Player *player = getPlayer(playername.c_str());
456 bool newplayer = false;
459 infostream<<"Is a new player"<<std::endl;
460 player = new ServerRemotePlayer(this);
466 infostream<<"Reading player "<<testplayer.getName()<<" from "
468 // Open file and deserialize
469 std::ifstream is(path.c_str(), std::ios_base::binary);
470 if(is.good() == false)
472 infostream<<"Failed to read "<<path<<std::endl;
475 player->deSerialize(is, m_gamedef);
483 void ServerEnvironment::saveMeta(const std::string &savedir)
485 std::string path = savedir + "/env_meta.txt";
487 // Open file and serialize
488 std::ofstream os(path.c_str(), std::ios_base::binary);
489 if(os.good() == false)
491 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
493 throw SerializationError("Couldn't save env meta");
497 args.setU64("game_time", m_game_time);
498 args.setU64("time_of_day", getTimeOfDay());
503 void ServerEnvironment::loadMeta(const std::string &savedir)
505 std::string path = savedir + "/env_meta.txt";
507 // Open file and deserialize
508 std::ifstream is(path.c_str(), std::ios_base::binary);
509 if(is.good() == false)
511 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
513 throw SerializationError("Couldn't load env meta");
521 throw SerializationError
522 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
524 std::getline(is, line);
525 std::string trimmedline = trim(line);
526 if(trimmedline == "EnvArgsEnd")
528 args.parseConfigLine(line);
532 m_game_time = args.getU64("game_time");
533 }catch(SettingNotFoundException &e){
534 // Getting this is crucial, otherwise timestamps are useless
535 throw SerializationError("Couldn't load env meta game_time");
539 m_time_of_day = args.getU64("time_of_day");
540 }catch(SettingNotFoundException &e){
541 // This is not as important
542 m_time_of_day = 9000;
547 // This is probably very useless
548 void spawnRandomObjects(MapBlock *block)
550 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
551 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
553 bool last_node_walkable = false;
554 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
557 MapNode n = block->getNodeNoEx(p);
558 if(n.getContent() == CONTENT_IGNORE)
560 if(content_features(n).liquid_type != LIQUID_NONE)
562 if(content_features(n).walkable)
564 last_node_walkable = true;
567 if(last_node_walkable)
569 // If block contains light information
570 if(content_features(n).param_type == CPT_LIGHT)
572 if(n.getLight(LIGHTBANK_DAY) <= 5)
574 if(myrand() % 1000 == 0)
576 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
578 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
579 std::string data = obj->getStaticData();
580 StaticObject s_obj(obj->getType(),
581 obj->getBasePosition(), data);
583 block->m_static_objects.insert(0, s_obj);
585 block->setChangedFlag();
590 last_node_walkable = false;
596 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
598 // Get time difference
600 u32 stamp = block->getTimestamp();
601 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
602 dtime_s = m_game_time - block->getTimestamp();
603 dtime_s += additional_dtime;
605 // Set current time as timestamp (and let it set ChangedFlag)
606 block->setTimestamp(m_game_time);
608 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
610 // Activate stored objects
611 activateObjects(block);
614 bool changed = block->m_node_metadata->step((float)dtime_s);
618 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
619 event.p = block->getPos();
620 m_map->dispatchEvent(&event);
622 block->setChangedFlag();
625 // TODO: Do something
626 // TODO: Implement usage of ActiveBlockModifier
628 // Here's a quick demonstration
630 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
631 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
632 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
634 v3s16 p = p0 + block->getPosRelative();
635 MapNode n = block->getNodeNoEx(p0);
638 // Convert all mud under proper day lighting to grass
639 if(n.getContent() == CONTENT_MUD)
643 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
644 if(content_features(n_top).air_equivalent &&
645 n_top.getLight(LIGHTBANK_DAY) >= 13)
647 n.setContent(CONTENT_GRASS);
648 m_map->addNodeWithEvent(p, n);
656 void ServerEnvironment::clearAllObjects()
658 infostream<<"ServerEnvironment::clearAllObjects(): "
659 <<"Removing all active objects"<<std::endl;
660 core::list<u16> objects_to_remove;
661 for(core::map<u16, ServerActiveObject*>::Iterator
662 i = m_active_objects.getIterator();
663 i.atEnd()==false; i++)
665 ServerActiveObject* obj = i.getNode()->getValue();
666 u16 id = i.getNode()->getKey();
667 v3f objectpos = obj->getBasePosition();
668 // Delete static object if block is loaded
669 if(obj->m_static_exists){
670 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
672 block->m_static_objects.remove(id);
673 block->raiseModified(MOD_STATE_WRITE_NEEDED);
674 obj->m_static_exists = false;
677 // If known by some client, don't delete immediately
678 if(obj->m_known_by_count > 0){
679 obj->m_pending_deactivation = true;
680 obj->m_removed = true;
683 // Deregister in scripting api
684 scriptapi_rm_object_reference(m_lua, obj);
685 // Delete active object
687 // Id to be removed from m_active_objects
688 objects_to_remove.push_back(id);
690 // Remove references from m_active_objects
691 for(core::list<u16>::Iterator i = objects_to_remove.begin();
692 i != objects_to_remove.end(); i++)
694 m_active_objects.remove(*i);
697 core::list<v3s16> loadable_blocks;
698 infostream<<"ServerEnvironment::clearAllObjects(): "
699 <<"Listing all loadable blocks"<<std::endl;
700 m_map->listAllLoadableBlocks(loadable_blocks);
701 infostream<<"ServerEnvironment::clearAllObjects(): "
702 <<"Done listing all loadable blocks: "
703 <<loadable_blocks.size()
704 <<", now clearing"<<std::endl;
705 u32 report_interval = loadable_blocks.size() / 10;
706 u32 num_blocks_checked = 0;
707 u32 num_blocks_cleared = 0;
708 u32 num_objs_cleared = 0;
709 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
710 i != loadable_blocks.end(); i++)
713 MapBlock *block = m_map->emergeBlock(p, false);
715 errorstream<<"ServerEnvironment::clearAllObjects(): "
716 <<"Failed to emerge block "<<PP(p)<<std::endl;
719 u32 num_stored = block->m_static_objects.m_stored.size();
720 u32 num_active = block->m_static_objects.m_active.size();
721 if(num_stored != 0 || num_active != 0){
722 block->m_static_objects.m_stored.clear();
723 block->m_static_objects.m_active.clear();
724 block->raiseModified(MOD_STATE_WRITE_NEEDED);
725 num_objs_cleared += num_stored + num_active;
726 num_blocks_cleared++;
728 num_blocks_checked++;
730 if(num_blocks_checked % report_interval == 0){
731 float percent = 100.0 * (float)num_blocks_checked /
732 loadable_blocks.size();
733 infostream<<"ServerEnvironment::clearAllObjects(): "
734 <<"Cleared "<<num_objs_cleared<<" objects"
735 <<" in "<<num_blocks_cleared<<" blocks ("
736 <<percent<<"%)"<<std::endl;
739 infostream<<"ServerEnvironment::clearAllObjects(): "
740 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
741 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
744 static void getMob_dungeon_master(Settings &properties)
746 properties.set("looks", "dungeon_master");
747 properties.setFloat("yaw", 1.57);
748 properties.setFloat("hp", 30);
749 properties.setBool("bright_shooting", true);
750 properties.set("shoot_type", "fireball");
751 properties.set("shoot_y", "0.7");
752 properties.set("player_hit_damage", "1");
753 properties.set("player_hit_distance", "1.0");
754 properties.set("player_hit_interval", "0.5");
755 properties.setBool("mindless_rage", myrand_range(0,100)==0);
758 void ServerEnvironment::step(float dtime)
760 DSTACK(__FUNCTION_NAME);
762 //TimeTaker timer("ServerEnv step");
765 bool footprints = g_settings->getBool("footprints");
771 m_game_time_fraction_counter += dtime;
772 u32 inc_i = (u32)m_game_time_fraction_counter;
773 m_game_time += inc_i;
774 m_game_time_fraction_counter -= (float)inc_i;
781 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
782 for(core::list<Player*>::Iterator i = m_players.begin();
783 i != m_players.end(); i++)
787 // Ignore disconnected players
788 if(player->peer_id == 0)
791 v3f playerpos = player->getPosition();
794 player->move(dtime, *m_map, 100*BS);
797 Add footsteps to grass
801 // Get node that is at BS/4 under player
802 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
804 MapNode n = m_map->getNode(bottompos);
805 if(n.getContent() == CONTENT_GRASS)
807 n.setContent(CONTENT_GRASS_FOOTSTEPS);
808 m_map->setNode(bottompos, n);
811 catch(InvalidPositionException &e)
819 Manage active block list
821 if(m_active_blocks_management_interval.step(dtime, 2.0))
823 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
825 Get player block positions
827 core::list<v3s16> players_blockpos;
828 for(core::list<Player*>::Iterator
829 i = m_players.begin();
830 i != m_players.end(); i++)
833 // Ignore disconnected players
834 if(player->peer_id == 0)
836 v3s16 blockpos = getNodeBlockPos(
837 floatToInt(player->getPosition(), BS));
838 players_blockpos.push_back(blockpos);
842 Update list of active blocks, collecting changes
844 const s16 active_block_range = g_settings->getS16("active_block_range");
845 core::map<v3s16, bool> blocks_removed;
846 core::map<v3s16, bool> blocks_added;
847 m_active_blocks.update(players_blockpos, active_block_range,
848 blocks_removed, blocks_added);
851 Handle removed blocks
854 // Convert active objects that are no more in active blocks to static
855 deactivateFarObjects(false);
857 for(core::map<v3s16, bool>::Iterator
858 i = blocks_removed.getIterator();
859 i.atEnd()==false; i++)
861 v3s16 p = i.getNode()->getKey();
863 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
864 <<") became inactive"<<std::endl;*/
866 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
870 // Set current time as timestamp (and let it set ChangedFlag)
871 block->setTimestamp(m_game_time);
878 for(core::map<v3s16, bool>::Iterator
879 i = blocks_added.getIterator();
880 i.atEnd()==false; i++)
882 v3s16 p = i.getNode()->getKey();
884 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
885 <<") became active"<<std::endl;*/
887 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
891 activateBlock(block);
896 Mess around in active blocks
898 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
900 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
904 for(core::map<v3s16, bool>::Iterator
905 i = m_active_blocks.m_list.getIterator();
906 i.atEnd()==false; i++)
908 v3s16 p = i.getNode()->getKey();
910 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
911 <<") being handled"<<std::endl;*/
913 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
917 // Reset block usage timer
918 block->resetUsageTimer();
920 // Set current time as timestamp
921 block->setTimestampNoChangedFlag(m_game_time);
924 bool changed = block->m_node_metadata->step(dtime);
928 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
930 m_map->dispatchEvent(&event);
932 block->setChangedFlag();
937 if(m_active_blocks_test_interval.step(dtime, 10.0))
939 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
940 //float dtime = 10.0;
942 for(core::map<v3s16, bool>::Iterator
943 i = m_active_blocks.m_list.getIterator();
944 i.atEnd()==false; i++)
946 v3s16 p = i.getNode()->getKey();
948 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
949 <<") being handled"<<std::endl;*/
951 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
955 // Set current time as timestamp
956 block->setTimestampNoChangedFlag(m_game_time);
961 Note that map modifications should be done using the event-
962 making map methods so that the server gets information
965 Reading can be done quickly directly from the block.
967 Everything should bind to inside this single content
968 searching loop to keep things fast.
970 // TODO: Implement usage of ActiveBlockModifier
972 // Find out how many objects the block contains
973 //u32 active_object_count = block->m_static_objects.m_active.size();
974 // Find out how many objects this and all the neighbors contain
975 u32 active_object_count_wider = 0;
976 for(s16 x=-1; x<=1; x++)
977 for(s16 y=-1; y<=1; y++)
978 for(s16 z=-1; z<=1; z++)
980 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
983 active_object_count_wider +=
984 block->m_static_objects.m_active.size()
985 + block->m_static_objects.m_stored.size();
987 /*if(block->m_static_objects.m_stored.size() != 0){
988 errorstream<<"ServerEnvironment::step(): "
989 <<PP(block->getPos())<<" contains "
990 <<block->m_static_objects.m_stored.size()
991 <<" stored objects; "
992 <<"when spawning objects, when counting active "
993 <<"objects in wide area. relative position: "
994 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
999 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1000 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1001 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1003 v3s16 p = p0 + block->getPosRelative();
1004 MapNode n = block->getNodeNoEx(p0);
1008 Convert mud under proper lighting to grass
1010 if(n.getContent() == CONTENT_MUD)
1012 if(myrand()%20 == 0)
1014 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1015 if(content_features(n_top).air_equivalent &&
1016 n_top.getLightBlend(getDayNightRatio()) >= 13)
1018 n.setContent(CONTENT_GRASS);
1019 m_map->addNodeWithEvent(p, n);
1024 Convert grass into mud if under something else than air
1026 if(n.getContent() == CONTENT_GRASS)
1028 //if(myrand()%20 == 0)
1030 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1031 if(content_features(n_top).air_equivalent == false)
1033 n.setContent(CONTENT_MUD);
1034 m_map->addNodeWithEvent(p, n);
1039 Rats spawn around regular trees
1041 if(n.getContent() == CONTENT_TREE ||
1042 n.getContent() == CONTENT_JUNGLETREE)
1044 if(myrand()%200 == 0 && active_object_count_wider == 0)
1046 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1047 0, myrand_range(-2, 2));
1048 MapNode n1 = m_map->getNodeNoEx(p1);
1049 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1050 if(n1b.getContent() == CONTENT_GRASS &&
1051 n1.getContent() == CONTENT_AIR)
1053 v3f pos = intToFloat(p1, BS);
1054 ServerActiveObject *obj = new RatSAO(this, pos);
1055 addActiveObject(obj);
1060 Fun things spawn in caves and dungeons
1062 if(n.getContent() == CONTENT_STONE ||
1063 n.getContent() == CONTENT_MOSSYCOBBLE)
1065 if(myrand()%200 == 0 && active_object_count_wider == 0)
1067 v3s16 p1 = p + v3s16(0,1,0);
1068 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1069 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
1070 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1071 if(n1a.getContent() == CONTENT_AIR &&
1072 n1b.getContent() == CONTENT_AIR)
1074 v3f pos = intToFloat(p1, BS);
1076 if(i == 0 || i == 1){
1077 actionstream<<"A dungeon master spawns at "
1078 <<PP(p1)<<std::endl;
1079 Settings properties;
1080 getMob_dungeon_master(properties);
1081 ServerActiveObject *obj = new MobV2SAO(
1082 this, pos, &properties);
1083 addActiveObject(obj);
1084 } else if(i == 2 || i == 3){
1085 actionstream<<"Rats spawn at "
1086 <<PP(p1)<<std::endl;
1087 for(int j=0; j<3; j++){
1088 ServerActiveObject *obj = new RatSAO(
1090 addActiveObject(obj);
1093 actionstream<<"An oerkki spawns at "
1094 <<PP(p1)<<std::endl;
1095 ServerActiveObject *obj = new Oerkki1SAO(
1097 addActiveObject(obj);
1104 Make trees from saplings!
1106 if(n.getContent() == CONTENT_SAPLING)
1108 if(myrand()%50 == 0)
1110 actionstream<<"A sapling grows into a tree at "
1113 core::map<v3s16, MapBlock*> modified_blocks;
1115 ManualMapVoxelManipulator vmanip(m_map);
1116 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1117 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1118 bool is_apple_tree = myrand()%4 == 0;
1119 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1120 vmanip.blitBackAll(&modified_blocks);
1123 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1124 for(core::map<v3s16, MapBlock*>::Iterator
1125 i = modified_blocks.getIterator();
1126 i.atEnd() == false; i++)
1128 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1130 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1132 // Send a MEET_OTHER event
1134 event.type = MEET_OTHER;
1135 for(core::map<v3s16, MapBlock*>::Iterator
1136 i = modified_blocks.getIterator();
1137 i.atEnd() == false; i++)
1139 v3s16 p = i.getNode()->getKey();
1140 event.modified_blocks.insert(p, true);
1142 m_map->dispatchEvent(&event);
1150 Step script environment (run global on_step())
1152 scriptapi_environment_step(m_lua, dtime);
1158 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1159 //TimeTaker timer("Step active objects");
1161 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1163 // This helps the objects to send data at the same time
1164 bool send_recommended = false;
1165 m_send_recommended_timer += dtime;
1166 if(m_send_recommended_timer > getSendRecommendedInterval())
1168 m_send_recommended_timer -= getSendRecommendedInterval();
1169 send_recommended = true;
1172 for(core::map<u16, ServerActiveObject*>::Iterator
1173 i = m_active_objects.getIterator();
1174 i.atEnd()==false; i++)
1176 ServerActiveObject* obj = i.getNode()->getValue();
1177 // Remove non-peaceful mobs on peaceful mode
1178 if(g_settings->getBool("only_peaceful_mobs")){
1179 if(!obj->isPeaceful())
1180 obj->m_removed = true;
1182 // Don't step if is to be removed or stored statically
1183 if(obj->m_removed || obj->m_pending_deactivation)
1186 obj->step(dtime, send_recommended);
1187 // Read messages from object
1188 while(obj->m_messages_out.size() > 0)
1190 m_active_object_messages.push_back(
1191 obj->m_messages_out.pop_front());
1197 Manage active objects
1199 if(m_object_management_interval.step(dtime, 0.5))
1201 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1203 Remove objects that satisfy (m_removed && m_known_by_count==0)
1205 removeRemovedObjects();
1208 if(g_settings->getBool("enable_experimental"))
1215 m_random_spawn_timer -= dtime;
1216 if(m_random_spawn_timer < 0)
1218 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1219 //m_random_spawn_timer += 2.0;
1220 m_random_spawn_timer += 200.0;
1226 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1227 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1228 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1230 Player *player = getRandomConnectedPlayer();
1233 pos = player->getPosition();
1235 myrand_range(-3,3)*BS,
1237 myrand_range(-3,3)*BS
1241 Create a ServerActiveObject
1244 //TestSAO *obj = new TestSAO(this, pos);
1245 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1246 //ServerActiveObject *obj = new RatSAO(this, pos);
1247 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1248 //ServerActiveObject *obj = new FireflySAO(this, pos);
1250 infostream<<"Server: Spawning MobV2SAO at "
1251 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1253 Settings properties;
1254 getMob_dungeon_master(properties);
1255 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1256 addActiveObject(obj);
1260 } // enable_experimental
1263 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1265 core::map<u16, ServerActiveObject*>::Node *n;
1266 n = m_active_objects.find(id);
1269 return n->getValue();
1272 bool isFreeServerActiveObjectId(u16 id,
1273 core::map<u16, ServerActiveObject*> &objects)
1278 for(core::map<u16, ServerActiveObject*>::Iterator
1279 i = objects.getIterator();
1280 i.atEnd()==false; i++)
1282 if(i.getNode()->getKey() == id)
1288 u16 getFreeServerActiveObjectId(
1289 core::map<u16, ServerActiveObject*> &objects)
1294 if(isFreeServerActiveObjectId(new_id, objects))
1304 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1307 u16 id = addActiveObjectRaw(object, true);
1311 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1315 v3f objectpos = obj->getBasePosition();
1317 // The block in which the object resides in
1318 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1321 Update the static data
1324 // Create new static object
1325 std::string staticdata = obj->getStaticData();
1326 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1327 // Add to the block where the object is located in
1328 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1329 // Get or generate the block
1330 MapBlock *block = m_map->emergeBlock(blockpos);
1332 bool succeeded = false;
1336 block->m_static_objects.insert(0, s_obj);
1337 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1341 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1342 <<"Could not find or generate "
1343 <<"a block for storing static object"<<std::endl;
1353 Finds out what new objects have been added to
1354 inside a radius around a position
1356 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1357 core::map<u16, bool> ¤t_objects,
1358 core::map<u16, bool> &added_objects)
1360 v3f pos_f = intToFloat(pos, BS);
1361 f32 radius_f = radius * BS;
1363 Go through the object list,
1364 - discard m_removed objects,
1365 - discard objects that are too far away,
1366 - discard objects that are found in current_objects.
1367 - add remaining objects to added_objects
1369 for(core::map<u16, ServerActiveObject*>::Iterator
1370 i = m_active_objects.getIterator();
1371 i.atEnd()==false; i++)
1373 u16 id = i.getNode()->getKey();
1375 ServerActiveObject *object = i.getNode()->getValue();
1378 // Discard if removed
1379 if(object->m_removed)
1381 // Discard if too far
1382 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1383 if(distance_f > radius_f)
1385 // Discard if already on current_objects
1386 core::map<u16, bool>::Node *n;
1387 n = current_objects.find(id);
1390 // Add to added_objects
1391 added_objects.insert(id, false);
1396 Finds out what objects have been removed from
1397 inside a radius around a position
1399 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1400 core::map<u16, bool> ¤t_objects,
1401 core::map<u16, bool> &removed_objects)
1403 v3f pos_f = intToFloat(pos, BS);
1404 f32 radius_f = radius * BS;
1406 Go through current_objects; object is removed if:
1407 - object is not found in m_active_objects (this is actually an
1408 error condition; objects should be set m_removed=true and removed
1409 only after all clients have been informed about removal), or
1410 - object has m_removed=true, or
1411 - object is too far away
1413 for(core::map<u16, bool>::Iterator
1414 i = current_objects.getIterator();
1415 i.atEnd()==false; i++)
1417 u16 id = i.getNode()->getKey();
1418 ServerActiveObject *object = getActiveObject(id);
1421 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1422 <<" object in current_objects is NULL"<<std::endl;
1424 else if(object->m_removed == false)
1426 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1427 /*infostream<<"removed == false"
1428 <<"distance_f = "<<distance_f
1429 <<", radius_f = "<<radius_f<<std::endl;*/
1430 if(distance_f < radius_f)
1436 removed_objects.insert(id, false);
1440 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1442 if(m_active_object_messages.size() == 0)
1443 return ActiveObjectMessage(0);
1445 return m_active_object_messages.pop_front();
1449 ************ Private methods *************
1452 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1456 if(object->getId() == 0){
1457 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1460 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1461 <<"no free ids available"<<std::endl;
1465 object->setId(new_id);
1468 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"supplied with id "<<object->getId()<<std::endl;
1471 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1473 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1478 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1479 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1481 m_active_objects.insert(object->getId(), object);
1483 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"Added id="<<object->getId()<<"; there are now "
1485 <<m_active_objects.size()<<" active objects."
1488 // Add static object to active static list of the block
1489 v3f objectpos = object->getBasePosition();
1490 std::string staticdata = object->getStaticData();
1491 StaticObject s_obj(object->getType(), objectpos, staticdata);
1492 // Add to the block where the object is located in
1493 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1494 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1497 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1498 object->m_static_exists = true;
1499 object->m_static_block = blockpos;
1502 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1505 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1506 <<"could not find block for storing id="<<object->getId()
1507 <<" statically"<<std::endl;
1510 // Register reference in scripting api (must be done before post-init)
1511 scriptapi_add_object_reference(m_lua, object);
1512 // Post-initialize object
1513 object->addedToEnvironment();
1515 return object->getId();
1519 Remove objects that satisfy (m_removed && m_known_by_count==0)
1521 void ServerEnvironment::removeRemovedObjects()
1523 core::list<u16> objects_to_remove;
1524 for(core::map<u16, ServerActiveObject*>::Iterator
1525 i = m_active_objects.getIterator();
1526 i.atEnd()==false; i++)
1528 u16 id = i.getNode()->getKey();
1529 ServerActiveObject* obj = i.getNode()->getValue();
1530 // This shouldn't happen but check it
1533 infostream<<"NULL object found in ServerEnvironment"
1534 <<" while finding removed objects. id="<<id<<std::endl;
1535 // Id to be removed from m_active_objects
1536 objects_to_remove.push_back(id);
1541 We will delete objects that are marked as removed or thatare
1542 waiting for deletion after deactivation
1544 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1548 Delete static data from block if is marked as removed
1550 if(obj->m_static_exists && obj->m_removed)
1552 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1555 block->m_static_objects.remove(id);
1556 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1557 obj->m_static_exists = false;
1561 // If m_known_by_count > 0, don't actually remove.
1562 if(obj->m_known_by_count > 0)
1565 // Deregister in scripting api
1566 scriptapi_rm_object_reference(m_lua, obj);
1570 // Id to be removed from m_active_objects
1571 objects_to_remove.push_back(id);
1573 // Remove references from m_active_objects
1574 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1575 i != objects_to_remove.end(); i++)
1577 m_active_objects.remove(*i);
1581 static void print_hexdump(std::ostream &o, const std::string &data)
1583 const int linelength = 16;
1584 for(int l=0; ; l++){
1585 int i0 = linelength * l;
1586 bool at_end = false;
1587 int thislinelength = linelength;
1588 if(i0 + thislinelength > (int)data.size()){
1589 thislinelength = data.size() - i0;
1592 for(int di=0; di<linelength; di++){
1595 if(di<thislinelength)
1596 snprintf(buf, 4, "%.2x ", data[i]);
1598 snprintf(buf, 4, " ");
1602 for(int di=0; di<thislinelength; di++){
1616 Convert stored objects from blocks near the players to active.
1618 void ServerEnvironment::activateObjects(MapBlock *block)
1622 // Ignore if no stored objects (to not set changed flag)
1623 if(block->m_static_objects.m_stored.size() == 0)
1625 verbosestream<<"ServerEnvironment::activateObjects(): "
1626 <<"activating objects of block "<<PP(block->getPos())
1627 <<" ("<<block->m_static_objects.m_stored.size()
1628 <<" objects)"<<std::endl;
1629 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1631 errorstream<<"suspiciously large amount of objects detected: "
1632 <<block->m_static_objects.m_stored.size()<<" in "
1633 <<PP(block->getPos())
1634 <<"; removing all of them."<<std::endl;
1635 // Clear stored list
1636 block->m_static_objects.m_stored.clear();
1637 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1640 // A list for objects that couldn't be converted to static for some
1641 // reason. They will be stored back.
1642 core::list<StaticObject> new_stored;
1643 // Loop through stored static objects
1644 for(core::list<StaticObject>::Iterator
1645 i = block->m_static_objects.m_stored.begin();
1646 i != block->m_static_objects.m_stored.end(); i++)
1648 /*infostream<<"Server: Creating an active object from "
1649 <<"static data"<<std::endl;*/
1650 StaticObject &s_obj = *i;
1651 // Create an active object from the data
1652 ServerActiveObject *obj = ServerActiveObject::create
1653 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1654 // If couldn't create object, store static data back.
1657 errorstream<<"ServerEnvironment::activateObjects(): "
1658 <<"failed to create active object from static object "
1659 <<"in block "<<PP(s_obj.pos/BS)
1660 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1661 print_hexdump(verbosestream, s_obj.data);
1663 new_stored.push_back(s_obj);
1666 verbosestream<<"ServerEnvironment::activateObjects(): "
1667 <<"activated static object pos="<<PP(s_obj.pos/BS)
1668 <<" type="<<(int)s_obj.type<<std::endl;
1669 // This will also add the object to the active static list
1670 addActiveObjectRaw(obj, false);
1672 // Clear stored list
1673 block->m_static_objects.m_stored.clear();
1674 // Add leftover failed stuff to stored list
1675 for(core::list<StaticObject>::Iterator
1676 i = new_stored.begin();
1677 i != new_stored.end(); i++)
1679 StaticObject &s_obj = *i;
1680 block->m_static_objects.m_stored.push_back(s_obj);
1683 Note: Block hasn't really been modified here.
1684 The objects have just been activated and moved from the stored
1685 static list to the active static list.
1686 As such, the block is essentially the same.
1687 Thus, do not call block->setChangedFlag().
1688 Otherwise there would be a huge amount of unnecessary I/O.
1693 Convert objects that are not standing inside active blocks to static.
1695 If m_known_by_count != 0, active object is not deleted, but static
1696 data is still updated.
1698 If force_delete is set, active object is deleted nevertheless. It
1699 shall only be set so in the destructor of the environment.
1701 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1703 core::list<u16> objects_to_remove;
1704 for(core::map<u16, ServerActiveObject*>::Iterator
1705 i = m_active_objects.getIterator();
1706 i.atEnd()==false; i++)
1708 ServerActiveObject* obj = i.getNode()->getValue();
1710 // This shouldn't happen but check it
1713 errorstream<<"NULL object found in ServerEnvironment"
1719 // If pending deactivation, let removeRemovedObjects() do it
1720 if(obj->m_pending_deactivation)
1723 u16 id = i.getNode()->getKey();
1724 v3f objectpos = obj->getBasePosition();
1726 // The block in which the object resides in
1727 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1729 // If block is active, don't remove
1730 if(m_active_blocks.contains(blockpos_o))
1733 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1734 <<"deactivating object id="<<id<<" on inactive block "
1735 <<PP(blockpos_o)<<std::endl;
1737 // If known by some client, don't immediately delete.
1738 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1741 Update the static data
1744 // Create new static object
1745 std::string staticdata_new = obj->getStaticData();
1746 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1748 bool stays_in_same_block = false;
1749 bool data_changed = true;
1751 if(obj->m_static_exists){
1752 if(obj->m_static_block == blockpos_o)
1753 stays_in_same_block = true;
1755 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1757 core::map<u16, StaticObject>::Node *n =
1758 block->m_static_objects.m_active.find(id);
1760 StaticObject static_old = n->getValue();
1762 float save_movem = obj->getMinimumSavedMovement();
1764 if(static_old.data == staticdata_new &&
1765 (static_old.pos - objectpos).getLength() < save_movem)
1766 data_changed = false;
1768 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1769 <<"id="<<id<<" m_static_exists=true but "
1770 <<"static data doesn't actually exist in "
1771 <<PP(obj->m_static_block)<<std::endl;
1775 bool shall_be_written = (!stays_in_same_block || data_changed);
1777 // Delete old static object
1778 if(obj->m_static_exists)
1780 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1783 block->m_static_objects.remove(id);
1784 obj->m_static_exists = false;
1785 // Only mark block as modified if data changed considerably
1786 if(shall_be_written)
1787 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1791 // Add to the block where the object is located in
1792 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1793 // Get or generate the block
1794 MapBlock *block = m_map->emergeBlock(blockpos);
1798 if(block->m_static_objects.m_stored.size() >= 49){
1799 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1800 <<" statically but block "<<PP(blockpos)
1801 <<" already contains "
1802 <<block->m_static_objects.m_stored.size()
1803 <<" (over 49) objects."
1804 <<" Forcing delete."<<std::endl;
1805 force_delete = true;
1807 u16 new_id = pending_delete ? id : 0;
1808 block->m_static_objects.insert(new_id, s_obj);
1810 // Only mark block as modified if data changed considerably
1811 if(shall_be_written)
1812 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1814 obj->m_static_exists = true;
1815 obj->m_static_block = block->getPos();
1819 errorstream<<"ServerEnv: Could not find or generate "
1820 <<"a block for storing id="<<obj->getId()
1821 <<" statically"<<std::endl;
1826 If known by some client, set pending deactivation.
1827 Otherwise delete it immediately.
1832 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1833 <<"object id="<<id<<" is known by clients"
1834 <<"; not deleting yet"<<std::endl;
1836 obj->m_pending_deactivation = true;
1840 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1841 <<"object id="<<id<<" is not known by clients"
1842 <<"; deleting"<<std::endl;
1844 // Deregister in scripting api
1845 scriptapi_rm_object_reference(m_lua, obj);
1847 // Delete active object
1849 // Id to be removed from m_active_objects
1850 objects_to_remove.push_back(id);
1853 // Remove references from m_active_objects
1854 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1855 i != objects_to_remove.end(); i++)
1857 m_active_objects.remove(*i);
1868 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1869 ITextureSource *texturesource, IGameDef *gamedef):
1872 m_texturesource(texturesource),
1879 ClientEnvironment::~ClientEnvironment()
1881 // delete active objects
1882 for(core::map<u16, ClientActiveObject*>::Iterator
1883 i = m_active_objects.getIterator();
1884 i.atEnd()==false; i++)
1886 delete i.getNode()->getValue();
1893 void ClientEnvironment::addPlayer(Player *player)
1895 DSTACK(__FUNCTION_NAME);
1897 It is a failure if player is local and there already is a local
1900 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1902 Environment::addPlayer(player);
1905 LocalPlayer * ClientEnvironment::getLocalPlayer()
1907 for(core::list<Player*>::Iterator i = m_players.begin();
1908 i != m_players.end(); i++)
1910 Player *player = *i;
1911 if(player->isLocal())
1912 return (LocalPlayer*)player;
1917 void ClientEnvironment::step(float dtime)
1919 DSTACK(__FUNCTION_NAME);
1921 // Get some settings
1922 bool free_move = g_settings->getBool("free_move");
1923 bool footprints = g_settings->getBool("footprints");
1926 LocalPlayer *lplayer = getLocalPlayer();
1928 // collision info queue
1929 core::list<CollisionInfo> player_collisions;
1932 Get the speed the player is going
1934 bool is_climbing = lplayer->is_climbing;
1936 f32 player_speed = 0.001; // just some small value
1937 player_speed = lplayer->getSpeed().getLength();
1940 Maximum position increment
1942 //f32 position_max_increment = 0.05*BS;
1943 f32 position_max_increment = 0.1*BS;
1945 // Maximum time increment (for collision detection etc)
1946 // time = distance / speed
1947 f32 dtime_max_increment = position_max_increment / player_speed;
1949 // Maximum time increment is 10ms or lower
1950 if(dtime_max_increment > 0.01)
1951 dtime_max_increment = 0.01;
1953 // Don't allow overly huge dtime
1957 f32 dtime_downcount = dtime;
1960 Stuff that has a maximum time increment
1969 if(dtime_downcount > dtime_max_increment)
1971 dtime_part = dtime_max_increment;
1972 dtime_downcount -= dtime_part;
1976 dtime_part = dtime_downcount;
1978 Setting this to 0 (no -=dtime_part) disables an infinite loop
1979 when dtime_part is so small that dtime_downcount -= dtime_part
1982 dtime_downcount = 0;
1990 v3f lplayerpos = lplayer->getPosition();
1993 if(free_move == false && is_climbing == false)
1996 v3f speed = lplayer->getSpeed();
1997 if(lplayer->swimming_up == false)
1998 speed.Y -= 9.81 * BS * dtime_part * 2;
2001 if(lplayer->in_water_stable || lplayer->in_water)
2003 f32 max_down = 2.0*BS;
2004 if(speed.Y < -max_down) speed.Y = -max_down;
2007 if(speed.getLength() > max)
2009 speed = speed / speed.getLength() * max;
2013 lplayer->setSpeed(speed);
2018 This also does collision detection.
2020 lplayer->move(dtime_part, *m_map, position_max_increment,
2021 &player_collisions);
2024 while(dtime_downcount > 0.001);
2026 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2028 for(core::list<CollisionInfo>::Iterator
2029 i = player_collisions.begin();
2030 i != player_collisions.end(); i++)
2032 CollisionInfo &info = *i;
2033 if(info.t == COLLISION_FALL)
2035 //f32 tolerance = BS*10; // 2 without damage
2036 f32 tolerance = BS*12; // 3 without damage
2038 if(info.speed > tolerance)
2040 f32 damage_f = (info.speed - tolerance)/BS*factor;
2041 u16 damage = (u16)(damage_f+0.5);
2042 if(lplayer->hp > damage)
2043 lplayer->hp -= damage;
2047 ClientEnvEvent event;
2048 event.type = CEE_PLAYER_DAMAGE;
2049 event.player_damage.amount = damage;
2050 m_client_event_queue.push_back(event);
2056 A quick draft of lava damage
2058 if(m_lava_hurt_interval.step(dtime, 1.0))
2060 v3f pf = lplayer->getPosition();
2062 // Feet, middle and head
2063 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2064 MapNode n1 = m_map->getNodeNoEx(p1);
2065 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2066 MapNode n2 = m_map->getNodeNoEx(p2);
2067 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2068 MapNode n3 = m_map->getNodeNoEx(p2);
2070 u32 damage_per_second = 0;
2071 damage_per_second = MYMAX(damage_per_second,
2072 content_features(n1).damage_per_second);
2073 damage_per_second = MYMAX(damage_per_second,
2074 content_features(n2).damage_per_second);
2075 damage_per_second = MYMAX(damage_per_second,
2076 content_features(n3).damage_per_second);
2078 if(damage_per_second != 0)
2080 ClientEnvEvent event;
2081 event.type = CEE_PLAYER_DAMAGE;
2082 event.player_damage.amount = damage_per_second;
2083 m_client_event_queue.push_back(event);
2088 Stuff that can be done in an arbitarily large dtime
2090 for(core::list<Player*>::Iterator i = m_players.begin();
2091 i != m_players.end(); i++)
2093 Player *player = *i;
2094 v3f playerpos = player->getPosition();
2097 Handle non-local players
2099 if(player->isLocal() == false)
2102 player->move(dtime, *m_map, 100*BS);
2106 // Update lighting on all players on client
2107 u8 light = LIGHT_MAX;
2110 v3s16 p = player->getLightPosition();
2111 MapNode n = m_map->getNode(p);
2112 light = n.getLightBlend(getDayNightRatio());
2114 catch(InvalidPositionException &e) {}
2115 player->updateLight(light);
2118 Add footsteps to grass
2122 // Get node that is at BS/4 under player
2123 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2125 MapNode n = m_map->getNode(bottompos);
2126 if(n.getContent() == CONTENT_GRASS)
2128 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2129 m_map->setNode(bottompos, n);
2130 // Update mesh on client
2131 if(m_map->mapType() == MAPTYPE_CLIENT)
2133 v3s16 p_blocks = getNodeBlockPos(bottompos);
2134 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2135 //b->updateMesh(getDayNightRatio());
2136 b->setMeshExpired(true);
2140 catch(InvalidPositionException &e)
2147 Step active objects and update lighting of them
2150 for(core::map<u16, ClientActiveObject*>::Iterator
2151 i = m_active_objects.getIterator();
2152 i.atEnd()==false; i++)
2154 ClientActiveObject* obj = i.getNode()->getValue();
2156 obj->step(dtime, this);
2158 if(m_active_object_light_update_interval.step(dtime, 0.21))
2161 //u8 light = LIGHT_MAX;
2165 v3s16 p = obj->getLightPosition();
2166 MapNode n = m_map->getNode(p);
2167 light = n.getLightBlend(getDayNightRatio());
2169 catch(InvalidPositionException &e) {}
2170 obj->updateLight(light);
2175 void ClientEnvironment::updateMeshes(v3s16 blockpos, ITextureSource *tsrc)
2177 m_map->updateMeshes(blockpos, getDayNightRatio(), tsrc);
2180 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2182 m_map->expireMeshes(only_daynight_diffed);
2185 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2187 core::map<u16, ClientActiveObject*>::Node *n;
2188 n = m_active_objects.find(id);
2191 return n->getValue();
2194 bool isFreeClientActiveObjectId(u16 id,
2195 core::map<u16, ClientActiveObject*> &objects)
2200 for(core::map<u16, ClientActiveObject*>::Iterator
2201 i = objects.getIterator();
2202 i.atEnd()==false; i++)
2204 if(i.getNode()->getKey() == id)
2210 u16 getFreeClientActiveObjectId(
2211 core::map<u16, ClientActiveObject*> &objects)
2216 if(isFreeClientActiveObjectId(new_id, objects))
2226 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2229 if(object->getId() == 0)
2231 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2234 infostream<<"ClientEnvironment::addActiveObject(): "
2235 <<"no free ids available"<<std::endl;
2239 object->setId(new_id);
2241 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2243 infostream<<"ClientEnvironment::addActiveObject(): "
2244 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2248 infostream<<"ClientEnvironment::addActiveObject(): "
2249 <<"added (id="<<object->getId()<<")"<<std::endl;
2250 m_active_objects.insert(object->getId(), object);
2251 // TODO: Make g_texturesource non-global
2252 object->addToScene(m_smgr, m_texturesource);
2253 return object->getId();
2256 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2257 const std::string &init_data)
2259 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2262 infostream<<"ClientEnvironment::addActiveObject(): "
2263 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2270 obj->initialize(init_data);
2272 addActiveObject(obj);
2275 void ClientEnvironment::removeActiveObject(u16 id)
2277 infostream<<"ClientEnvironment::removeActiveObject(): "
2278 <<"id="<<id<<std::endl;
2279 ClientActiveObject* obj = getActiveObject(id);
2282 infostream<<"ClientEnvironment::removeActiveObject(): "
2283 <<"id="<<id<<" not found"<<std::endl;
2286 obj->removeFromScene();
2288 m_active_objects.remove(id);
2291 void ClientEnvironment::processActiveObjectMessage(u16 id,
2292 const std::string &data)
2294 ClientActiveObject* obj = getActiveObject(id);
2297 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2298 <<" got message for id="<<id<<", which doesn't exist."
2302 obj->processMessage(data);
2306 Callbacks for activeobjects
2309 void ClientEnvironment::damageLocalPlayer(u8 damage)
2311 LocalPlayer *lplayer = getLocalPlayer();
2314 if(lplayer->hp > damage)
2315 lplayer->hp -= damage;
2319 ClientEnvEvent event;
2320 event.type = CEE_PLAYER_DAMAGE;
2321 event.player_damage.amount = damage;
2322 m_client_event_queue.push_back(event);
2326 Client likes to call these
2329 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2330 core::array<DistanceSortedActiveObject> &dest)
2332 for(core::map<u16, ClientActiveObject*>::Iterator
2333 i = m_active_objects.getIterator();
2334 i.atEnd()==false; i++)
2336 ClientActiveObject* obj = i.getNode()->getValue();
2338 f32 d = (obj->getPosition() - origin).getLength();
2343 DistanceSortedActiveObject dso(obj, d);
2345 dest.push_back(dso);
2349 ClientEnvEvent ClientEnvironment::getClientEvent()
2351 if(m_client_event_queue.size() == 0)
2353 ClientEnvEvent event;
2354 event.type = CEE_NONE;
2357 return m_client_event_queue.pop_front();
2360 #endif // #ifndef SERVER