3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
32 #include "scriptapi.h"
34 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
36 Environment::Environment():
41 Environment::~Environment()
44 for(core::list<Player*>::Iterator i = m_players.begin();
45 i != m_players.end(); i++)
51 void Environment::addPlayer(Player *player)
53 DSTACK(__FUNCTION_NAME);
55 Check that peer_ids are unique.
56 Also check that names are unique.
57 Exception: there can be multiple players with peer_id=0
59 // If peer id is non-zero, it has to be unique.
60 if(player->peer_id != 0)
61 assert(getPlayer(player->peer_id) == NULL);
62 // Name has to be unique.
63 assert(getPlayer(player->getName()) == NULL);
65 m_players.push_back(player);
68 void Environment::removePlayer(u16 peer_id)
70 DSTACK(__FUNCTION_NAME);
72 for(core::list<Player*>::Iterator i = m_players.begin();
73 i != m_players.end(); i++)
76 if(player->peer_id != peer_id)
81 // See if there is an another one
82 // (shouldn't be, but just to be sure)
87 Player * Environment::getPlayer(u16 peer_id)
89 for(core::list<Player*>::Iterator i = m_players.begin();
90 i != m_players.end(); i++)
93 if(player->peer_id == peer_id)
99 Player * Environment::getPlayer(const char *name)
101 for(core::list<Player*>::Iterator i = m_players.begin();
102 i != m_players.end(); i++)
105 if(strcmp(player->getName(), name) == 0)
111 Player * Environment::getRandomConnectedPlayer()
113 core::list<Player*> connected_players = getPlayers(true);
114 u32 chosen_one = myrand() % connected_players.size();
116 for(core::list<Player*>::Iterator
117 i = connected_players.begin();
118 i != connected_players.end(); i++)
130 Player * Environment::getNearestConnectedPlayer(v3f pos)
132 core::list<Player*> connected_players = getPlayers(true);
134 Player *nearest_player = NULL;
135 for(core::list<Player*>::Iterator
136 i = connected_players.begin();
137 i != connected_players.end(); i++)
140 f32 d = player->getPosition().getDistanceFrom(pos);
141 if(d < nearest_d || nearest_player == NULL)
144 nearest_player = player;
147 return nearest_player;
150 core::list<Player*> Environment::getPlayers()
155 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
157 core::list<Player*> newlist;
158 for(core::list<Player*>::Iterator
159 i = m_players.begin();
160 i != m_players.end(); i++)
164 if(ignore_disconnected)
166 // Ignore disconnected players
167 if(player->peer_id == 0)
171 newlist.push_back(player);
176 void Environment::printPlayers(std::ostream &o)
178 o<<"Players in environment:"<<std::endl;
179 for(core::list<Player*>::Iterator i = m_players.begin();
180 i != m_players.end(); i++)
183 o<<"Player peer_id="<<player->peer_id<<std::endl;
187 /*void Environment::setDayNightRatio(u32 r)
189 getDayNightRatio() = r;
192 u32 Environment::getDayNightRatio()
194 //return getDayNightRatio();
195 return time_to_daynight_ratio(m_time_of_day);
202 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
205 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
206 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
207 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
214 void ActiveBlockList::update(core::list<v3s16> &active_positions,
216 core::map<v3s16, bool> &blocks_removed,
217 core::map<v3s16, bool> &blocks_added)
222 core::map<v3s16, bool> newlist;
223 for(core::list<v3s16>::Iterator i = active_positions.begin();
224 i != active_positions.end(); i++)
226 fillRadiusBlock(*i, radius, newlist);
230 Find out which blocks on the old list are not on the new list
232 // Go through old list
233 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
234 i.atEnd()==false; i++)
236 v3s16 p = i.getNode()->getKey();
237 // If not on new list, it's been removed
238 if(newlist.find(p) == NULL)
239 blocks_removed.insert(p, true);
243 Find out which blocks on the new list are not on the old list
245 // Go through new list
246 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
247 i.atEnd()==false; i++)
249 v3s16 p = i.getNode()->getKey();
250 // If not on old list, it's been added
251 if(m_list.find(p) == NULL)
252 blocks_added.insert(p, true);
259 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
260 i.atEnd()==false; i++)
262 v3s16 p = i.getNode()->getKey();
263 m_list.insert(p, true);
271 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L):
274 m_random_spawn_timer(3),
275 m_send_recommended_timer(0),
277 m_game_time_fraction_counter(0)
281 ServerEnvironment::~ServerEnvironment()
283 // Clear active block list.
284 // This makes the next one delete all active objects.
285 m_active_blocks.clear();
287 // Convert all objects to static and delete the active objects
288 deactivateFarObjects(true);
294 void ServerEnvironment::serializePlayers(const std::string &savedir)
296 std::string players_path = savedir + "/players";
297 fs::CreateDir(players_path);
299 core::map<Player*, bool> saved_players;
301 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
302 for(u32 i=0; i<player_files.size(); i++)
304 if(player_files[i].dir)
307 // Full path to this file
308 std::string path = players_path + "/" + player_files[i].name;
310 //infostream<<"Checking player file "<<path<<std::endl;
312 // Load player to see what is its name
313 ServerRemotePlayer testplayer;
315 // Open file and deserialize
316 std::ifstream is(path.c_str(), std::ios_base::binary);
317 if(is.good() == false)
319 infostream<<"Failed to read "<<path<<std::endl;
322 testplayer.deSerialize(is);
325 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
327 // Search for the player
328 std::string playername = testplayer.getName();
329 Player *player = getPlayer(playername.c_str());
332 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
336 //infostream<<"Found matching player, overwriting."<<std::endl;
338 // OK, found. Save player there.
340 // Open file and serialize
341 std::ofstream os(path.c_str(), std::ios_base::binary);
342 if(os.good() == false)
344 infostream<<"Failed to overwrite "<<path<<std::endl;
347 player->serialize(os);
348 saved_players.insert(player, true);
352 for(core::list<Player*>::Iterator i = m_players.begin();
353 i != m_players.end(); i++)
356 if(saved_players.find(player) != NULL)
358 /*infostream<<"Player "<<player->getName()
359 <<" was already saved."<<std::endl;*/
362 std::string playername = player->getName();
363 // Don't save unnamed player
366 //infostream<<"Not saving unnamed player."<<std::endl;
372 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
373 playername = "player";
374 std::string path = players_path + "/" + playername;
376 for(u32 i=0; i<1000; i++)
378 if(fs::PathExists(path) == false)
383 path = players_path + "/" + playername + itos(i);
387 infostream<<"Didn't find free file for player"<<std::endl;
392 /*infostream<<"Saving player "<<player->getName()<<" to "
394 // Open file and serialize
395 std::ofstream os(path.c_str(), std::ios_base::binary);
396 if(os.good() == false)
398 infostream<<"Failed to overwrite "<<path<<std::endl;
401 player->serialize(os);
402 saved_players.insert(player, true);
406 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
409 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
411 std::string players_path = savedir + "/players";
413 core::map<Player*, bool> saved_players;
415 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
416 for(u32 i=0; i<player_files.size(); i++)
418 if(player_files[i].dir)
421 // Full path to this file
422 std::string path = players_path + "/" + player_files[i].name;
424 infostream<<"Checking player file "<<path<<std::endl;
426 // Load player to see what is its name
427 ServerRemotePlayer testplayer;
429 // Open file and deserialize
430 std::ifstream is(path.c_str(), std::ios_base::binary);
431 if(is.good() == false)
433 infostream<<"Failed to read "<<path<<std::endl;
436 testplayer.deSerialize(is);
439 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
441 infostream<<"Not loading player with invalid name: "
442 <<testplayer.getName()<<std::endl;
445 infostream<<"Loaded test player with name "<<testplayer.getName()
448 // Search for the player
449 std::string playername = testplayer.getName();
450 Player *player = getPlayer(playername.c_str());
451 bool newplayer = false;
454 infostream<<"Is a new player"<<std::endl;
455 player = new ServerRemotePlayer();
461 infostream<<"Reading player "<<testplayer.getName()<<" from "
463 // Open file and deserialize
464 std::ifstream is(path.c_str(), std::ios_base::binary);
465 if(is.good() == false)
467 infostream<<"Failed to read "<<path<<std::endl;
470 player->deSerialize(is);
478 void ServerEnvironment::saveMeta(const std::string &savedir)
480 std::string path = savedir + "/env_meta.txt";
482 // Open file and serialize
483 std::ofstream os(path.c_str(), std::ios_base::binary);
484 if(os.good() == false)
486 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
488 throw SerializationError("Couldn't save env meta");
492 args.setU64("game_time", m_game_time);
493 args.setU64("time_of_day", getTimeOfDay());
498 void ServerEnvironment::loadMeta(const std::string &savedir)
500 std::string path = savedir + "/env_meta.txt";
502 // Open file and deserialize
503 std::ifstream is(path.c_str(), std::ios_base::binary);
504 if(is.good() == false)
506 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
508 throw SerializationError("Couldn't load env meta");
516 throw SerializationError
517 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
519 std::getline(is, line);
520 std::string trimmedline = trim(line);
521 if(trimmedline == "EnvArgsEnd")
523 args.parseConfigLine(line);
527 m_game_time = args.getU64("game_time");
528 }catch(SettingNotFoundException &e){
529 // Getting this is crucial, otherwise timestamps are useless
530 throw SerializationError("Couldn't load env meta game_time");
534 m_time_of_day = args.getU64("time_of_day");
535 }catch(SettingNotFoundException &e){
536 // This is not as important
537 m_time_of_day = 9000;
542 // This is probably very useless
543 void spawnRandomObjects(MapBlock *block)
545 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
546 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
548 bool last_node_walkable = false;
549 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
552 MapNode n = block->getNodeNoEx(p);
553 if(n.getContent() == CONTENT_IGNORE)
555 if(content_features(n).liquid_type != LIQUID_NONE)
557 if(content_features(n).walkable)
559 last_node_walkable = true;
562 if(last_node_walkable)
564 // If block contains light information
565 if(content_features(n).param_type == CPT_LIGHT)
567 if(n.getLight(LIGHTBANK_DAY) <= 5)
569 if(myrand() % 1000 == 0)
571 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
573 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
574 std::string data = obj->getStaticData();
575 StaticObject s_obj(obj->getType(),
576 obj->getBasePosition(), data);
578 block->m_static_objects.insert(0, s_obj);
580 block->setChangedFlag();
585 last_node_walkable = false;
591 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
593 // Get time difference
595 u32 stamp = block->getTimestamp();
596 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
597 dtime_s = m_game_time - block->getTimestamp();
598 dtime_s += additional_dtime;
600 // Set current time as timestamp (and let it set ChangedFlag)
601 block->setTimestamp(m_game_time);
603 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
605 // Activate stored objects
606 activateObjects(block);
609 bool changed = block->m_node_metadata.step((float)dtime_s);
613 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
614 event.p = block->getPos();
615 m_map->dispatchEvent(&event);
617 block->setChangedFlag();
620 // TODO: Do something
621 // TODO: Implement usage of ActiveBlockModifier
623 // Here's a quick demonstration
625 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
626 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
627 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
629 v3s16 p = p0 + block->getPosRelative();
630 MapNode n = block->getNodeNoEx(p0);
633 // Convert all mud under proper day lighting to grass
634 if(n.getContent() == CONTENT_MUD)
638 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
639 if(content_features(n_top).air_equivalent &&
640 n_top.getLight(LIGHTBANK_DAY) >= 13)
642 n.setContent(CONTENT_GRASS);
643 m_map->addNodeWithEvent(p, n);
651 void ServerEnvironment::clearAllObjects()
653 infostream<<"ServerEnvironment::clearAllObjects(): "
654 <<"Removing all active objects"<<std::endl;
655 core::list<u16> objects_to_remove;
656 for(core::map<u16, ServerActiveObject*>::Iterator
657 i = m_active_objects.getIterator();
658 i.atEnd()==false; i++)
660 ServerActiveObject* obj = i.getNode()->getValue();
661 u16 id = i.getNode()->getKey();
662 v3f objectpos = obj->getBasePosition();
663 // Delete static object if block is loaded
664 if(obj->m_static_exists){
665 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
667 block->m_static_objects.remove(id);
668 block->raiseModified(MOD_STATE_WRITE_NEEDED);
669 obj->m_static_exists = false;
672 // If known by some client, don't delete immediately
673 if(obj->m_known_by_count > 0){
674 obj->m_pending_deactivation = true;
675 obj->m_removed = true;
678 // Deregister in scripting api
679 scriptapi_rm_object_reference(m_lua, obj);
680 // Delete active object
682 // Id to be removed from m_active_objects
683 objects_to_remove.push_back(id);
685 // Remove references from m_active_objects
686 for(core::list<u16>::Iterator i = objects_to_remove.begin();
687 i != objects_to_remove.end(); i++)
689 m_active_objects.remove(*i);
692 core::list<v3s16> loadable_blocks;
693 infostream<<"ServerEnvironment::clearAllObjects(): "
694 <<"Listing all loadable blocks"<<std::endl;
695 m_map->listAllLoadableBlocks(loadable_blocks);
696 infostream<<"ServerEnvironment::clearAllObjects(): "
697 <<"Done listing all loadable blocks: "
698 <<loadable_blocks.size()
699 <<", now clearing"<<std::endl;
700 u32 report_interval = loadable_blocks.size() / 10;
701 u32 num_blocks_checked = 0;
702 u32 num_blocks_cleared = 0;
703 u32 num_objs_cleared = 0;
704 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
705 i != loadable_blocks.end(); i++)
708 MapBlock *block = m_map->emergeBlock(p, false);
710 errorstream<<"ServerEnvironment::clearAllObjects(): "
711 <<"Failed to emerge block "<<PP(p)<<std::endl;
714 u32 num_stored = block->m_static_objects.m_stored.size();
715 u32 num_active = block->m_static_objects.m_active.size();
716 if(num_stored != 0 || num_active != 0){
717 block->m_static_objects.m_stored.clear();
718 block->m_static_objects.m_active.clear();
719 block->raiseModified(MOD_STATE_WRITE_NEEDED);
720 num_objs_cleared += num_stored + num_active;
721 num_blocks_cleared++;
723 num_blocks_checked++;
725 if(num_blocks_checked % report_interval == 0){
726 float percent = 100.0 * (float)num_blocks_checked /
727 loadable_blocks.size();
728 infostream<<"ServerEnvironment::clearAllObjects(): "
729 <<"Cleared "<<num_objs_cleared<<" objects"
730 <<" in "<<num_blocks_cleared<<" blocks ("
731 <<percent<<"%)"<<std::endl;
734 infostream<<"ServerEnvironment::clearAllObjects(): "
735 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
736 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
739 static void getMob_dungeon_master(Settings &properties)
741 properties.set("looks", "dungeon_master");
742 properties.setFloat("yaw", 1.57);
743 properties.setFloat("hp", 30);
744 properties.setBool("bright_shooting", true);
745 properties.set("shoot_type", "fireball");
746 properties.set("shoot_y", "0.7");
747 properties.set("player_hit_damage", "1");
748 properties.set("player_hit_distance", "1.0");
749 properties.set("player_hit_interval", "0.5");
750 properties.setBool("mindless_rage", myrand_range(0,100)==0);
753 void ServerEnvironment::step(float dtime)
755 DSTACK(__FUNCTION_NAME);
757 //TimeTaker timer("ServerEnv step");
760 bool footprints = g_settings->getBool("footprints");
766 m_game_time_fraction_counter += dtime;
767 u32 inc_i = (u32)m_game_time_fraction_counter;
768 m_game_time += inc_i;
769 m_game_time_fraction_counter -= (float)inc_i;
776 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
777 for(core::list<Player*>::Iterator i = m_players.begin();
778 i != m_players.end(); i++)
782 // Ignore disconnected players
783 if(player->peer_id == 0)
786 v3f playerpos = player->getPosition();
789 player->move(dtime, *m_map, 100*BS);
792 Add footsteps to grass
796 // Get node that is at BS/4 under player
797 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
799 MapNode n = m_map->getNode(bottompos);
800 if(n.getContent() == CONTENT_GRASS)
802 n.setContent(CONTENT_GRASS_FOOTSTEPS);
803 m_map->setNode(bottompos, n);
806 catch(InvalidPositionException &e)
814 Manage active block list
816 if(m_active_blocks_management_interval.step(dtime, 2.0))
818 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
820 Get player block positions
822 core::list<v3s16> players_blockpos;
823 for(core::list<Player*>::Iterator
824 i = m_players.begin();
825 i != m_players.end(); i++)
828 // Ignore disconnected players
829 if(player->peer_id == 0)
831 v3s16 blockpos = getNodeBlockPos(
832 floatToInt(player->getPosition(), BS));
833 players_blockpos.push_back(blockpos);
837 Update list of active blocks, collecting changes
839 const s16 active_block_range = g_settings->getS16("active_block_range");
840 core::map<v3s16, bool> blocks_removed;
841 core::map<v3s16, bool> blocks_added;
842 m_active_blocks.update(players_blockpos, active_block_range,
843 blocks_removed, blocks_added);
846 Handle removed blocks
849 // Convert active objects that are no more in active blocks to static
850 deactivateFarObjects(false);
852 for(core::map<v3s16, bool>::Iterator
853 i = blocks_removed.getIterator();
854 i.atEnd()==false; i++)
856 v3s16 p = i.getNode()->getKey();
858 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
859 <<") became inactive"<<std::endl;*/
861 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
865 // Set current time as timestamp (and let it set ChangedFlag)
866 block->setTimestamp(m_game_time);
873 for(core::map<v3s16, bool>::Iterator
874 i = blocks_added.getIterator();
875 i.atEnd()==false; i++)
877 v3s16 p = i.getNode()->getKey();
879 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
880 <<") became active"<<std::endl;*/
882 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
886 activateBlock(block);
891 Mess around in active blocks
893 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
895 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
899 for(core::map<v3s16, bool>::Iterator
900 i = m_active_blocks.m_list.getIterator();
901 i.atEnd()==false; i++)
903 v3s16 p = i.getNode()->getKey();
905 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
906 <<") being handled"<<std::endl;*/
908 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
912 // Reset block usage timer
913 block->resetUsageTimer();
915 // Set current time as timestamp
916 block->setTimestampNoChangedFlag(m_game_time);
919 bool changed = block->m_node_metadata.step(dtime);
923 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
925 m_map->dispatchEvent(&event);
927 block->setChangedFlag();
932 if(m_active_blocks_test_interval.step(dtime, 10.0))
934 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
935 //float dtime = 10.0;
937 for(core::map<v3s16, bool>::Iterator
938 i = m_active_blocks.m_list.getIterator();
939 i.atEnd()==false; i++)
941 v3s16 p = i.getNode()->getKey();
943 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
944 <<") being handled"<<std::endl;*/
946 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
950 // Set current time as timestamp
951 block->setTimestampNoChangedFlag(m_game_time);
956 Note that map modifications should be done using the event-
957 making map methods so that the server gets information
960 Reading can be done quickly directly from the block.
962 Everything should bind to inside this single content
963 searching loop to keep things fast.
965 // TODO: Implement usage of ActiveBlockModifier
967 // Find out how many objects the block contains
968 //u32 active_object_count = block->m_static_objects.m_active.size();
969 // Find out how many objects this and all the neighbors contain
970 u32 active_object_count_wider = 0;
971 for(s16 x=-1; x<=1; x++)
972 for(s16 y=-1; y<=1; y++)
973 for(s16 z=-1; z<=1; z++)
975 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
978 active_object_count_wider +=
979 block->m_static_objects.m_active.size()
980 + block->m_static_objects.m_stored.size();
982 /*if(block->m_static_objects.m_stored.size() != 0){
983 errorstream<<"ServerEnvironment::step(): "
984 <<PP(block->getPos())<<" contains "
985 <<block->m_static_objects.m_stored.size()
986 <<" stored objects; "
987 <<"when spawning objects, when counting active "
988 <<"objects in wide area. relative position: "
989 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
994 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
995 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
996 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
998 v3s16 p = p0 + block->getPosRelative();
999 MapNode n = block->getNodeNoEx(p0);
1003 Convert mud under proper lighting to grass
1005 if(n.getContent() == CONTENT_MUD)
1007 if(myrand()%20 == 0)
1009 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1010 if(content_features(n_top).air_equivalent &&
1011 n_top.getLightBlend(getDayNightRatio()) >= 13)
1013 n.setContent(CONTENT_GRASS);
1014 m_map->addNodeWithEvent(p, n);
1019 Convert grass into mud if under something else than air
1021 if(n.getContent() == CONTENT_GRASS)
1023 //if(myrand()%20 == 0)
1025 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1026 if(content_features(n_top).air_equivalent == false)
1028 n.setContent(CONTENT_MUD);
1029 m_map->addNodeWithEvent(p, n);
1034 Rats spawn around regular trees
1036 if(n.getContent() == CONTENT_TREE ||
1037 n.getContent() == CONTENT_JUNGLETREE)
1039 if(myrand()%200 == 0 && active_object_count_wider == 0)
1041 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1042 0, myrand_range(-2, 2));
1043 MapNode n1 = m_map->getNodeNoEx(p1);
1044 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1045 if(n1b.getContent() == CONTENT_GRASS &&
1046 n1.getContent() == CONTENT_AIR)
1048 v3f pos = intToFloat(p1, BS);
1049 ServerActiveObject *obj = new RatSAO(this, pos);
1050 addActiveObject(obj);
1055 Fun things spawn in caves and dungeons
1057 if(n.getContent() == CONTENT_STONE ||
1058 n.getContent() == CONTENT_MOSSYCOBBLE)
1060 if(myrand()%200 == 0 && active_object_count_wider == 0)
1062 v3s16 p1 = p + v3s16(0,1,0);
1063 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1064 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
1065 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1066 if(n1a.getContent() == CONTENT_AIR &&
1067 n1b.getContent() == CONTENT_AIR)
1069 v3f pos = intToFloat(p1, BS);
1071 if(i == 0 || i == 1){
1072 actionstream<<"A dungeon master spawns at "
1073 <<PP(p1)<<std::endl;
1074 Settings properties;
1075 getMob_dungeon_master(properties);
1076 ServerActiveObject *obj = new MobV2SAO(
1077 this, pos, &properties);
1078 addActiveObject(obj);
1079 } else if(i == 2 || i == 3){
1080 actionstream<<"Rats spawn at "
1081 <<PP(p1)<<std::endl;
1082 for(int j=0; j<3; j++){
1083 ServerActiveObject *obj = new RatSAO(
1085 addActiveObject(obj);
1088 actionstream<<"An oerkki spawns at "
1089 <<PP(p1)<<std::endl;
1090 ServerActiveObject *obj = new Oerkki1SAO(
1092 addActiveObject(obj);
1099 Make trees from saplings!
1101 if(n.getContent() == CONTENT_SAPLING)
1103 if(myrand()%50 == 0)
1105 actionstream<<"A sapling grows into a tree at "
1108 core::map<v3s16, MapBlock*> modified_blocks;
1110 ManualMapVoxelManipulator vmanip(m_map);
1111 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1112 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1113 bool is_apple_tree = myrand()%4 == 0;
1114 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1115 vmanip.blitBackAll(&modified_blocks);
1118 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1119 for(core::map<v3s16, MapBlock*>::Iterator
1120 i = modified_blocks.getIterator();
1121 i.atEnd() == false; i++)
1123 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1125 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1127 // Send a MEET_OTHER event
1129 event.type = MEET_OTHER;
1130 for(core::map<v3s16, MapBlock*>::Iterator
1131 i = modified_blocks.getIterator();
1132 i.atEnd() == false; i++)
1134 v3s16 p = i.getNode()->getKey();
1135 event.modified_blocks.insert(p, true);
1137 m_map->dispatchEvent(&event);
1148 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1149 //TimeTaker timer("Step active objects");
1151 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1153 // This helps the objects to send data at the same time
1154 bool send_recommended = false;
1155 m_send_recommended_timer += dtime;
1156 if(m_send_recommended_timer > getSendRecommendedInterval())
1158 m_send_recommended_timer -= getSendRecommendedInterval();
1159 send_recommended = true;
1162 for(core::map<u16, ServerActiveObject*>::Iterator
1163 i = m_active_objects.getIterator();
1164 i.atEnd()==false; i++)
1166 ServerActiveObject* obj = i.getNode()->getValue();
1167 // Remove non-peaceful mobs on peaceful mode
1168 if(g_settings->getBool("only_peaceful_mobs")){
1169 if(!obj->isPeaceful())
1170 obj->m_removed = true;
1172 // Don't step if is to be removed or stored statically
1173 if(obj->m_removed || obj->m_pending_deactivation)
1176 obj->step(dtime, send_recommended);
1177 // Read messages from object
1178 while(obj->m_messages_out.size() > 0)
1180 m_active_object_messages.push_back(
1181 obj->m_messages_out.pop_front());
1187 Manage active objects
1189 if(m_object_management_interval.step(dtime, 0.5))
1191 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1193 Remove objects that satisfy (m_removed && m_known_by_count==0)
1195 removeRemovedObjects();
1198 if(g_settings->getBool("enable_experimental"))
1205 m_random_spawn_timer -= dtime;
1206 if(m_random_spawn_timer < 0)
1208 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1209 //m_random_spawn_timer += 2.0;
1210 m_random_spawn_timer += 200.0;
1216 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1217 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1218 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1220 Player *player = getRandomConnectedPlayer();
1223 pos = player->getPosition();
1225 myrand_range(-3,3)*BS,
1227 myrand_range(-3,3)*BS
1231 Create a ServerActiveObject
1234 //TestSAO *obj = new TestSAO(this, pos);
1235 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1236 //ServerActiveObject *obj = new RatSAO(this, pos);
1237 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1238 //ServerActiveObject *obj = new FireflySAO(this, pos);
1240 infostream<<"Server: Spawning MobV2SAO at "
1241 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1243 Settings properties;
1244 getMob_dungeon_master(properties);
1245 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1246 addActiveObject(obj);
1250 } // enable_experimental
1253 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1255 core::map<u16, ServerActiveObject*>::Node *n;
1256 n = m_active_objects.find(id);
1259 return n->getValue();
1262 bool isFreeServerActiveObjectId(u16 id,
1263 core::map<u16, ServerActiveObject*> &objects)
1268 for(core::map<u16, ServerActiveObject*>::Iterator
1269 i = objects.getIterator();
1270 i.atEnd()==false; i++)
1272 if(i.getNode()->getKey() == id)
1278 u16 getFreeServerActiveObjectId(
1279 core::map<u16, ServerActiveObject*> &objects)
1284 if(isFreeServerActiveObjectId(new_id, objects))
1294 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1297 u16 id = addActiveObjectRaw(object, true);
1301 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1305 v3f objectpos = obj->getBasePosition();
1307 // The block in which the object resides in
1308 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1311 Update the static data
1314 // Create new static object
1315 std::string staticdata = obj->getStaticData();
1316 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1317 // Add to the block where the object is located in
1318 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1319 // Get or generate the block
1320 MapBlock *block = m_map->emergeBlock(blockpos);
1322 bool succeeded = false;
1326 block->m_static_objects.insert(0, s_obj);
1327 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1331 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1332 <<"Could not find or generate "
1333 <<"a block for storing static object"<<std::endl;
1343 Finds out what new objects have been added to
1344 inside a radius around a position
1346 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1347 core::map<u16, bool> ¤t_objects,
1348 core::map<u16, bool> &added_objects)
1350 v3f pos_f = intToFloat(pos, BS);
1351 f32 radius_f = radius * BS;
1353 Go through the object list,
1354 - discard m_removed objects,
1355 - discard objects that are too far away,
1356 - discard objects that are found in current_objects.
1357 - add remaining objects to added_objects
1359 for(core::map<u16, ServerActiveObject*>::Iterator
1360 i = m_active_objects.getIterator();
1361 i.atEnd()==false; i++)
1363 u16 id = i.getNode()->getKey();
1365 ServerActiveObject *object = i.getNode()->getValue();
1368 // Discard if removed
1369 if(object->m_removed)
1371 // Discard if too far
1372 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1373 if(distance_f > radius_f)
1375 // Discard if already on current_objects
1376 core::map<u16, bool>::Node *n;
1377 n = current_objects.find(id);
1380 // Add to added_objects
1381 added_objects.insert(id, false);
1386 Finds out what objects have been removed from
1387 inside a radius around a position
1389 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1390 core::map<u16, bool> ¤t_objects,
1391 core::map<u16, bool> &removed_objects)
1393 v3f pos_f = intToFloat(pos, BS);
1394 f32 radius_f = radius * BS;
1396 Go through current_objects; object is removed if:
1397 - object is not found in m_active_objects (this is actually an
1398 error condition; objects should be set m_removed=true and removed
1399 only after all clients have been informed about removal), or
1400 - object has m_removed=true, or
1401 - object is too far away
1403 for(core::map<u16, bool>::Iterator
1404 i = current_objects.getIterator();
1405 i.atEnd()==false; i++)
1407 u16 id = i.getNode()->getKey();
1408 ServerActiveObject *object = getActiveObject(id);
1411 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1412 <<" object in current_objects is NULL"<<std::endl;
1414 else if(object->m_removed == false)
1416 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1417 /*infostream<<"removed == false"
1418 <<"distance_f = "<<distance_f
1419 <<", radius_f = "<<radius_f<<std::endl;*/
1420 if(distance_f < radius_f)
1426 removed_objects.insert(id, false);
1430 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1432 if(m_active_object_messages.size() == 0)
1433 return ActiveObjectMessage(0);
1435 return m_active_object_messages.pop_front();
1439 ************ Private methods *************
1442 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1446 if(object->getId() == 0){
1447 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1450 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1451 <<"no free ids available"<<std::endl;
1455 object->setId(new_id);
1458 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1459 <<"supplied with id "<<object->getId()<<std::endl;
1461 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1463 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1464 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1468 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1471 m_active_objects.insert(object->getId(), object);
1473 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1474 <<"Added id="<<object->getId()<<"; there are now "
1475 <<m_active_objects.size()<<" active objects."
1478 // Add static object to active static list of the block
1479 v3f objectpos = object->getBasePosition();
1480 std::string staticdata = object->getStaticData();
1481 StaticObject s_obj(object->getType(), objectpos, staticdata);
1482 // Add to the block where the object is located in
1483 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1484 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1487 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1488 object->m_static_exists = true;
1489 object->m_static_block = blockpos;
1492 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1495 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1496 <<"could not find block for storing id="<<object->getId()
1497 <<" statically"<<std::endl;
1500 // Register reference in scripting api (must be done before post-init)
1501 scriptapi_add_object_reference(m_lua, object);
1502 // Post-initialize object
1503 object->addedToEnvironment(object->getId());
1505 return object->getId();
1509 Remove objects that satisfy (m_removed && m_known_by_count==0)
1511 void ServerEnvironment::removeRemovedObjects()
1513 core::list<u16> objects_to_remove;
1514 for(core::map<u16, ServerActiveObject*>::Iterator
1515 i = m_active_objects.getIterator();
1516 i.atEnd()==false; i++)
1518 u16 id = i.getNode()->getKey();
1519 ServerActiveObject* obj = i.getNode()->getValue();
1520 // This shouldn't happen but check it
1523 infostream<<"NULL object found in ServerEnvironment"
1524 <<" while finding removed objects. id="<<id<<std::endl;
1525 // Id to be removed from m_active_objects
1526 objects_to_remove.push_back(id);
1531 We will delete objects that are marked as removed or thatare
1532 waiting for deletion after deactivation
1534 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1538 Delete static data from block if is marked as removed
1540 if(obj->m_static_exists && obj->m_removed)
1542 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1545 block->m_static_objects.remove(id);
1546 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1547 obj->m_static_exists = false;
1551 // If m_known_by_count > 0, don't actually remove.
1552 if(obj->m_known_by_count > 0)
1555 // Deregister in scripting api
1556 scriptapi_rm_object_reference(m_lua, obj);
1560 // Id to be removed from m_active_objects
1561 objects_to_remove.push_back(id);
1563 // Remove references from m_active_objects
1564 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1565 i != objects_to_remove.end(); i++)
1567 m_active_objects.remove(*i);
1571 static void print_hexdump(std::ostream &o, const std::string &data)
1573 const int linelength = 16;
1574 for(int l=0; ; l++){
1575 int i0 = linelength * l;
1576 bool at_end = false;
1577 int thislinelength = linelength;
1578 if(i0 + thislinelength > (int)data.size()){
1579 thislinelength = data.size() - i0;
1582 for(int di=0; di<linelength; di++){
1585 if(di<thislinelength)
1586 snprintf(buf, 4, "%.2x ", data[i]);
1588 snprintf(buf, 4, " ");
1592 for(int di=0; di<thislinelength; di++){
1606 Convert stored objects from blocks near the players to active.
1608 void ServerEnvironment::activateObjects(MapBlock *block)
1612 // Ignore if no stored objects (to not set changed flag)
1613 if(block->m_static_objects.m_stored.size() == 0)
1615 verbosestream<<"ServerEnvironment::activateObjects(): "
1616 <<"activating objects of block "<<PP(block->getPos())
1617 <<" ("<<block->m_static_objects.m_stored.size()
1618 <<" objects)"<<std::endl;
1619 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1621 errorstream<<"suspiciously large amount of objects detected: "
1622 <<block->m_static_objects.m_stored.size()<<" in "
1623 <<PP(block->getPos())
1624 <<"; removing all of them."<<std::endl;
1625 // Clear stored list
1626 block->m_static_objects.m_stored.clear();
1627 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1630 // A list for objects that couldn't be converted to static for some
1631 // reason. They will be stored back.
1632 core::list<StaticObject> new_stored;
1633 // Loop through stored static objects
1634 for(core::list<StaticObject>::Iterator
1635 i = block->m_static_objects.m_stored.begin();
1636 i != block->m_static_objects.m_stored.end(); i++)
1638 /*infostream<<"Server: Creating an active object from "
1639 <<"static data"<<std::endl;*/
1640 StaticObject &s_obj = *i;
1641 // Create an active object from the data
1642 ServerActiveObject *obj = ServerActiveObject::create
1643 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1644 // If couldn't create object, store static data back.
1647 errorstream<<"ServerEnvironment::activateObjects(): "
1648 <<"failed to create active object from static object "
1649 <<"in block "<<PP(s_obj.pos/BS)
1650 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1651 print_hexdump(verbosestream, s_obj.data);
1653 new_stored.push_back(s_obj);
1656 verbosestream<<"ServerEnvironment::activateObjects(): "
1657 <<"activated static object pos="<<PP(s_obj.pos/BS)
1658 <<" type="<<(int)s_obj.type<<std::endl;
1659 // This will also add the object to the active static list
1660 addActiveObjectRaw(obj, false);
1662 // Clear stored list
1663 block->m_static_objects.m_stored.clear();
1664 // Add leftover failed stuff to stored list
1665 for(core::list<StaticObject>::Iterator
1666 i = new_stored.begin();
1667 i != new_stored.end(); i++)
1669 StaticObject &s_obj = *i;
1670 block->m_static_objects.m_stored.push_back(s_obj);
1673 Note: Block hasn't really been modified here.
1674 The objects have just been activated and moved from the stored
1675 static list to the active static list.
1676 As such, the block is essentially the same.
1677 Thus, do not call block->setChangedFlag().
1678 Otherwise there would be a huge amount of unnecessary I/O.
1683 Convert objects that are not standing inside active blocks to static.
1685 If m_known_by_count != 0, active object is not deleted, but static
1686 data is still updated.
1688 If force_delete is set, active object is deleted nevertheless. It
1689 shall only be set so in the destructor of the environment.
1691 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1693 core::list<u16> objects_to_remove;
1694 for(core::map<u16, ServerActiveObject*>::Iterator
1695 i = m_active_objects.getIterator();
1696 i.atEnd()==false; i++)
1698 ServerActiveObject* obj = i.getNode()->getValue();
1700 // This shouldn't happen but check it
1703 errorstream<<"NULL object found in ServerEnvironment"
1709 // If pending deactivation, let removeRemovedObjects() do it
1710 if(obj->m_pending_deactivation)
1713 u16 id = i.getNode()->getKey();
1714 v3f objectpos = obj->getBasePosition();
1716 // The block in which the object resides in
1717 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1719 // If block is active, don't remove
1720 if(m_active_blocks.contains(blockpos_o))
1723 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1724 <<"deactivating object id="<<id<<" on inactive block "
1725 <<PP(blockpos_o)<<std::endl;
1727 // If known by some client, don't immediately delete.
1728 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1731 Update the static data
1734 // Create new static object
1735 std::string staticdata_new = obj->getStaticData();
1736 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1738 bool stays_in_same_block = false;
1739 bool data_changed = true;
1741 if(obj->m_static_exists){
1742 if(obj->m_static_block == blockpos_o)
1743 stays_in_same_block = true;
1745 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1747 core::map<u16, StaticObject>::Node *n =
1748 block->m_static_objects.m_active.find(id);
1750 StaticObject static_old = n->getValue();
1752 float save_movem = obj->getMinimumSavedMovement();
1754 if(static_old.data == staticdata_new &&
1755 (static_old.pos - objectpos).getLength() < save_movem)
1756 data_changed = false;
1758 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1759 <<"id="<<id<<" m_static_exists=true but "
1760 <<"static data doesn't actually exist in "
1761 <<PP(obj->m_static_block)<<std::endl;
1765 bool shall_be_written = (!stays_in_same_block || data_changed);
1767 // Delete old static object
1768 if(obj->m_static_exists)
1770 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1773 block->m_static_objects.remove(id);
1774 obj->m_static_exists = false;
1775 // Only mark block as modified if data changed considerably
1776 if(shall_be_written)
1777 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1781 // Add to the block where the object is located in
1782 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1783 // Get or generate the block
1784 MapBlock *block = m_map->emergeBlock(blockpos);
1788 if(block->m_static_objects.m_stored.size() >= 49){
1789 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1790 <<" statically but block "<<PP(blockpos)
1791 <<" already contains "
1792 <<block->m_static_objects.m_stored.size()
1793 <<" (over 49) objects."
1794 <<" Forcing delete."<<std::endl;
1795 force_delete = true;
1797 u16 new_id = pending_delete ? id : 0;
1798 block->m_static_objects.insert(new_id, s_obj);
1800 // Only mark block as modified if data changed considerably
1801 if(shall_be_written)
1802 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1804 obj->m_static_exists = true;
1805 obj->m_static_block = block->getPos();
1809 errorstream<<"ServerEnv: Could not find or generate "
1810 <<"a block for storing id="<<obj->getId()
1811 <<" statically"<<std::endl;
1816 If known by some client, set pending deactivation.
1817 Otherwise delete it immediately.
1822 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1823 <<"object id="<<id<<" is known by clients"
1824 <<"; not deleting yet"<<std::endl;
1826 obj->m_pending_deactivation = true;
1830 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1831 <<"object id="<<id<<" is not known by clients"
1832 <<"; deleting"<<std::endl;
1834 // Deregister in scripting api
1835 scriptapi_rm_object_reference(m_lua, obj);
1837 // Delete active object
1839 // Id to be removed from m_active_objects
1840 objects_to_remove.push_back(id);
1843 // Remove references from m_active_objects
1844 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1845 i != objects_to_remove.end(); i++)
1847 m_active_objects.remove(*i);
1858 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1866 ClientEnvironment::~ClientEnvironment()
1868 // delete active objects
1869 for(core::map<u16, ClientActiveObject*>::Iterator
1870 i = m_active_objects.getIterator();
1871 i.atEnd()==false; i++)
1873 delete i.getNode()->getValue();
1880 void ClientEnvironment::addPlayer(Player *player)
1882 DSTACK(__FUNCTION_NAME);
1884 It is a failure if player is local and there already is a local
1887 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1889 Environment::addPlayer(player);
1892 LocalPlayer * ClientEnvironment::getLocalPlayer()
1894 for(core::list<Player*>::Iterator i = m_players.begin();
1895 i != m_players.end(); i++)
1897 Player *player = *i;
1898 if(player->isLocal())
1899 return (LocalPlayer*)player;
1904 void ClientEnvironment::step(float dtime)
1906 DSTACK(__FUNCTION_NAME);
1908 // Get some settings
1909 bool free_move = g_settings->getBool("free_move");
1910 bool footprints = g_settings->getBool("footprints");
1913 LocalPlayer *lplayer = getLocalPlayer();
1915 // collision info queue
1916 core::list<CollisionInfo> player_collisions;
1919 Get the speed the player is going
1921 bool is_climbing = lplayer->is_climbing;
1923 f32 player_speed = 0.001; // just some small value
1924 player_speed = lplayer->getSpeed().getLength();
1927 Maximum position increment
1929 //f32 position_max_increment = 0.05*BS;
1930 f32 position_max_increment = 0.1*BS;
1932 // Maximum time increment (for collision detection etc)
1933 // time = distance / speed
1934 f32 dtime_max_increment = position_max_increment / player_speed;
1936 // Maximum time increment is 10ms or lower
1937 if(dtime_max_increment > 0.01)
1938 dtime_max_increment = 0.01;
1940 // Don't allow overly huge dtime
1944 f32 dtime_downcount = dtime;
1947 Stuff that has a maximum time increment
1956 if(dtime_downcount > dtime_max_increment)
1958 dtime_part = dtime_max_increment;
1959 dtime_downcount -= dtime_part;
1963 dtime_part = dtime_downcount;
1965 Setting this to 0 (no -=dtime_part) disables an infinite loop
1966 when dtime_part is so small that dtime_downcount -= dtime_part
1969 dtime_downcount = 0;
1977 v3f lplayerpos = lplayer->getPosition();
1980 if(free_move == false && is_climbing == false)
1983 v3f speed = lplayer->getSpeed();
1984 if(lplayer->swimming_up == false)
1985 speed.Y -= 9.81 * BS * dtime_part * 2;
1988 if(lplayer->in_water_stable || lplayer->in_water)
1990 f32 max_down = 2.0*BS;
1991 if(speed.Y < -max_down) speed.Y = -max_down;
1994 if(speed.getLength() > max)
1996 speed = speed / speed.getLength() * max;
2000 lplayer->setSpeed(speed);
2005 This also does collision detection.
2007 lplayer->move(dtime_part, *m_map, position_max_increment,
2008 &player_collisions);
2011 while(dtime_downcount > 0.001);
2013 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2015 for(core::list<CollisionInfo>::Iterator
2016 i = player_collisions.begin();
2017 i != player_collisions.end(); i++)
2019 CollisionInfo &info = *i;
2020 if(info.t == COLLISION_FALL)
2022 //f32 tolerance = BS*10; // 2 without damage
2023 f32 tolerance = BS*12; // 3 without damage
2025 if(info.speed > tolerance)
2027 f32 damage_f = (info.speed - tolerance)/BS*factor;
2028 u16 damage = (u16)(damage_f+0.5);
2029 if(lplayer->hp > damage)
2030 lplayer->hp -= damage;
2034 ClientEnvEvent event;
2035 event.type = CEE_PLAYER_DAMAGE;
2036 event.player_damage.amount = damage;
2037 m_client_event_queue.push_back(event);
2043 A quick draft of lava damage
2045 if(m_lava_hurt_interval.step(dtime, 1.0))
2047 v3f pf = lplayer->getPosition();
2049 // Feet, middle and head
2050 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2051 MapNode n1 = m_map->getNodeNoEx(p1);
2052 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2053 MapNode n2 = m_map->getNodeNoEx(p2);
2054 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2055 MapNode n3 = m_map->getNodeNoEx(p2);
2057 u32 damage_per_second = 0;
2058 damage_per_second = MYMAX(damage_per_second,
2059 content_features(n1).damage_per_second);
2060 damage_per_second = MYMAX(damage_per_second,
2061 content_features(n2).damage_per_second);
2062 damage_per_second = MYMAX(damage_per_second,
2063 content_features(n3).damage_per_second);
2065 if(damage_per_second != 0)
2067 ClientEnvEvent event;
2068 event.type = CEE_PLAYER_DAMAGE;
2069 event.player_damage.amount = damage_per_second;
2070 m_client_event_queue.push_back(event);
2075 Stuff that can be done in an arbitarily large dtime
2077 for(core::list<Player*>::Iterator i = m_players.begin();
2078 i != m_players.end(); i++)
2080 Player *player = *i;
2081 v3f playerpos = player->getPosition();
2084 Handle non-local players
2086 if(player->isLocal() == false)
2089 player->move(dtime, *m_map, 100*BS);
2093 // Update lighting on all players on client
2094 u8 light = LIGHT_MAX;
2097 v3s16 p = player->getLightPosition();
2098 MapNode n = m_map->getNode(p);
2099 light = n.getLightBlend(getDayNightRatio());
2101 catch(InvalidPositionException &e) {}
2102 player->updateLight(light);
2105 Add footsteps to grass
2109 // Get node that is at BS/4 under player
2110 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2112 MapNode n = m_map->getNode(bottompos);
2113 if(n.getContent() == CONTENT_GRASS)
2115 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2116 m_map->setNode(bottompos, n);
2117 // Update mesh on client
2118 if(m_map->mapType() == MAPTYPE_CLIENT)
2120 v3s16 p_blocks = getNodeBlockPos(bottompos);
2121 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2122 //b->updateMesh(getDayNightRatio());
2123 b->setMeshExpired(true);
2127 catch(InvalidPositionException &e)
2134 Step active objects and update lighting of them
2137 for(core::map<u16, ClientActiveObject*>::Iterator
2138 i = m_active_objects.getIterator();
2139 i.atEnd()==false; i++)
2141 ClientActiveObject* obj = i.getNode()->getValue();
2143 obj->step(dtime, this);
2145 if(m_active_object_light_update_interval.step(dtime, 0.21))
2148 //u8 light = LIGHT_MAX;
2152 v3s16 p = obj->getLightPosition();
2153 MapNode n = m_map->getNode(p);
2154 light = n.getLightBlend(getDayNightRatio());
2156 catch(InvalidPositionException &e) {}
2157 obj->updateLight(light);
2162 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2164 m_map->updateMeshes(blockpos, getDayNightRatio());
2167 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2169 m_map->expireMeshes(only_daynight_diffed);
2172 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2174 core::map<u16, ClientActiveObject*>::Node *n;
2175 n = m_active_objects.find(id);
2178 return n->getValue();
2181 bool isFreeClientActiveObjectId(u16 id,
2182 core::map<u16, ClientActiveObject*> &objects)
2187 for(core::map<u16, ClientActiveObject*>::Iterator
2188 i = objects.getIterator();
2189 i.atEnd()==false; i++)
2191 if(i.getNode()->getKey() == id)
2197 u16 getFreeClientActiveObjectId(
2198 core::map<u16, ClientActiveObject*> &objects)
2203 if(isFreeClientActiveObjectId(new_id, objects))
2213 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2216 if(object->getId() == 0)
2218 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2221 infostream<<"ClientEnvironment::addActiveObject(): "
2222 <<"no free ids available"<<std::endl;
2226 object->setId(new_id);
2228 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2230 infostream<<"ClientEnvironment::addActiveObject(): "
2231 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2235 infostream<<"ClientEnvironment::addActiveObject(): "
2236 <<"added (id="<<object->getId()<<")"<<std::endl;
2237 m_active_objects.insert(object->getId(), object);
2238 object->addToScene(m_smgr);
2239 return object->getId();
2242 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2243 const std::string &init_data)
2245 ClientActiveObject* obj = ClientActiveObject::create(type);
2248 infostream<<"ClientEnvironment::addActiveObject(): "
2249 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2256 obj->initialize(init_data);
2258 addActiveObject(obj);
2261 void ClientEnvironment::removeActiveObject(u16 id)
2263 infostream<<"ClientEnvironment::removeActiveObject(): "
2264 <<"id="<<id<<std::endl;
2265 ClientActiveObject* obj = getActiveObject(id);
2268 infostream<<"ClientEnvironment::removeActiveObject(): "
2269 <<"id="<<id<<" not found"<<std::endl;
2272 obj->removeFromScene();
2274 m_active_objects.remove(id);
2277 void ClientEnvironment::processActiveObjectMessage(u16 id,
2278 const std::string &data)
2280 ClientActiveObject* obj = getActiveObject(id);
2283 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2284 <<" got message for id="<<id<<", which doesn't exist."
2288 obj->processMessage(data);
2292 Callbacks for activeobjects
2295 void ClientEnvironment::damageLocalPlayer(u8 damage)
2297 LocalPlayer *lplayer = getLocalPlayer();
2300 if(lplayer->hp > damage)
2301 lplayer->hp -= damage;
2305 ClientEnvEvent event;
2306 event.type = CEE_PLAYER_DAMAGE;
2307 event.player_damage.amount = damage;
2308 m_client_event_queue.push_back(event);
2312 Client likes to call these
2315 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2316 core::array<DistanceSortedActiveObject> &dest)
2318 for(core::map<u16, ClientActiveObject*>::Iterator
2319 i = m_active_objects.getIterator();
2320 i.atEnd()==false; i++)
2322 ClientActiveObject* obj = i.getNode()->getValue();
2324 f32 d = (obj->getPosition() - origin).getLength();
2329 DistanceSortedActiveObject dso(obj, d);
2331 dest.push_back(dso);
2335 ClientEnvEvent ClientEnvironment::getClientEvent()
2337 if(m_client_event_queue.size() == 0)
2339 ClientEnvEvent event;
2340 event.type = CEE_NONE;
2343 return m_client_event_queue.pop_front();
2346 #endif // #ifndef SERVER