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"
36 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
38 Environment::Environment():
43 Environment::~Environment()
46 for(core::list<Player*>::Iterator i = m_players.begin();
47 i != m_players.end(); i++)
53 void Environment::addPlayer(Player *player)
55 DSTACK(__FUNCTION_NAME);
57 Check that peer_ids are unique.
58 Also check that names are unique.
59 Exception: there can be multiple players with peer_id=0
61 // If peer id is non-zero, it has to be unique.
62 if(player->peer_id != 0)
63 assert(getPlayer(player->peer_id) == NULL);
64 // Name has to be unique.
65 assert(getPlayer(player->getName()) == NULL);
67 m_players.push_back(player);
70 void Environment::removePlayer(u16 peer_id)
72 DSTACK(__FUNCTION_NAME);
74 for(core::list<Player*>::Iterator i = m_players.begin();
75 i != m_players.end(); i++)
78 if(player->peer_id != peer_id)
83 // See if there is an another one
84 // (shouldn't be, but just to be sure)
89 Player * Environment::getPlayer(u16 peer_id)
91 for(core::list<Player*>::Iterator i = m_players.begin();
92 i != m_players.end(); i++)
95 if(player->peer_id == peer_id)
101 Player * Environment::getPlayer(const char *name)
103 for(core::list<Player*>::Iterator i = m_players.begin();
104 i != m_players.end(); i++)
107 if(strcmp(player->getName(), name) == 0)
113 Player * Environment::getRandomConnectedPlayer()
115 core::list<Player*> connected_players = getPlayers(true);
116 u32 chosen_one = myrand() % connected_players.size();
118 for(core::list<Player*>::Iterator
119 i = connected_players.begin();
120 i != connected_players.end(); i++)
132 Player * Environment::getNearestConnectedPlayer(v3f pos)
134 core::list<Player*> connected_players = getPlayers(true);
136 Player *nearest_player = NULL;
137 for(core::list<Player*>::Iterator
138 i = connected_players.begin();
139 i != connected_players.end(); i++)
142 f32 d = player->getPosition().getDistanceFrom(pos);
143 if(d < nearest_d || nearest_player == NULL)
146 nearest_player = player;
149 return nearest_player;
152 core::list<Player*> Environment::getPlayers()
157 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
159 core::list<Player*> newlist;
160 for(core::list<Player*>::Iterator
161 i = m_players.begin();
162 i != m_players.end(); i++)
166 if(ignore_disconnected)
168 // Ignore disconnected players
169 if(player->peer_id == 0)
173 newlist.push_back(player);
178 void Environment::printPlayers(std::ostream &o)
180 o<<"Players in environment:"<<std::endl;
181 for(core::list<Player*>::Iterator i = m_players.begin();
182 i != m_players.end(); i++)
185 o<<"Player peer_id="<<player->peer_id<<std::endl;
189 /*void Environment::setDayNightRatio(u32 r)
191 getDayNightRatio() = r;
194 u32 Environment::getDayNightRatio()
196 //return getDayNightRatio();
197 return time_to_daynight_ratio(m_time_of_day);
204 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
207 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
208 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
209 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
216 void ActiveBlockList::update(core::list<v3s16> &active_positions,
218 core::map<v3s16, bool> &blocks_removed,
219 core::map<v3s16, bool> &blocks_added)
224 core::map<v3s16, bool> newlist;
225 for(core::list<v3s16>::Iterator i = active_positions.begin();
226 i != active_positions.end(); i++)
228 fillRadiusBlock(*i, radius, newlist);
232 Find out which blocks on the old list are not on the new list
234 // Go through old list
235 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
236 i.atEnd()==false; i++)
238 v3s16 p = i.getNode()->getKey();
239 // If not on new list, it's been removed
240 if(newlist.find(p) == NULL)
241 blocks_removed.insert(p, true);
245 Find out which blocks on the new list are not on the old list
247 // Go through new list
248 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
249 i.atEnd()==false; i++)
251 v3s16 p = i.getNode()->getKey();
252 // If not on old list, it's been added
253 if(m_list.find(p) == NULL)
254 blocks_added.insert(p, true);
261 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
262 i.atEnd()==false; i++)
264 v3s16 p = i.getNode()->getKey();
265 m_list.insert(p, true);
273 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L):
276 m_random_spawn_timer(3),
277 m_send_recommended_timer(0),
279 m_game_time_fraction_counter(0)
283 ServerEnvironment::~ServerEnvironment()
285 // Clear active block list.
286 // This makes the next one delete all active objects.
287 m_active_blocks.clear();
289 // Convert all objects to static and delete the active objects
290 deactivateFarObjects(true);
296 void ServerEnvironment::serializePlayers(const std::string &savedir)
298 std::string players_path = savedir + "/players";
299 fs::CreateDir(players_path);
301 core::map<Player*, bool> saved_players;
303 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
304 for(u32 i=0; i<player_files.size(); i++)
306 if(player_files[i].dir)
309 // Full path to this file
310 std::string path = players_path + "/" + player_files[i].name;
312 //infostream<<"Checking player file "<<path<<std::endl;
314 // Load player to see what is its name
315 ServerRemotePlayer testplayer;
317 // Open file and deserialize
318 std::ifstream is(path.c_str(), std::ios_base::binary);
319 if(is.good() == false)
321 infostream<<"Failed to read "<<path<<std::endl;
324 testplayer.deSerialize(is);
327 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
329 // Search for the player
330 std::string playername = testplayer.getName();
331 Player *player = getPlayer(playername.c_str());
334 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
338 //infostream<<"Found matching player, overwriting."<<std::endl;
340 // OK, found. Save player there.
342 // Open file and serialize
343 std::ofstream os(path.c_str(), std::ios_base::binary);
344 if(os.good() == false)
346 infostream<<"Failed to overwrite "<<path<<std::endl;
349 player->serialize(os);
350 saved_players.insert(player, true);
354 for(core::list<Player*>::Iterator i = m_players.begin();
355 i != m_players.end(); i++)
358 if(saved_players.find(player) != NULL)
360 /*infostream<<"Player "<<player->getName()
361 <<" was already saved."<<std::endl;*/
364 std::string playername = player->getName();
365 // Don't save unnamed player
368 //infostream<<"Not saving unnamed player."<<std::endl;
374 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
375 playername = "player";
376 std::string path = players_path + "/" + playername;
378 for(u32 i=0; i<1000; i++)
380 if(fs::PathExists(path) == false)
385 path = players_path + "/" + playername + itos(i);
389 infostream<<"Didn't find free file for player"<<std::endl;
394 /*infostream<<"Saving player "<<player->getName()<<" to "
396 // Open file and serialize
397 std::ofstream os(path.c_str(), std::ios_base::binary);
398 if(os.good() == false)
400 infostream<<"Failed to overwrite "<<path<<std::endl;
403 player->serialize(os);
404 saved_players.insert(player, true);
408 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
411 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
413 std::string players_path = savedir + "/players";
415 core::map<Player*, bool> saved_players;
417 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
418 for(u32 i=0; i<player_files.size(); i++)
420 if(player_files[i].dir)
423 // Full path to this file
424 std::string path = players_path + "/" + player_files[i].name;
426 infostream<<"Checking player file "<<path<<std::endl;
428 // Load player to see what is its name
429 ServerRemotePlayer testplayer;
431 // Open file and deserialize
432 std::ifstream is(path.c_str(), std::ios_base::binary);
433 if(is.good() == false)
435 infostream<<"Failed to read "<<path<<std::endl;
438 testplayer.deSerialize(is);
441 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
443 infostream<<"Not loading player with invalid name: "
444 <<testplayer.getName()<<std::endl;
447 infostream<<"Loaded test player with name "<<testplayer.getName()
450 // Search for the player
451 std::string playername = testplayer.getName();
452 Player *player = getPlayer(playername.c_str());
453 bool newplayer = false;
456 infostream<<"Is a new player"<<std::endl;
457 player = new ServerRemotePlayer();
463 infostream<<"Reading player "<<testplayer.getName()<<" from "
465 // Open file and deserialize
466 std::ifstream is(path.c_str(), std::ios_base::binary);
467 if(is.good() == false)
469 infostream<<"Failed to read "<<path<<std::endl;
472 player->deSerialize(is);
480 void ServerEnvironment::saveMeta(const std::string &savedir)
482 std::string path = savedir + "/env_meta.txt";
484 // Open file and serialize
485 std::ofstream os(path.c_str(), std::ios_base::binary);
486 if(os.good() == false)
488 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
490 throw SerializationError("Couldn't save env meta");
494 args.setU64("game_time", m_game_time);
495 args.setU64("time_of_day", getTimeOfDay());
500 void ServerEnvironment::loadMeta(const std::string &savedir)
502 std::string path = savedir + "/env_meta.txt";
504 // Open file and deserialize
505 std::ifstream is(path.c_str(), std::ios_base::binary);
506 if(is.good() == false)
508 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
510 throw SerializationError("Couldn't load env meta");
518 throw SerializationError
519 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
521 std::getline(is, line);
522 std::string trimmedline = trim(line);
523 if(trimmedline == "EnvArgsEnd")
525 args.parseConfigLine(line);
529 m_game_time = args.getU64("game_time");
530 }catch(SettingNotFoundException &e){
531 // Getting this is crucial, otherwise timestamps are useless
532 throw SerializationError("Couldn't load env meta game_time");
536 m_time_of_day = args.getU64("time_of_day");
537 }catch(SettingNotFoundException &e){
538 // This is not as important
539 m_time_of_day = 9000;
544 // This is probably very useless
545 void spawnRandomObjects(MapBlock *block)
547 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
548 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
550 bool last_node_walkable = false;
551 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
554 MapNode n = block->getNodeNoEx(p);
555 if(n.getContent() == CONTENT_IGNORE)
557 if(content_features(n).liquid_type != LIQUID_NONE)
559 if(content_features(n).walkable)
561 last_node_walkable = true;
564 if(last_node_walkable)
566 // If block contains light information
567 if(content_features(n).param_type == CPT_LIGHT)
569 if(n.getLight(LIGHTBANK_DAY) <= 5)
571 if(myrand() % 1000 == 0)
573 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
575 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
576 std::string data = obj->getStaticData();
577 StaticObject s_obj(obj->getType(),
578 obj->getBasePosition(), data);
580 block->m_static_objects.insert(0, s_obj);
582 block->setChangedFlag();
587 last_node_walkable = false;
593 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
595 // Get time difference
597 u32 stamp = block->getTimestamp();
598 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
599 dtime_s = m_game_time - block->getTimestamp();
600 dtime_s += additional_dtime;
602 // Set current time as timestamp (and let it set ChangedFlag)
603 block->setTimestamp(m_game_time);
605 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
607 // Activate stored objects
608 activateObjects(block);
611 bool changed = block->m_node_metadata->step((float)dtime_s);
615 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
616 event.p = block->getPos();
617 m_map->dispatchEvent(&event);
619 block->setChangedFlag();
622 // TODO: Do something
623 // TODO: Implement usage of ActiveBlockModifier
625 // Here's a quick demonstration
627 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
628 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
629 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
631 v3s16 p = p0 + block->getPosRelative();
632 MapNode n = block->getNodeNoEx(p0);
635 // Convert all mud under proper day lighting to grass
636 if(n.getContent() == CONTENT_MUD)
640 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
641 if(content_features(n_top).air_equivalent &&
642 n_top.getLight(LIGHTBANK_DAY) >= 13)
644 n.setContent(CONTENT_GRASS);
645 m_map->addNodeWithEvent(p, n);
653 void ServerEnvironment::clearAllObjects()
655 infostream<<"ServerEnvironment::clearAllObjects(): "
656 <<"Removing all active objects"<<std::endl;
657 core::list<u16> objects_to_remove;
658 for(core::map<u16, ServerActiveObject*>::Iterator
659 i = m_active_objects.getIterator();
660 i.atEnd()==false; i++)
662 ServerActiveObject* obj = i.getNode()->getValue();
663 u16 id = i.getNode()->getKey();
664 v3f objectpos = obj->getBasePosition();
665 // Delete static object if block is loaded
666 if(obj->m_static_exists){
667 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
669 block->m_static_objects.remove(id);
670 block->raiseModified(MOD_STATE_WRITE_NEEDED);
671 obj->m_static_exists = false;
674 // If known by some client, don't delete immediately
675 if(obj->m_known_by_count > 0){
676 obj->m_pending_deactivation = true;
677 obj->m_removed = true;
680 // Deregister in scripting api
681 scriptapi_rm_object_reference(m_lua, obj);
682 // Delete active object
684 // Id to be removed from m_active_objects
685 objects_to_remove.push_back(id);
687 // Remove references from m_active_objects
688 for(core::list<u16>::Iterator i = objects_to_remove.begin();
689 i != objects_to_remove.end(); i++)
691 m_active_objects.remove(*i);
694 core::list<v3s16> loadable_blocks;
695 infostream<<"ServerEnvironment::clearAllObjects(): "
696 <<"Listing all loadable blocks"<<std::endl;
697 m_map->listAllLoadableBlocks(loadable_blocks);
698 infostream<<"ServerEnvironment::clearAllObjects(): "
699 <<"Done listing all loadable blocks: "
700 <<loadable_blocks.size()
701 <<", now clearing"<<std::endl;
702 u32 report_interval = loadable_blocks.size() / 10;
703 u32 num_blocks_checked = 0;
704 u32 num_blocks_cleared = 0;
705 u32 num_objs_cleared = 0;
706 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
707 i != loadable_blocks.end(); i++)
710 MapBlock *block = m_map->emergeBlock(p, false);
712 errorstream<<"ServerEnvironment::clearAllObjects(): "
713 <<"Failed to emerge block "<<PP(p)<<std::endl;
716 u32 num_stored = block->m_static_objects.m_stored.size();
717 u32 num_active = block->m_static_objects.m_active.size();
718 if(num_stored != 0 || num_active != 0){
719 block->m_static_objects.m_stored.clear();
720 block->m_static_objects.m_active.clear();
721 block->raiseModified(MOD_STATE_WRITE_NEEDED);
722 num_objs_cleared += num_stored + num_active;
723 num_blocks_cleared++;
725 num_blocks_checked++;
727 if(num_blocks_checked % report_interval == 0){
728 float percent = 100.0 * (float)num_blocks_checked /
729 loadable_blocks.size();
730 infostream<<"ServerEnvironment::clearAllObjects(): "
731 <<"Cleared "<<num_objs_cleared<<" objects"
732 <<" in "<<num_blocks_cleared<<" blocks ("
733 <<percent<<"%)"<<std::endl;
736 infostream<<"ServerEnvironment::clearAllObjects(): "
737 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
738 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
741 static void getMob_dungeon_master(Settings &properties)
743 properties.set("looks", "dungeon_master");
744 properties.setFloat("yaw", 1.57);
745 properties.setFloat("hp", 30);
746 properties.setBool("bright_shooting", true);
747 properties.set("shoot_type", "fireball");
748 properties.set("shoot_y", "0.7");
749 properties.set("player_hit_damage", "1");
750 properties.set("player_hit_distance", "1.0");
751 properties.set("player_hit_interval", "0.5");
752 properties.setBool("mindless_rage", myrand_range(0,100)==0);
755 void ServerEnvironment::step(float dtime)
757 DSTACK(__FUNCTION_NAME);
759 //TimeTaker timer("ServerEnv step");
762 bool footprints = g_settings->getBool("footprints");
768 m_game_time_fraction_counter += dtime;
769 u32 inc_i = (u32)m_game_time_fraction_counter;
770 m_game_time += inc_i;
771 m_game_time_fraction_counter -= (float)inc_i;
778 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
779 for(core::list<Player*>::Iterator i = m_players.begin();
780 i != m_players.end(); i++)
784 // Ignore disconnected players
785 if(player->peer_id == 0)
788 v3f playerpos = player->getPosition();
791 player->move(dtime, *m_map, 100*BS);
794 Add footsteps to grass
798 // Get node that is at BS/4 under player
799 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
801 MapNode n = m_map->getNode(bottompos);
802 if(n.getContent() == CONTENT_GRASS)
804 n.setContent(CONTENT_GRASS_FOOTSTEPS);
805 m_map->setNode(bottompos, n);
808 catch(InvalidPositionException &e)
816 Manage active block list
818 if(m_active_blocks_management_interval.step(dtime, 2.0))
820 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
822 Get player block positions
824 core::list<v3s16> players_blockpos;
825 for(core::list<Player*>::Iterator
826 i = m_players.begin();
827 i != m_players.end(); i++)
830 // Ignore disconnected players
831 if(player->peer_id == 0)
833 v3s16 blockpos = getNodeBlockPos(
834 floatToInt(player->getPosition(), BS));
835 players_blockpos.push_back(blockpos);
839 Update list of active blocks, collecting changes
841 const s16 active_block_range = g_settings->getS16("active_block_range");
842 core::map<v3s16, bool> blocks_removed;
843 core::map<v3s16, bool> blocks_added;
844 m_active_blocks.update(players_blockpos, active_block_range,
845 blocks_removed, blocks_added);
848 Handle removed blocks
851 // Convert active objects that are no more in active blocks to static
852 deactivateFarObjects(false);
854 for(core::map<v3s16, bool>::Iterator
855 i = blocks_removed.getIterator();
856 i.atEnd()==false; i++)
858 v3s16 p = i.getNode()->getKey();
860 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
861 <<") became inactive"<<std::endl;*/
863 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
867 // Set current time as timestamp (and let it set ChangedFlag)
868 block->setTimestamp(m_game_time);
875 for(core::map<v3s16, bool>::Iterator
876 i = blocks_added.getIterator();
877 i.atEnd()==false; i++)
879 v3s16 p = i.getNode()->getKey();
881 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
882 <<") became active"<<std::endl;*/
884 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
888 activateBlock(block);
893 Mess around in active blocks
895 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
897 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
901 for(core::map<v3s16, bool>::Iterator
902 i = m_active_blocks.m_list.getIterator();
903 i.atEnd()==false; i++)
905 v3s16 p = i.getNode()->getKey();
907 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
908 <<") being handled"<<std::endl;*/
910 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
914 // Reset block usage timer
915 block->resetUsageTimer();
917 // Set current time as timestamp
918 block->setTimestampNoChangedFlag(m_game_time);
921 bool changed = block->m_node_metadata->step(dtime);
925 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
927 m_map->dispatchEvent(&event);
929 block->setChangedFlag();
934 if(m_active_blocks_test_interval.step(dtime, 10.0))
936 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
937 //float dtime = 10.0;
939 for(core::map<v3s16, bool>::Iterator
940 i = m_active_blocks.m_list.getIterator();
941 i.atEnd()==false; i++)
943 v3s16 p = i.getNode()->getKey();
945 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
946 <<") being handled"<<std::endl;*/
948 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
952 // Set current time as timestamp
953 block->setTimestampNoChangedFlag(m_game_time);
958 Note that map modifications should be done using the event-
959 making map methods so that the server gets information
962 Reading can be done quickly directly from the block.
964 Everything should bind to inside this single content
965 searching loop to keep things fast.
967 // TODO: Implement usage of ActiveBlockModifier
969 // Find out how many objects the block contains
970 //u32 active_object_count = block->m_static_objects.m_active.size();
971 // Find out how many objects this and all the neighbors contain
972 u32 active_object_count_wider = 0;
973 for(s16 x=-1; x<=1; x++)
974 for(s16 y=-1; y<=1; y++)
975 for(s16 z=-1; z<=1; z++)
977 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
980 active_object_count_wider +=
981 block->m_static_objects.m_active.size()
982 + block->m_static_objects.m_stored.size();
984 /*if(block->m_static_objects.m_stored.size() != 0){
985 errorstream<<"ServerEnvironment::step(): "
986 <<PP(block->getPos())<<" contains "
987 <<block->m_static_objects.m_stored.size()
988 <<" stored objects; "
989 <<"when spawning objects, when counting active "
990 <<"objects in wide area. relative position: "
991 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
996 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
997 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
998 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1000 v3s16 p = p0 + block->getPosRelative();
1001 MapNode n = block->getNodeNoEx(p0);
1005 Convert mud under proper lighting to grass
1007 if(n.getContent() == CONTENT_MUD)
1009 if(myrand()%20 == 0)
1011 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1012 if(content_features(n_top).air_equivalent &&
1013 n_top.getLightBlend(getDayNightRatio()) >= 13)
1015 n.setContent(CONTENT_GRASS);
1016 m_map->addNodeWithEvent(p, n);
1021 Convert grass into mud if under something else than air
1023 if(n.getContent() == CONTENT_GRASS)
1025 //if(myrand()%20 == 0)
1027 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1028 if(content_features(n_top).air_equivalent == false)
1030 n.setContent(CONTENT_MUD);
1031 m_map->addNodeWithEvent(p, n);
1036 Rats spawn around regular trees
1038 if(n.getContent() == CONTENT_TREE ||
1039 n.getContent() == CONTENT_JUNGLETREE)
1041 if(myrand()%200 == 0 && active_object_count_wider == 0)
1043 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1044 0, myrand_range(-2, 2));
1045 MapNode n1 = m_map->getNodeNoEx(p1);
1046 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1047 if(n1b.getContent() == CONTENT_GRASS &&
1048 n1.getContent() == CONTENT_AIR)
1050 v3f pos = intToFloat(p1, BS);
1051 ServerActiveObject *obj = new RatSAO(this, pos);
1052 addActiveObject(obj);
1057 Fun things spawn in caves and dungeons
1059 if(n.getContent() == CONTENT_STONE ||
1060 n.getContent() == CONTENT_MOSSYCOBBLE)
1062 if(myrand()%200 == 0 && active_object_count_wider == 0)
1064 v3s16 p1 = p + v3s16(0,1,0);
1065 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1066 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
1067 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1068 if(n1a.getContent() == CONTENT_AIR &&
1069 n1b.getContent() == CONTENT_AIR)
1071 v3f pos = intToFloat(p1, BS);
1073 if(i == 0 || i == 1){
1074 actionstream<<"A dungeon master spawns at "
1075 <<PP(p1)<<std::endl;
1076 Settings properties;
1077 getMob_dungeon_master(properties);
1078 ServerActiveObject *obj = new MobV2SAO(
1079 this, pos, &properties);
1080 addActiveObject(obj);
1081 } else if(i == 2 || i == 3){
1082 actionstream<<"Rats spawn at "
1083 <<PP(p1)<<std::endl;
1084 for(int j=0; j<3; j++){
1085 ServerActiveObject *obj = new RatSAO(
1087 addActiveObject(obj);
1090 actionstream<<"An oerkki spawns at "
1091 <<PP(p1)<<std::endl;
1092 ServerActiveObject *obj = new Oerkki1SAO(
1094 addActiveObject(obj);
1101 Make trees from saplings!
1103 if(n.getContent() == CONTENT_SAPLING)
1105 if(myrand()%50 == 0)
1107 actionstream<<"A sapling grows into a tree at "
1110 core::map<v3s16, MapBlock*> modified_blocks;
1112 ManualMapVoxelManipulator vmanip(m_map);
1113 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1114 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1115 bool is_apple_tree = myrand()%4 == 0;
1116 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1117 vmanip.blitBackAll(&modified_blocks);
1120 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1121 for(core::map<v3s16, MapBlock*>::Iterator
1122 i = modified_blocks.getIterator();
1123 i.atEnd() == false; i++)
1125 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1127 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1129 // Send a MEET_OTHER event
1131 event.type = MEET_OTHER;
1132 for(core::map<v3s16, MapBlock*>::Iterator
1133 i = modified_blocks.getIterator();
1134 i.atEnd() == false; i++)
1136 v3s16 p = i.getNode()->getKey();
1137 event.modified_blocks.insert(p, true);
1139 m_map->dispatchEvent(&event);
1147 Step script environment (run global on_step())
1149 scriptapi_environment_step(m_lua, dtime);
1155 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1156 //TimeTaker timer("Step active objects");
1158 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1160 // This helps the objects to send data at the same time
1161 bool send_recommended = false;
1162 m_send_recommended_timer += dtime;
1163 if(m_send_recommended_timer > getSendRecommendedInterval())
1165 m_send_recommended_timer -= getSendRecommendedInterval();
1166 send_recommended = true;
1169 for(core::map<u16, ServerActiveObject*>::Iterator
1170 i = m_active_objects.getIterator();
1171 i.atEnd()==false; i++)
1173 ServerActiveObject* obj = i.getNode()->getValue();
1174 // Remove non-peaceful mobs on peaceful mode
1175 if(g_settings->getBool("only_peaceful_mobs")){
1176 if(!obj->isPeaceful())
1177 obj->m_removed = true;
1179 // Don't step if is to be removed or stored statically
1180 if(obj->m_removed || obj->m_pending_deactivation)
1183 obj->step(dtime, send_recommended);
1184 // Read messages from object
1185 while(obj->m_messages_out.size() > 0)
1187 m_active_object_messages.push_back(
1188 obj->m_messages_out.pop_front());
1194 Manage active objects
1196 if(m_object_management_interval.step(dtime, 0.5))
1198 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1200 Remove objects that satisfy (m_removed && m_known_by_count==0)
1202 removeRemovedObjects();
1205 if(g_settings->getBool("enable_experimental"))
1212 m_random_spawn_timer -= dtime;
1213 if(m_random_spawn_timer < 0)
1215 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1216 //m_random_spawn_timer += 2.0;
1217 m_random_spawn_timer += 200.0;
1223 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1224 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1225 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1227 Player *player = getRandomConnectedPlayer();
1230 pos = player->getPosition();
1232 myrand_range(-3,3)*BS,
1234 myrand_range(-3,3)*BS
1238 Create a ServerActiveObject
1241 //TestSAO *obj = new TestSAO(this, pos);
1242 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1243 //ServerActiveObject *obj = new RatSAO(this, pos);
1244 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1245 //ServerActiveObject *obj = new FireflySAO(this, pos);
1247 infostream<<"Server: Spawning MobV2SAO at "
1248 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1250 Settings properties;
1251 getMob_dungeon_master(properties);
1252 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1253 addActiveObject(obj);
1257 } // enable_experimental
1260 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1262 core::map<u16, ServerActiveObject*>::Node *n;
1263 n = m_active_objects.find(id);
1266 return n->getValue();
1269 bool isFreeServerActiveObjectId(u16 id,
1270 core::map<u16, ServerActiveObject*> &objects)
1275 for(core::map<u16, ServerActiveObject*>::Iterator
1276 i = objects.getIterator();
1277 i.atEnd()==false; i++)
1279 if(i.getNode()->getKey() == id)
1285 u16 getFreeServerActiveObjectId(
1286 core::map<u16, ServerActiveObject*> &objects)
1291 if(isFreeServerActiveObjectId(new_id, objects))
1301 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1304 u16 id = addActiveObjectRaw(object, true);
1308 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1312 v3f objectpos = obj->getBasePosition();
1314 // The block in which the object resides in
1315 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1318 Update the static data
1321 // Create new static object
1322 std::string staticdata = obj->getStaticData();
1323 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1324 // Add to the block where the object is located in
1325 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1326 // Get or generate the block
1327 MapBlock *block = m_map->emergeBlock(blockpos);
1329 bool succeeded = false;
1333 block->m_static_objects.insert(0, s_obj);
1334 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1338 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1339 <<"Could not find or generate "
1340 <<"a block for storing static object"<<std::endl;
1350 Finds out what new objects have been added to
1351 inside a radius around a position
1353 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1354 core::map<u16, bool> ¤t_objects,
1355 core::map<u16, bool> &added_objects)
1357 v3f pos_f = intToFloat(pos, BS);
1358 f32 radius_f = radius * BS;
1360 Go through the object list,
1361 - discard m_removed objects,
1362 - discard objects that are too far away,
1363 - discard objects that are found in current_objects.
1364 - add remaining objects to added_objects
1366 for(core::map<u16, ServerActiveObject*>::Iterator
1367 i = m_active_objects.getIterator();
1368 i.atEnd()==false; i++)
1370 u16 id = i.getNode()->getKey();
1372 ServerActiveObject *object = i.getNode()->getValue();
1375 // Discard if removed
1376 if(object->m_removed)
1378 // Discard if too far
1379 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1380 if(distance_f > radius_f)
1382 // Discard if already on current_objects
1383 core::map<u16, bool>::Node *n;
1384 n = current_objects.find(id);
1387 // Add to added_objects
1388 added_objects.insert(id, false);
1393 Finds out what objects have been removed from
1394 inside a radius around a position
1396 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1397 core::map<u16, bool> ¤t_objects,
1398 core::map<u16, bool> &removed_objects)
1400 v3f pos_f = intToFloat(pos, BS);
1401 f32 radius_f = radius * BS;
1403 Go through current_objects; object is removed if:
1404 - object is not found in m_active_objects (this is actually an
1405 error condition; objects should be set m_removed=true and removed
1406 only after all clients have been informed about removal), or
1407 - object has m_removed=true, or
1408 - object is too far away
1410 for(core::map<u16, bool>::Iterator
1411 i = current_objects.getIterator();
1412 i.atEnd()==false; i++)
1414 u16 id = i.getNode()->getKey();
1415 ServerActiveObject *object = getActiveObject(id);
1418 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1419 <<" object in current_objects is NULL"<<std::endl;
1421 else if(object->m_removed == false)
1423 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1424 /*infostream<<"removed == false"
1425 <<"distance_f = "<<distance_f
1426 <<", radius_f = "<<radius_f<<std::endl;*/
1427 if(distance_f < radius_f)
1433 removed_objects.insert(id, false);
1437 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1439 if(m_active_object_messages.size() == 0)
1440 return ActiveObjectMessage(0);
1442 return m_active_object_messages.pop_front();
1446 ************ Private methods *************
1449 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1453 if(object->getId() == 0){
1454 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1457 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1458 <<"no free ids available"<<std::endl;
1462 object->setId(new_id);
1465 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1466 <<"supplied with id "<<object->getId()<<std::endl;
1468 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1470 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1471 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1475 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1476 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1478 m_active_objects.insert(object->getId(), object);
1480 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1481 <<"Added id="<<object->getId()<<"; there are now "
1482 <<m_active_objects.size()<<" active objects."
1485 // Add static object to active static list of the block
1486 v3f objectpos = object->getBasePosition();
1487 std::string staticdata = object->getStaticData();
1488 StaticObject s_obj(object->getType(), objectpos, staticdata);
1489 // Add to the block where the object is located in
1490 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1491 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1494 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1495 object->m_static_exists = true;
1496 object->m_static_block = blockpos;
1499 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1502 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1503 <<"could not find block for storing id="<<object->getId()
1504 <<" statically"<<std::endl;
1507 // Register reference in scripting api (must be done before post-init)
1508 scriptapi_add_object_reference(m_lua, object);
1509 // Post-initialize object
1510 object->addedToEnvironment(object->getId());
1512 return object->getId();
1516 Remove objects that satisfy (m_removed && m_known_by_count==0)
1518 void ServerEnvironment::removeRemovedObjects()
1520 core::list<u16> objects_to_remove;
1521 for(core::map<u16, ServerActiveObject*>::Iterator
1522 i = m_active_objects.getIterator();
1523 i.atEnd()==false; i++)
1525 u16 id = i.getNode()->getKey();
1526 ServerActiveObject* obj = i.getNode()->getValue();
1527 // This shouldn't happen but check it
1530 infostream<<"NULL object found in ServerEnvironment"
1531 <<" while finding removed objects. id="<<id<<std::endl;
1532 // Id to be removed from m_active_objects
1533 objects_to_remove.push_back(id);
1538 We will delete objects that are marked as removed or thatare
1539 waiting for deletion after deactivation
1541 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1545 Delete static data from block if is marked as removed
1547 if(obj->m_static_exists && obj->m_removed)
1549 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1552 block->m_static_objects.remove(id);
1553 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1554 obj->m_static_exists = false;
1558 // If m_known_by_count > 0, don't actually remove.
1559 if(obj->m_known_by_count > 0)
1562 // Deregister in scripting api
1563 scriptapi_rm_object_reference(m_lua, obj);
1567 // Id to be removed from m_active_objects
1568 objects_to_remove.push_back(id);
1570 // Remove references from m_active_objects
1571 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1572 i != objects_to_remove.end(); i++)
1574 m_active_objects.remove(*i);
1578 static void print_hexdump(std::ostream &o, const std::string &data)
1580 const int linelength = 16;
1581 for(int l=0; ; l++){
1582 int i0 = linelength * l;
1583 bool at_end = false;
1584 int thislinelength = linelength;
1585 if(i0 + thislinelength > (int)data.size()){
1586 thislinelength = data.size() - i0;
1589 for(int di=0; di<linelength; di++){
1592 if(di<thislinelength)
1593 snprintf(buf, 4, "%.2x ", data[i]);
1595 snprintf(buf, 4, " ");
1599 for(int di=0; di<thislinelength; di++){
1613 Convert stored objects from blocks near the players to active.
1615 void ServerEnvironment::activateObjects(MapBlock *block)
1619 // Ignore if no stored objects (to not set changed flag)
1620 if(block->m_static_objects.m_stored.size() == 0)
1622 verbosestream<<"ServerEnvironment::activateObjects(): "
1623 <<"activating objects of block "<<PP(block->getPos())
1624 <<" ("<<block->m_static_objects.m_stored.size()
1625 <<" objects)"<<std::endl;
1626 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1628 errorstream<<"suspiciously large amount of objects detected: "
1629 <<block->m_static_objects.m_stored.size()<<" in "
1630 <<PP(block->getPos())
1631 <<"; removing all of them."<<std::endl;
1632 // Clear stored list
1633 block->m_static_objects.m_stored.clear();
1634 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1637 // A list for objects that couldn't be converted to static for some
1638 // reason. They will be stored back.
1639 core::list<StaticObject> new_stored;
1640 // Loop through stored static objects
1641 for(core::list<StaticObject>::Iterator
1642 i = block->m_static_objects.m_stored.begin();
1643 i != block->m_static_objects.m_stored.end(); i++)
1645 /*infostream<<"Server: Creating an active object from "
1646 <<"static data"<<std::endl;*/
1647 StaticObject &s_obj = *i;
1648 // Create an active object from the data
1649 ServerActiveObject *obj = ServerActiveObject::create
1650 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1651 // If couldn't create object, store static data back.
1654 errorstream<<"ServerEnvironment::activateObjects(): "
1655 <<"failed to create active object from static object "
1656 <<"in block "<<PP(s_obj.pos/BS)
1657 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1658 print_hexdump(verbosestream, s_obj.data);
1660 new_stored.push_back(s_obj);
1663 verbosestream<<"ServerEnvironment::activateObjects(): "
1664 <<"activated static object pos="<<PP(s_obj.pos/BS)
1665 <<" type="<<(int)s_obj.type<<std::endl;
1666 // This will also add the object to the active static list
1667 addActiveObjectRaw(obj, false);
1669 // Clear stored list
1670 block->m_static_objects.m_stored.clear();
1671 // Add leftover failed stuff to stored list
1672 for(core::list<StaticObject>::Iterator
1673 i = new_stored.begin();
1674 i != new_stored.end(); i++)
1676 StaticObject &s_obj = *i;
1677 block->m_static_objects.m_stored.push_back(s_obj);
1680 Note: Block hasn't really been modified here.
1681 The objects have just been activated and moved from the stored
1682 static list to the active static list.
1683 As such, the block is essentially the same.
1684 Thus, do not call block->setChangedFlag().
1685 Otherwise there would be a huge amount of unnecessary I/O.
1690 Convert objects that are not standing inside active blocks to static.
1692 If m_known_by_count != 0, active object is not deleted, but static
1693 data is still updated.
1695 If force_delete is set, active object is deleted nevertheless. It
1696 shall only be set so in the destructor of the environment.
1698 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1700 core::list<u16> objects_to_remove;
1701 for(core::map<u16, ServerActiveObject*>::Iterator
1702 i = m_active_objects.getIterator();
1703 i.atEnd()==false; i++)
1705 ServerActiveObject* obj = i.getNode()->getValue();
1707 // This shouldn't happen but check it
1710 errorstream<<"NULL object found in ServerEnvironment"
1716 // If pending deactivation, let removeRemovedObjects() do it
1717 if(obj->m_pending_deactivation)
1720 u16 id = i.getNode()->getKey();
1721 v3f objectpos = obj->getBasePosition();
1723 // The block in which the object resides in
1724 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1726 // If block is active, don't remove
1727 if(m_active_blocks.contains(blockpos_o))
1730 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1731 <<"deactivating object id="<<id<<" on inactive block "
1732 <<PP(blockpos_o)<<std::endl;
1734 // If known by some client, don't immediately delete.
1735 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1738 Update the static data
1741 // Create new static object
1742 std::string staticdata_new = obj->getStaticData();
1743 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1745 bool stays_in_same_block = false;
1746 bool data_changed = true;
1748 if(obj->m_static_exists){
1749 if(obj->m_static_block == blockpos_o)
1750 stays_in_same_block = true;
1752 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1754 core::map<u16, StaticObject>::Node *n =
1755 block->m_static_objects.m_active.find(id);
1757 StaticObject static_old = n->getValue();
1759 float save_movem = obj->getMinimumSavedMovement();
1761 if(static_old.data == staticdata_new &&
1762 (static_old.pos - objectpos).getLength() < save_movem)
1763 data_changed = false;
1765 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1766 <<"id="<<id<<" m_static_exists=true but "
1767 <<"static data doesn't actually exist in "
1768 <<PP(obj->m_static_block)<<std::endl;
1772 bool shall_be_written = (!stays_in_same_block || data_changed);
1774 // Delete old static object
1775 if(obj->m_static_exists)
1777 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1780 block->m_static_objects.remove(id);
1781 obj->m_static_exists = false;
1782 // Only mark block as modified if data changed considerably
1783 if(shall_be_written)
1784 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1788 // Add to the block where the object is located in
1789 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1790 // Get or generate the block
1791 MapBlock *block = m_map->emergeBlock(blockpos);
1795 if(block->m_static_objects.m_stored.size() >= 49){
1796 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1797 <<" statically but block "<<PP(blockpos)
1798 <<" already contains "
1799 <<block->m_static_objects.m_stored.size()
1800 <<" (over 49) objects."
1801 <<" Forcing delete."<<std::endl;
1802 force_delete = true;
1804 u16 new_id = pending_delete ? id : 0;
1805 block->m_static_objects.insert(new_id, s_obj);
1807 // Only mark block as modified if data changed considerably
1808 if(shall_be_written)
1809 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1811 obj->m_static_exists = true;
1812 obj->m_static_block = block->getPos();
1816 errorstream<<"ServerEnv: Could not find or generate "
1817 <<"a block for storing id="<<obj->getId()
1818 <<" statically"<<std::endl;
1823 If known by some client, set pending deactivation.
1824 Otherwise delete it immediately.
1829 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1830 <<"object id="<<id<<" is known by clients"
1831 <<"; not deleting yet"<<std::endl;
1833 obj->m_pending_deactivation = true;
1837 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1838 <<"object id="<<id<<" is not known by clients"
1839 <<"; deleting"<<std::endl;
1841 // Deregister in scripting api
1842 scriptapi_rm_object_reference(m_lua, obj);
1844 // Delete active object
1846 // Id to be removed from m_active_objects
1847 objects_to_remove.push_back(id);
1850 // Remove references from m_active_objects
1851 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1852 i != objects_to_remove.end(); i++)
1854 m_active_objects.remove(*i);
1865 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1873 ClientEnvironment::~ClientEnvironment()
1875 // delete active objects
1876 for(core::map<u16, ClientActiveObject*>::Iterator
1877 i = m_active_objects.getIterator();
1878 i.atEnd()==false; i++)
1880 delete i.getNode()->getValue();
1887 void ClientEnvironment::addPlayer(Player *player)
1889 DSTACK(__FUNCTION_NAME);
1891 It is a failure if player is local and there already is a local
1894 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1896 Environment::addPlayer(player);
1899 LocalPlayer * ClientEnvironment::getLocalPlayer()
1901 for(core::list<Player*>::Iterator i = m_players.begin();
1902 i != m_players.end(); i++)
1904 Player *player = *i;
1905 if(player->isLocal())
1906 return (LocalPlayer*)player;
1911 void ClientEnvironment::step(float dtime)
1913 DSTACK(__FUNCTION_NAME);
1915 // Get some settings
1916 bool free_move = g_settings->getBool("free_move");
1917 bool footprints = g_settings->getBool("footprints");
1920 LocalPlayer *lplayer = getLocalPlayer();
1922 // collision info queue
1923 core::list<CollisionInfo> player_collisions;
1926 Get the speed the player is going
1928 bool is_climbing = lplayer->is_climbing;
1930 f32 player_speed = 0.001; // just some small value
1931 player_speed = lplayer->getSpeed().getLength();
1934 Maximum position increment
1936 //f32 position_max_increment = 0.05*BS;
1937 f32 position_max_increment = 0.1*BS;
1939 // Maximum time increment (for collision detection etc)
1940 // time = distance / speed
1941 f32 dtime_max_increment = position_max_increment / player_speed;
1943 // Maximum time increment is 10ms or lower
1944 if(dtime_max_increment > 0.01)
1945 dtime_max_increment = 0.01;
1947 // Don't allow overly huge dtime
1951 f32 dtime_downcount = dtime;
1954 Stuff that has a maximum time increment
1963 if(dtime_downcount > dtime_max_increment)
1965 dtime_part = dtime_max_increment;
1966 dtime_downcount -= dtime_part;
1970 dtime_part = dtime_downcount;
1972 Setting this to 0 (no -=dtime_part) disables an infinite loop
1973 when dtime_part is so small that dtime_downcount -= dtime_part
1976 dtime_downcount = 0;
1984 v3f lplayerpos = lplayer->getPosition();
1987 if(free_move == false && is_climbing == false)
1990 v3f speed = lplayer->getSpeed();
1991 if(lplayer->swimming_up == false)
1992 speed.Y -= 9.81 * BS * dtime_part * 2;
1995 if(lplayer->in_water_stable || lplayer->in_water)
1997 f32 max_down = 2.0*BS;
1998 if(speed.Y < -max_down) speed.Y = -max_down;
2001 if(speed.getLength() > max)
2003 speed = speed / speed.getLength() * max;
2007 lplayer->setSpeed(speed);
2012 This also does collision detection.
2014 lplayer->move(dtime_part, *m_map, position_max_increment,
2015 &player_collisions);
2018 while(dtime_downcount > 0.001);
2020 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2022 for(core::list<CollisionInfo>::Iterator
2023 i = player_collisions.begin();
2024 i != player_collisions.end(); i++)
2026 CollisionInfo &info = *i;
2027 if(info.t == COLLISION_FALL)
2029 //f32 tolerance = BS*10; // 2 without damage
2030 f32 tolerance = BS*12; // 3 without damage
2032 if(info.speed > tolerance)
2034 f32 damage_f = (info.speed - tolerance)/BS*factor;
2035 u16 damage = (u16)(damage_f+0.5);
2036 if(lplayer->hp > damage)
2037 lplayer->hp -= damage;
2041 ClientEnvEvent event;
2042 event.type = CEE_PLAYER_DAMAGE;
2043 event.player_damage.amount = damage;
2044 m_client_event_queue.push_back(event);
2050 A quick draft of lava damage
2052 if(m_lava_hurt_interval.step(dtime, 1.0))
2054 v3f pf = lplayer->getPosition();
2056 // Feet, middle and head
2057 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2058 MapNode n1 = m_map->getNodeNoEx(p1);
2059 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2060 MapNode n2 = m_map->getNodeNoEx(p2);
2061 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2062 MapNode n3 = m_map->getNodeNoEx(p2);
2064 u32 damage_per_second = 0;
2065 damage_per_second = MYMAX(damage_per_second,
2066 content_features(n1).damage_per_second);
2067 damage_per_second = MYMAX(damage_per_second,
2068 content_features(n2).damage_per_second);
2069 damage_per_second = MYMAX(damage_per_second,
2070 content_features(n3).damage_per_second);
2072 if(damage_per_second != 0)
2074 ClientEnvEvent event;
2075 event.type = CEE_PLAYER_DAMAGE;
2076 event.player_damage.amount = damage_per_second;
2077 m_client_event_queue.push_back(event);
2082 Stuff that can be done in an arbitarily large dtime
2084 for(core::list<Player*>::Iterator i = m_players.begin();
2085 i != m_players.end(); i++)
2087 Player *player = *i;
2088 v3f playerpos = player->getPosition();
2091 Handle non-local players
2093 if(player->isLocal() == false)
2096 player->move(dtime, *m_map, 100*BS);
2100 // Update lighting on all players on client
2101 u8 light = LIGHT_MAX;
2104 v3s16 p = player->getLightPosition();
2105 MapNode n = m_map->getNode(p);
2106 light = n.getLightBlend(getDayNightRatio());
2108 catch(InvalidPositionException &e) {}
2109 player->updateLight(light);
2112 Add footsteps to grass
2116 // Get node that is at BS/4 under player
2117 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2119 MapNode n = m_map->getNode(bottompos);
2120 if(n.getContent() == CONTENT_GRASS)
2122 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2123 m_map->setNode(bottompos, n);
2124 // Update mesh on client
2125 if(m_map->mapType() == MAPTYPE_CLIENT)
2127 v3s16 p_blocks = getNodeBlockPos(bottompos);
2128 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2129 //b->updateMesh(getDayNightRatio());
2130 b->setMeshExpired(true);
2134 catch(InvalidPositionException &e)
2141 Step active objects and update lighting of them
2144 for(core::map<u16, ClientActiveObject*>::Iterator
2145 i = m_active_objects.getIterator();
2146 i.atEnd()==false; i++)
2148 ClientActiveObject* obj = i.getNode()->getValue();
2150 obj->step(dtime, this);
2152 if(m_active_object_light_update_interval.step(dtime, 0.21))
2155 //u8 light = LIGHT_MAX;
2159 v3s16 p = obj->getLightPosition();
2160 MapNode n = m_map->getNode(p);
2161 light = n.getLightBlend(getDayNightRatio());
2163 catch(InvalidPositionException &e) {}
2164 obj->updateLight(light);
2169 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2171 m_map->updateMeshes(blockpos, getDayNightRatio());
2174 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2176 m_map->expireMeshes(only_daynight_diffed);
2179 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2181 core::map<u16, ClientActiveObject*>::Node *n;
2182 n = m_active_objects.find(id);
2185 return n->getValue();
2188 bool isFreeClientActiveObjectId(u16 id,
2189 core::map<u16, ClientActiveObject*> &objects)
2194 for(core::map<u16, ClientActiveObject*>::Iterator
2195 i = objects.getIterator();
2196 i.atEnd()==false; i++)
2198 if(i.getNode()->getKey() == id)
2204 u16 getFreeClientActiveObjectId(
2205 core::map<u16, ClientActiveObject*> &objects)
2210 if(isFreeClientActiveObjectId(new_id, objects))
2220 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2223 if(object->getId() == 0)
2225 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2228 infostream<<"ClientEnvironment::addActiveObject(): "
2229 <<"no free ids available"<<std::endl;
2233 object->setId(new_id);
2235 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2237 infostream<<"ClientEnvironment::addActiveObject(): "
2238 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2242 infostream<<"ClientEnvironment::addActiveObject(): "
2243 <<"added (id="<<object->getId()<<")"<<std::endl;
2244 m_active_objects.insert(object->getId(), object);
2245 object->addToScene(m_smgr);
2246 return object->getId();
2249 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2250 const std::string &init_data)
2252 ClientActiveObject* obj = ClientActiveObject::create(type);
2255 infostream<<"ClientEnvironment::addActiveObject(): "
2256 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2263 obj->initialize(init_data);
2265 addActiveObject(obj);
2268 void ClientEnvironment::removeActiveObject(u16 id)
2270 infostream<<"ClientEnvironment::removeActiveObject(): "
2271 <<"id="<<id<<std::endl;
2272 ClientActiveObject* obj = getActiveObject(id);
2275 infostream<<"ClientEnvironment::removeActiveObject(): "
2276 <<"id="<<id<<" not found"<<std::endl;
2279 obj->removeFromScene();
2281 m_active_objects.remove(id);
2284 void ClientEnvironment::processActiveObjectMessage(u16 id,
2285 const std::string &data)
2287 ClientActiveObject* obj = getActiveObject(id);
2290 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2291 <<" got message for id="<<id<<", which doesn't exist."
2295 obj->processMessage(data);
2299 Callbacks for activeobjects
2302 void ClientEnvironment::damageLocalPlayer(u8 damage)
2304 LocalPlayer *lplayer = getLocalPlayer();
2307 if(lplayer->hp > damage)
2308 lplayer->hp -= damage;
2312 ClientEnvEvent event;
2313 event.type = CEE_PLAYER_DAMAGE;
2314 event.player_damage.amount = damage;
2315 m_client_event_queue.push_back(event);
2319 Client likes to call these
2322 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2323 core::array<DistanceSortedActiveObject> &dest)
2325 for(core::map<u16, ClientActiveObject*>::Iterator
2326 i = m_active_objects.getIterator();
2327 i.atEnd()==false; i++)
2329 ClientActiveObject* obj = i.getNode()->getValue();
2331 f32 d = (obj->getPosition() - origin).getLength();
2336 DistanceSortedActiveObject dso(obj, d);
2338 dest.push_back(dso);
2342 ClientEnvEvent ClientEnvironment::getClientEvent()
2344 if(m_client_event_queue.size() == 0)
2346 ClientEnvEvent event;
2347 event.type = CEE_NONE;
2350 return m_client_event_queue.pop_front();
2353 #endif // #ifndef SERVER