3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
32 #include "scriptapi.h"
34 #include "nodemetadata.h"
35 #include "main.h" // For g_settings, g_profiler
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
40 Environment::Environment():
45 Environment::~Environment()
48 for(core::list<Player*>::Iterator i = m_players.begin();
49 i != m_players.end(); i++)
55 void Environment::addPlayer(Player *player)
57 DSTACK(__FUNCTION_NAME);
59 Check that peer_ids are unique.
60 Also check that names are unique.
61 Exception: there can be multiple players with peer_id=0
63 // If peer id is non-zero, it has to be unique.
64 if(player->peer_id != 0)
65 assert(getPlayer(player->peer_id) == NULL);
66 // Name has to be unique.
67 assert(getPlayer(player->getName()) == NULL);
69 m_players.push_back(player);
72 void Environment::removePlayer(u16 peer_id)
74 DSTACK(__FUNCTION_NAME);
76 for(core::list<Player*>::Iterator i = m_players.begin();
77 i != m_players.end(); i++)
80 if(player->peer_id != peer_id)
85 // See if there is an another one
86 // (shouldn't be, but just to be sure)
91 Player * Environment::getPlayer(u16 peer_id)
93 for(core::list<Player*>::Iterator i = m_players.begin();
94 i != m_players.end(); i++)
97 if(player->peer_id == peer_id)
103 Player * Environment::getPlayer(const char *name)
105 for(core::list<Player*>::Iterator i = m_players.begin();
106 i != m_players.end(); i++)
109 if(strcmp(player->getName(), name) == 0)
115 Player * Environment::getRandomConnectedPlayer()
117 core::list<Player*> connected_players = getPlayers(true);
118 u32 chosen_one = myrand() % connected_players.size();
120 for(core::list<Player*>::Iterator
121 i = connected_players.begin();
122 i != connected_players.end(); i++)
134 Player * Environment::getNearestConnectedPlayer(v3f pos)
136 core::list<Player*> connected_players = getPlayers(true);
138 Player *nearest_player = NULL;
139 for(core::list<Player*>::Iterator
140 i = connected_players.begin();
141 i != connected_players.end(); i++)
144 f32 d = player->getPosition().getDistanceFrom(pos);
145 if(d < nearest_d || nearest_player == NULL)
148 nearest_player = player;
151 return nearest_player;
154 core::list<Player*> Environment::getPlayers()
159 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
161 core::list<Player*> newlist;
162 for(core::list<Player*>::Iterator
163 i = m_players.begin();
164 i != m_players.end(); i++)
168 if(ignore_disconnected)
170 // Ignore disconnected players
171 if(player->peer_id == 0)
175 newlist.push_back(player);
180 void Environment::printPlayers(std::ostream &o)
182 o<<"Players in environment:"<<std::endl;
183 for(core::list<Player*>::Iterator i = m_players.begin();
184 i != m_players.end(); i++)
187 o<<"Player peer_id="<<player->peer_id<<std::endl;
191 /*void Environment::setDayNightRatio(u32 r)
193 getDayNightRatio() = r;
196 u32 Environment::getDayNightRatio()
198 //return getDayNightRatio();
199 return time_to_daynight_ratio(m_time_of_day);
206 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
209 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
210 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
211 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
218 void ActiveBlockList::update(core::list<v3s16> &active_positions,
220 core::map<v3s16, bool> &blocks_removed,
221 core::map<v3s16, bool> &blocks_added)
226 core::map<v3s16, bool> newlist;
227 for(core::list<v3s16>::Iterator i = active_positions.begin();
228 i != active_positions.end(); i++)
230 fillRadiusBlock(*i, radius, newlist);
234 Find out which blocks on the old list are not on the new list
236 // Go through old list
237 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
238 i.atEnd()==false; i++)
240 v3s16 p = i.getNode()->getKey();
241 // If not on new list, it's been removed
242 if(newlist.find(p) == NULL)
243 blocks_removed.insert(p, true);
247 Find out which blocks on the new list are not on the old list
249 // Go through new list
250 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251 i.atEnd()==false; i++)
253 v3s16 p = i.getNode()->getKey();
254 // If not on old list, it's been added
255 if(m_list.find(p) == NULL)
256 blocks_added.insert(p, true);
263 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
264 i.atEnd()==false; i++)
266 v3s16 p = i.getNode()->getKey();
267 m_list.insert(p, true);
275 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
276 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
281 m_random_spawn_timer(3),
282 m_send_recommended_timer(0),
284 m_game_time_fraction_counter(0)
288 ServerEnvironment::~ServerEnvironment()
290 // Clear active block list.
291 // This makes the next one delete all active objects.
292 m_active_blocks.clear();
294 // Convert all objects to static and delete the active objects
295 deactivateFarObjects(true);
301 void ServerEnvironment::serializePlayers(const std::string &savedir)
303 std::string players_path = savedir + "/players";
304 fs::CreateDir(players_path);
306 core::map<Player*, bool> saved_players;
308 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
309 for(u32 i=0; i<player_files.size(); i++)
311 if(player_files[i].dir)
314 // Full path to this file
315 std::string path = players_path + "/" + player_files[i].name;
317 //infostream<<"Checking player file "<<path<<std::endl;
319 // Load player to see what is its name
320 ServerRemotePlayer testplayer(this);
322 // Open file and deserialize
323 std::ifstream is(path.c_str(), std::ios_base::binary);
324 if(is.good() == false)
326 infostream<<"Failed to read "<<path<<std::endl;
329 testplayer.deSerialize(is);
332 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
334 // Search for the player
335 std::string playername = testplayer.getName();
336 Player *player = getPlayer(playername.c_str());
339 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
343 //infostream<<"Found matching player, overwriting."<<std::endl;
345 // OK, found. Save player there.
347 // Open file and serialize
348 std::ofstream os(path.c_str(), std::ios_base::binary);
349 if(os.good() == false)
351 infostream<<"Failed to overwrite "<<path<<std::endl;
354 player->serialize(os);
355 saved_players.insert(player, true);
359 for(core::list<Player*>::Iterator i = m_players.begin();
360 i != m_players.end(); i++)
363 if(saved_players.find(player) != NULL)
365 /*infostream<<"Player "<<player->getName()
366 <<" was already saved."<<std::endl;*/
369 std::string playername = player->getName();
370 // Don't save unnamed player
373 //infostream<<"Not saving unnamed player."<<std::endl;
379 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
380 playername = "player";
381 std::string path = players_path + "/" + playername;
383 for(u32 i=0; i<1000; i++)
385 if(fs::PathExists(path) == false)
390 path = players_path + "/" + playername + itos(i);
394 infostream<<"Didn't find free file for player"<<std::endl;
399 /*infostream<<"Saving player "<<player->getName()<<" to "
401 // Open file and serialize
402 std::ofstream os(path.c_str(), std::ios_base::binary);
403 if(os.good() == false)
405 infostream<<"Failed to overwrite "<<path<<std::endl;
408 player->serialize(os);
409 saved_players.insert(player, true);
413 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
416 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
418 std::string players_path = savedir + "/players";
420 core::map<Player*, bool> saved_players;
422 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
423 for(u32 i=0; i<player_files.size(); i++)
425 if(player_files[i].dir)
428 // Full path to this file
429 std::string path = players_path + "/" + player_files[i].name;
431 infostream<<"Checking player file "<<path<<std::endl;
433 // Load player to see what is its name
434 ServerRemotePlayer testplayer(this);
436 // Open file and deserialize
437 std::ifstream is(path.c_str(), std::ios_base::binary);
438 if(is.good() == false)
440 infostream<<"Failed to read "<<path<<std::endl;
443 testplayer.deSerialize(is);
446 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
448 infostream<<"Not loading player with invalid name: "
449 <<testplayer.getName()<<std::endl;
452 infostream<<"Loaded test player with name "<<testplayer.getName()
455 // Search for the player
456 std::string playername = testplayer.getName();
457 Player *player = getPlayer(playername.c_str());
458 bool newplayer = false;
461 infostream<<"Is a new player"<<std::endl;
462 player = new ServerRemotePlayer(this);
468 infostream<<"Reading player "<<testplayer.getName()<<" from "
470 // Open file and deserialize
471 std::ifstream is(path.c_str(), std::ios_base::binary);
472 if(is.good() == false)
474 infostream<<"Failed to read "<<path<<std::endl;
477 player->deSerialize(is);
485 void ServerEnvironment::saveMeta(const std::string &savedir)
487 std::string path = savedir + "/env_meta.txt";
489 // Open file and serialize
490 std::ofstream os(path.c_str(), std::ios_base::binary);
491 if(os.good() == false)
493 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
495 throw SerializationError("Couldn't save env meta");
499 args.setU64("game_time", m_game_time);
500 args.setU64("time_of_day", getTimeOfDay());
505 void ServerEnvironment::loadMeta(const std::string &savedir)
507 std::string path = savedir + "/env_meta.txt";
509 // Open file and deserialize
510 std::ifstream is(path.c_str(), std::ios_base::binary);
511 if(is.good() == false)
513 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
515 throw SerializationError("Couldn't load env meta");
523 throw SerializationError
524 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
526 std::getline(is, line);
527 std::string trimmedline = trim(line);
528 if(trimmedline == "EnvArgsEnd")
530 args.parseConfigLine(line);
534 m_game_time = args.getU64("game_time");
535 }catch(SettingNotFoundException &e){
536 // Getting this is crucial, otherwise timestamps are useless
537 throw SerializationError("Couldn't load env meta game_time");
541 m_time_of_day = args.getU64("time_of_day");
542 }catch(SettingNotFoundException &e){
543 // This is not as important
544 m_time_of_day = 9000;
549 // This is probably very useless
550 void spawnRandomObjects(MapBlock *block)
552 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
553 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
555 bool last_node_walkable = false;
556 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
559 MapNode n = block->getNodeNoEx(p);
560 if(n.getContent() == CONTENT_IGNORE)
562 if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
564 if(m_gamedef->ndef()->get(n).walkable)
566 last_node_walkable = true;
569 if(last_node_walkable)
571 // If block contains light information
572 if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
574 if(n.getLight(LIGHTBANK_DAY) <= 5)
576 if(myrand() % 1000 == 0)
578 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
580 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
581 std::string data = obj->getStaticData();
582 StaticObject s_obj(obj->getType(),
583 obj->getBasePosition(), data);
585 block->m_static_objects.insert(0, s_obj);
587 block->raiseModified(MOD_STATE_WRITE_NEEDED,
588 "spawnRandomObjects");
593 last_node_walkable = false;
599 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
601 // Get time difference
603 u32 stamp = block->getTimestamp();
604 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
605 dtime_s = m_game_time - block->getTimestamp();
606 dtime_s += additional_dtime;
608 // Set current time as timestamp (and let it set ChangedFlag)
609 block->setTimestamp(m_game_time);
611 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
613 // Activate stored objects
614 activateObjects(block);
617 bool changed = block->m_node_metadata->step((float)dtime_s);
621 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
622 event.p = block->getPos();
623 m_map->dispatchEvent(&event);
625 block->raiseModified(MOD_STATE_WRITE_NEEDED,
626 "node metadata modified in activateBlock");
629 // TODO: Do something
630 // TODO: Implement usage of ActiveBlockModifier
632 // Here's a quick demonstration
634 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
635 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
636 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
638 v3s16 p = p0 + block->getPosRelative();
639 MapNode n = block->getNodeNoEx(p0);
642 // Convert all mud under proper day lighting to grass
643 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
647 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
648 if(m_gamedef->ndef()->get(n_top).light_propagates &&
649 !m_gamedef->ndef()->get(n_top).isLiquid() &&
650 n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
652 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
653 m_map->addNodeWithEvent(p, n);
661 void ServerEnvironment::clearAllObjects()
663 infostream<<"ServerEnvironment::clearAllObjects(): "
664 <<"Removing all active objects"<<std::endl;
665 core::list<u16> objects_to_remove;
666 for(core::map<u16, ServerActiveObject*>::Iterator
667 i = m_active_objects.getIterator();
668 i.atEnd()==false; i++)
670 ServerActiveObject* obj = i.getNode()->getValue();
671 u16 id = i.getNode()->getKey();
672 v3f objectpos = obj->getBasePosition();
673 // Delete static object if block is loaded
674 if(obj->m_static_exists){
675 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
677 block->m_static_objects.remove(id);
678 block->raiseModified(MOD_STATE_WRITE_NEEDED,
680 obj->m_static_exists = false;
683 // If known by some client, don't delete immediately
684 if(obj->m_known_by_count > 0){
685 obj->m_pending_deactivation = true;
686 obj->m_removed = true;
689 // Deregister in scripting api
690 scriptapi_rm_object_reference(m_lua, obj);
691 // Delete active object
693 // Id to be removed from m_active_objects
694 objects_to_remove.push_back(id);
696 // Remove references from m_active_objects
697 for(core::list<u16>::Iterator i = objects_to_remove.begin();
698 i != objects_to_remove.end(); i++)
700 m_active_objects.remove(*i);
703 core::list<v3s16> loadable_blocks;
704 infostream<<"ServerEnvironment::clearAllObjects(): "
705 <<"Listing all loadable blocks"<<std::endl;
706 m_map->listAllLoadableBlocks(loadable_blocks);
707 infostream<<"ServerEnvironment::clearAllObjects(): "
708 <<"Done listing all loadable blocks: "
709 <<loadable_blocks.size()
710 <<", now clearing"<<std::endl;
711 u32 report_interval = loadable_blocks.size() / 10;
712 u32 num_blocks_checked = 0;
713 u32 num_blocks_cleared = 0;
714 u32 num_objs_cleared = 0;
715 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
716 i != loadable_blocks.end(); i++)
719 MapBlock *block = m_map->emergeBlock(p, false);
721 errorstream<<"ServerEnvironment::clearAllObjects(): "
722 <<"Failed to emerge block "<<PP(p)<<std::endl;
725 u32 num_stored = block->m_static_objects.m_stored.size();
726 u32 num_active = block->m_static_objects.m_active.size();
727 if(num_stored != 0 || num_active != 0){
728 block->m_static_objects.m_stored.clear();
729 block->m_static_objects.m_active.clear();
730 block->raiseModified(MOD_STATE_WRITE_NEEDED,
732 num_objs_cleared += num_stored + num_active;
733 num_blocks_cleared++;
735 num_blocks_checked++;
737 if(num_blocks_checked % report_interval == 0){
738 float percent = 100.0 * (float)num_blocks_checked /
739 loadable_blocks.size();
740 infostream<<"ServerEnvironment::clearAllObjects(): "
741 <<"Cleared "<<num_objs_cleared<<" objects"
742 <<" in "<<num_blocks_cleared<<" blocks ("
743 <<percent<<"%)"<<std::endl;
746 infostream<<"ServerEnvironment::clearAllObjects(): "
747 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
748 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
751 static void getMob_dungeon_master(Settings &properties)
753 properties.set("looks", "dungeon_master");
754 properties.setFloat("yaw", 1.57);
755 properties.setFloat("hp", 30);
756 properties.setBool("bright_shooting", true);
757 properties.set("shoot_type", "fireball");
758 properties.set("shoot_y", "0.7");
759 properties.set("player_hit_damage", "1");
760 properties.set("player_hit_distance", "1.0");
761 properties.set("player_hit_interval", "0.5");
762 properties.setBool("mindless_rage", myrand_range(0,100)==0);
765 void ServerEnvironment::step(float dtime)
767 DSTACK(__FUNCTION_NAME);
769 //TimeTaker timer("ServerEnv step");
772 bool footprints = g_settings->getBool("footprints");
778 m_game_time_fraction_counter += dtime;
779 u32 inc_i = (u32)m_game_time_fraction_counter;
780 m_game_time += inc_i;
781 m_game_time_fraction_counter -= (float)inc_i;
788 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
789 for(core::list<Player*>::Iterator i = m_players.begin();
790 i != m_players.end(); i++)
794 // Ignore disconnected players
795 if(player->peer_id == 0)
798 v3f playerpos = player->getPosition();
801 player->move(dtime, *m_map, 100*BS);
804 Add footsteps to grass
808 // Get node that is at BS/4 under player
809 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
811 MapNode n = m_map->getNode(bottompos);
812 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
814 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
815 m_map->setNode(bottompos, n);
818 catch(InvalidPositionException &e)
826 Manage active block list
828 if(m_active_blocks_management_interval.step(dtime, 2.0))
830 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
832 Get player block positions
834 core::list<v3s16> players_blockpos;
835 for(core::list<Player*>::Iterator
836 i = m_players.begin();
837 i != m_players.end(); i++)
840 // Ignore disconnected players
841 if(player->peer_id == 0)
843 v3s16 blockpos = getNodeBlockPos(
844 floatToInt(player->getPosition(), BS));
845 players_blockpos.push_back(blockpos);
849 Update list of active blocks, collecting changes
851 const s16 active_block_range = g_settings->getS16("active_block_range");
852 core::map<v3s16, bool> blocks_removed;
853 core::map<v3s16, bool> blocks_added;
854 m_active_blocks.update(players_blockpos, active_block_range,
855 blocks_removed, blocks_added);
858 Handle removed blocks
861 // Convert active objects that are no more in active blocks to static
862 deactivateFarObjects(false);
864 for(core::map<v3s16, bool>::Iterator
865 i = blocks_removed.getIterator();
866 i.atEnd()==false; i++)
868 v3s16 p = i.getNode()->getKey();
870 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
871 <<") became inactive"<<std::endl;*/
873 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
877 // Set current time as timestamp (and let it set ChangedFlag)
878 block->setTimestamp(m_game_time);
885 for(core::map<v3s16, bool>::Iterator
886 i = blocks_added.getIterator();
887 i.atEnd()==false; i++)
889 v3s16 p = i.getNode()->getKey();
891 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
892 <<") became active"<<std::endl;*/
894 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
896 // Block needs to be fetched first
897 m_emerger->queueBlockEmerge(p, false);
898 m_active_blocks.m_list.remove(p);
902 activateBlock(block);
907 Mess around in active blocks
909 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
911 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
915 for(core::map<v3s16, bool>::Iterator
916 i = m_active_blocks.m_list.getIterator();
917 i.atEnd()==false; i++)
919 v3s16 p = i.getNode()->getKey();
921 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
922 <<") being handled"<<std::endl;*/
924 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
928 // Reset block usage timer
929 block->resetUsageTimer();
931 // Set current time as timestamp
932 block->setTimestampNoChangedFlag(m_game_time);
935 bool changed = block->m_node_metadata->step(dtime);
939 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
941 m_map->dispatchEvent(&event);
943 block->raiseModified(MOD_STATE_WRITE_NEEDED,
944 "node metadata modified in step");
949 if(m_active_blocks_test_interval.step(dtime, 10.0))
951 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
952 //float dtime = 10.0;
953 TimeTaker timer("modify in active blocks");
955 INodeDefManager *ndef = m_gamedef->ndef();
957 // Pre-fetch content ids for the luge loop
958 content_t c_dirt = ndef->getId("dirt");
959 content_t c_grass = ndef->getId("dirt_with_grass");
960 content_t c_tree = ndef->getId("tree");
961 content_t c_jungletree = ndef->getId("jungletree");
962 content_t c_stone = ndef->getId("stone");
963 content_t c_mossycobble = ndef->getId("mossycobble");
964 content_t c_sapling = ndef->getId("sapling");
966 for(core::map<v3s16, bool>::Iterator
967 i = m_active_blocks.m_list.getIterator();
968 i.atEnd()==false; i++)
970 v3s16 p = i.getNode()->getKey();
972 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
973 <<") being handled"<<std::endl;*/
975 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
979 // Set current time as timestamp
980 block->setTimestampNoChangedFlag(m_game_time);
985 Note that map modifications should be done using the event-
986 making map methods so that the server gets information
989 Reading can be done quickly directly from the block.
991 Everything should bind to inside this single content
992 searching loop to keep things fast.
994 // TODO: Implement usage of ActiveBlockModifier
996 // Find out how many objects the block contains
997 //u32 active_object_count = block->m_static_objects.m_active.size();
998 // Find out how many objects this and all the neighbors contain
999 u32 active_object_count_wider = 0;
1000 for(s16 x=-1; x<=1; x++)
1001 for(s16 y=-1; y<=1; y++)
1002 for(s16 z=-1; z<=1; z++)
1004 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
1007 active_object_count_wider +=
1008 block->m_static_objects.m_active.size()
1009 + block->m_static_objects.m_stored.size();
1011 /*if(block->m_static_objects.m_stored.size() != 0){
1012 errorstream<<"ServerEnvironment::step(): "
1013 <<PP(block->getPos())<<" contains "
1014 <<block->m_static_objects.m_stored.size()
1015 <<" stored objects; "
1016 <<"when spawning objects, when counting active "
1017 <<"objects in wide area. relative position: "
1018 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
1023 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
1024 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
1025 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
1027 v3s16 p = p0 + block->getPosRelative();
1028 MapNode n = block->getNodeNoEx(p0);
1032 Convert dirt under proper lighting to grass
1034 if(n.getContent() == c_dirt)
1036 if(myrand()%20 == 0)
1038 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1039 if(m_gamedef->ndef()->get(n_top).light_propagates &&
1040 !m_gamedef->ndef()->get(n_top).isLiquid() &&
1041 n_top.getLightBlend(getDayNightRatio(),
1042 m_gamedef->ndef()) >= 13)
1044 n.setContent(c_grass);
1045 m_map->addNodeWithEvent(p, n);
1050 Convert grass into dirt if under something else than air
1052 if(n.getContent() == c_grass)
1054 //if(myrand()%20 == 0)
1056 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1057 if(!m_gamedef->ndef()->get(n_top).light_propagates ||
1058 m_gamedef->ndef()->get(n_top).isLiquid())
1060 n.setContent(c_dirt);
1061 m_map->addNodeWithEvent(p, n);
1066 Rats spawn around regular trees
1068 if(n.getContent() == c_tree ||
1069 n.getContent() == c_jungletree)
1071 if(myrand()%200 == 0 && active_object_count_wider == 0)
1073 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1074 0, myrand_range(-2, 2));
1075 MapNode n1 = m_map->getNodeNoEx(p1);
1076 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1077 if(n1b.getContent() == c_grass &&
1078 n1.getContent() == CONTENT_AIR)
1080 v3f pos = intToFloat(p1, BS);
1081 ServerActiveObject *obj = new RatSAO(this, pos);
1082 addActiveObject(obj);
1087 Fun things spawn in caves and dungeons
1089 if(n.getContent() == c_stone ||
1090 n.getContent() == c_mossycobble)
1092 if(myrand()%200 == 0 && active_object_count_wider == 0)
1094 v3s16 p1 = p + v3s16(0,1,0);
1095 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1096 if(n1a.getLightBlend(getDayNightRatio(),
1097 m_gamedef->ndef()) <= 3){
1098 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1099 if(n1a.getContent() == CONTENT_AIR &&
1100 n1b.getContent() == CONTENT_AIR)
1102 v3f pos = intToFloat(p1, BS);
1104 if(i == 0 || i == 1){
1105 actionstream<<"A dungeon master spawns at "
1106 <<PP(p1)<<std::endl;
1107 Settings properties;
1108 getMob_dungeon_master(properties);
1109 ServerActiveObject *obj = new MobV2SAO(
1110 this, pos, &properties);
1111 addActiveObject(obj);
1112 } else if(i == 2 || i == 3){
1113 actionstream<<"Rats spawn at "
1114 <<PP(p1)<<std::endl;
1115 for(int j=0; j<3; j++){
1116 ServerActiveObject *obj = new RatSAO(
1118 addActiveObject(obj);
1121 actionstream<<"An oerkki spawns at "
1122 <<PP(p1)<<std::endl;
1123 ServerActiveObject *obj = new Oerkki1SAO(
1125 addActiveObject(obj);
1132 Make trees from saplings!
1134 if(n.getContent() == c_sapling)
1136 if(myrand()%50 == 0)
1138 actionstream<<"A sapling grows into a tree at "
1141 core::map<v3s16, MapBlock*> modified_blocks;
1143 ManualMapVoxelManipulator vmanip(m_map);
1144 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1145 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1146 bool is_apple_tree = myrand()%4 == 0;
1147 mapgen::make_tree(vmanip, tree_p, is_apple_tree,
1149 vmanip.blitBackAll(&modified_blocks);
1152 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1153 for(core::map<v3s16, MapBlock*>::Iterator
1154 i = modified_blocks.getIterator();
1155 i.atEnd() == false; i++)
1157 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1159 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1161 // Send a MEET_OTHER event
1163 event.type = MEET_OTHER;
1164 for(core::map<v3s16, MapBlock*>::Iterator
1165 i = modified_blocks.getIterator();
1166 i.atEnd() == false; i++)
1168 v3s16 p = i.getNode()->getKey();
1169 event.modified_blocks.insert(p, true);
1171 m_map->dispatchEvent(&event);
1177 u32 time_ms = timer.stop(true);
1178 u32 max_time_ms = 200;
1179 if(time_ms > max_time_ms){
1180 infostream<<"WARNING: active block modifiers took "
1181 <<time_ms<<"ms (longer than "
1182 <<max_time_ms<<"ms)"<<std::endl;
1187 Step script environment (run global on_step())
1189 scriptapi_environment_step(m_lua, dtime);
1195 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1196 //TimeTaker timer("Step active objects");
1198 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1200 // This helps the objects to send data at the same time
1201 bool send_recommended = false;
1202 m_send_recommended_timer += dtime;
1203 if(m_send_recommended_timer > getSendRecommendedInterval())
1205 m_send_recommended_timer -= getSendRecommendedInterval();
1206 send_recommended = true;
1209 for(core::map<u16, ServerActiveObject*>::Iterator
1210 i = m_active_objects.getIterator();
1211 i.atEnd()==false; i++)
1213 ServerActiveObject* obj = i.getNode()->getValue();
1214 // Remove non-peaceful mobs on peaceful mode
1215 if(g_settings->getBool("only_peaceful_mobs")){
1216 if(!obj->isPeaceful())
1217 obj->m_removed = true;
1219 // Don't step if is to be removed or stored statically
1220 if(obj->m_removed || obj->m_pending_deactivation)
1223 obj->step(dtime, send_recommended);
1224 // Read messages from object
1225 while(obj->m_messages_out.size() > 0)
1227 m_active_object_messages.push_back(
1228 obj->m_messages_out.pop_front());
1234 Manage active objects
1236 if(m_object_management_interval.step(dtime, 0.5))
1238 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1240 Remove objects that satisfy (m_removed && m_known_by_count==0)
1242 removeRemovedObjects();
1245 if(g_settings->getBool("enable_experimental"))
1252 m_random_spawn_timer -= dtime;
1253 if(m_random_spawn_timer < 0)
1255 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1256 //m_random_spawn_timer += 2.0;
1257 m_random_spawn_timer += 200.0;
1263 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1264 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1265 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1267 Player *player = getRandomConnectedPlayer();
1270 pos = player->getPosition();
1272 myrand_range(-3,3)*BS,
1274 myrand_range(-3,3)*BS
1278 Create a ServerActiveObject
1281 //TestSAO *obj = new TestSAO(this, pos);
1282 //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
1283 //ServerActiveObject *obj = new RatSAO(this, pos);
1284 //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
1285 //ServerActiveObject *obj = new FireflySAO(this, pos);
1287 infostream<<"Server: Spawning MobV2SAO at "
1288 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1290 Settings properties;
1291 getMob_dungeon_master(properties);
1292 ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
1293 addActiveObject(obj);
1297 } // enable_experimental
1300 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1302 core::map<u16, ServerActiveObject*>::Node *n;
1303 n = m_active_objects.find(id);
1306 return n->getValue();
1309 bool isFreeServerActiveObjectId(u16 id,
1310 core::map<u16, ServerActiveObject*> &objects)
1315 for(core::map<u16, ServerActiveObject*>::Iterator
1316 i = objects.getIterator();
1317 i.atEnd()==false; i++)
1319 if(i.getNode()->getKey() == id)
1325 u16 getFreeServerActiveObjectId(
1326 core::map<u16, ServerActiveObject*> &objects)
1331 if(isFreeServerActiveObjectId(new_id, objects))
1341 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1344 u16 id = addActiveObjectRaw(object, true);
1348 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1352 v3f objectpos = obj->getBasePosition();
1354 // The block in which the object resides in
1355 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1358 Update the static data
1361 // Create new static object
1362 std::string staticdata = obj->getStaticData();
1363 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1364 // Add to the block where the object is located in
1365 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1366 // Get or generate the block
1367 MapBlock *block = m_map->emergeBlock(blockpos);
1369 bool succeeded = false;
1373 block->m_static_objects.insert(0, s_obj);
1374 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1375 "addActiveObjectAsStatic");
1379 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1380 <<"Could not find or generate "
1381 <<"a block for storing static object"<<std::endl;
1391 Finds out what new objects have been added to
1392 inside a radius around a position
1394 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1395 core::map<u16, bool> ¤t_objects,
1396 core::map<u16, bool> &added_objects)
1398 v3f pos_f = intToFloat(pos, BS);
1399 f32 radius_f = radius * BS;
1401 Go through the object list,
1402 - discard m_removed objects,
1403 - discard objects that are too far away,
1404 - discard objects that are found in current_objects.
1405 - add remaining objects to added_objects
1407 for(core::map<u16, ServerActiveObject*>::Iterator
1408 i = m_active_objects.getIterator();
1409 i.atEnd()==false; i++)
1411 u16 id = i.getNode()->getKey();
1413 ServerActiveObject *object = i.getNode()->getValue();
1416 // Discard if removed
1417 if(object->m_removed)
1419 // Discard if too far
1420 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1421 if(distance_f > radius_f)
1423 // Discard if already on current_objects
1424 core::map<u16, bool>::Node *n;
1425 n = current_objects.find(id);
1428 // Add to added_objects
1429 added_objects.insert(id, false);
1434 Finds out what objects have been removed from
1435 inside a radius around a position
1437 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1438 core::map<u16, bool> ¤t_objects,
1439 core::map<u16, bool> &removed_objects)
1441 v3f pos_f = intToFloat(pos, BS);
1442 f32 radius_f = radius * BS;
1444 Go through current_objects; object is removed if:
1445 - object is not found in m_active_objects (this is actually an
1446 error condition; objects should be set m_removed=true and removed
1447 only after all clients have been informed about removal), or
1448 - object has m_removed=true, or
1449 - object is too far away
1451 for(core::map<u16, bool>::Iterator
1452 i = current_objects.getIterator();
1453 i.atEnd()==false; i++)
1455 u16 id = i.getNode()->getKey();
1456 ServerActiveObject *object = getActiveObject(id);
1459 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1460 <<" object in current_objects is NULL"<<std::endl;
1462 else if(object->m_removed == false)
1464 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1465 /*infostream<<"removed == false"
1466 <<"distance_f = "<<distance_f
1467 <<", radius_f = "<<radius_f<<std::endl;*/
1468 if(distance_f < radius_f)
1474 removed_objects.insert(id, false);
1478 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1480 if(m_active_object_messages.size() == 0)
1481 return ActiveObjectMessage(0);
1483 return m_active_object_messages.pop_front();
1487 ************ Private methods *************
1490 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1494 if(object->getId() == 0){
1495 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1498 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1499 <<"no free ids available"<<std::endl;
1503 object->setId(new_id);
1506 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1507 <<"supplied with id "<<object->getId()<<std::endl;
1509 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1511 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1512 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1516 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1517 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1519 m_active_objects.insert(object->getId(), object);
1521 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1522 <<"Added id="<<object->getId()<<"; there are now "
1523 <<m_active_objects.size()<<" active objects."
1526 // Add static object to active static list of the block
1527 v3f objectpos = object->getBasePosition();
1528 std::string staticdata = object->getStaticData();
1529 StaticObject s_obj(object->getType(), objectpos, staticdata);
1530 // Add to the block where the object is located in
1531 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1532 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1535 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1536 object->m_static_exists = true;
1537 object->m_static_block = blockpos;
1540 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1541 "addActiveObjectRaw");
1544 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1545 <<"could not find block for storing id="<<object->getId()
1546 <<" statically"<<std::endl;
1549 // Register reference in scripting api (must be done before post-init)
1550 scriptapi_add_object_reference(m_lua, object);
1551 // Post-initialize object
1552 object->addedToEnvironment();
1554 return object->getId();
1558 Remove objects that satisfy (m_removed && m_known_by_count==0)
1560 void ServerEnvironment::removeRemovedObjects()
1562 core::list<u16> objects_to_remove;
1563 for(core::map<u16, ServerActiveObject*>::Iterator
1564 i = m_active_objects.getIterator();
1565 i.atEnd()==false; i++)
1567 u16 id = i.getNode()->getKey();
1568 ServerActiveObject* obj = i.getNode()->getValue();
1569 // This shouldn't happen but check it
1572 infostream<<"NULL object found in ServerEnvironment"
1573 <<" while finding removed objects. id="<<id<<std::endl;
1574 // Id to be removed from m_active_objects
1575 objects_to_remove.push_back(id);
1580 We will delete objects that are marked as removed or thatare
1581 waiting for deletion after deactivation
1583 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1587 Delete static data from block if is marked as removed
1589 if(obj->m_static_exists && obj->m_removed)
1591 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1594 block->m_static_objects.remove(id);
1595 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1596 "removeRemovedObjects");
1597 obj->m_static_exists = false;
1601 // If m_known_by_count > 0, don't actually remove.
1602 if(obj->m_known_by_count > 0)
1605 // Deregister in scripting api
1606 scriptapi_rm_object_reference(m_lua, obj);
1610 // Id to be removed from m_active_objects
1611 objects_to_remove.push_back(id);
1613 // Remove references from m_active_objects
1614 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1615 i != objects_to_remove.end(); i++)
1617 m_active_objects.remove(*i);
1621 static void print_hexdump(std::ostream &o, const std::string &data)
1623 const int linelength = 16;
1624 for(int l=0; ; l++){
1625 int i0 = linelength * l;
1626 bool at_end = false;
1627 int thislinelength = linelength;
1628 if(i0 + thislinelength > (int)data.size()){
1629 thislinelength = data.size() - i0;
1632 for(int di=0; di<linelength; di++){
1635 if(di<thislinelength)
1636 snprintf(buf, 4, "%.2x ", data[i]);
1638 snprintf(buf, 4, " ");
1642 for(int di=0; di<thislinelength; di++){
1656 Convert stored objects from blocks near the players to active.
1658 void ServerEnvironment::activateObjects(MapBlock *block)
1662 // Ignore if no stored objects (to not set changed flag)
1663 if(block->m_static_objects.m_stored.size() == 0)
1665 verbosestream<<"ServerEnvironment::activateObjects(): "
1666 <<"activating objects of block "<<PP(block->getPos())
1667 <<" ("<<block->m_static_objects.m_stored.size()
1668 <<" objects)"<<std::endl;
1669 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1671 errorstream<<"suspiciously large amount of objects detected: "
1672 <<block->m_static_objects.m_stored.size()<<" in "
1673 <<PP(block->getPos())
1674 <<"; removing all of them."<<std::endl;
1675 // Clear stored list
1676 block->m_static_objects.m_stored.clear();
1677 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1678 "stored list cleared in activateObjects due to "
1679 "large amount of objects");
1682 // A list for objects that couldn't be converted to static for some
1683 // reason. They will be stored back.
1684 core::list<StaticObject> new_stored;
1685 // Loop through stored static objects
1686 for(core::list<StaticObject>::Iterator
1687 i = block->m_static_objects.m_stored.begin();
1688 i != block->m_static_objects.m_stored.end(); i++)
1690 /*infostream<<"Server: Creating an active object from "
1691 <<"static data"<<std::endl;*/
1692 StaticObject &s_obj = *i;
1693 // Create an active object from the data
1694 ServerActiveObject *obj = ServerActiveObject::create
1695 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1696 // If couldn't create object, store static data back.
1699 errorstream<<"ServerEnvironment::activateObjects(): "
1700 <<"failed to create active object from static object "
1701 <<"in block "<<PP(s_obj.pos/BS)
1702 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1703 print_hexdump(verbosestream, s_obj.data);
1705 new_stored.push_back(s_obj);
1708 verbosestream<<"ServerEnvironment::activateObjects(): "
1709 <<"activated static object pos="<<PP(s_obj.pos/BS)
1710 <<" type="<<(int)s_obj.type<<std::endl;
1711 // This will also add the object to the active static list
1712 addActiveObjectRaw(obj, false);
1714 // Clear stored list
1715 block->m_static_objects.m_stored.clear();
1716 // Add leftover failed stuff to stored list
1717 for(core::list<StaticObject>::Iterator
1718 i = new_stored.begin();
1719 i != new_stored.end(); i++)
1721 StaticObject &s_obj = *i;
1722 block->m_static_objects.m_stored.push_back(s_obj);
1725 Note: Block hasn't really been modified here.
1726 The objects have just been activated and moved from the stored
1727 static list to the active static list.
1728 As such, the block is essentially the same.
1729 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1730 Otherwise there would be a huge amount of unnecessary I/O.
1735 Convert objects that are not standing inside active blocks to static.
1737 If m_known_by_count != 0, active object is not deleted, but static
1738 data is still updated.
1740 If force_delete is set, active object is deleted nevertheless. It
1741 shall only be set so in the destructor of the environment.
1743 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1745 core::list<u16> objects_to_remove;
1746 for(core::map<u16, ServerActiveObject*>::Iterator
1747 i = m_active_objects.getIterator();
1748 i.atEnd()==false; i++)
1750 ServerActiveObject* obj = i.getNode()->getValue();
1752 // This shouldn't happen but check it
1755 errorstream<<"NULL object found in ServerEnvironment"
1761 // If pending deactivation, let removeRemovedObjects() do it
1762 if(obj->m_pending_deactivation)
1765 u16 id = i.getNode()->getKey();
1766 v3f objectpos = obj->getBasePosition();
1768 // The block in which the object resides in
1769 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1771 // If block is active, don't remove
1772 if(m_active_blocks.contains(blockpos_o))
1775 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1776 <<"deactivating object id="<<id<<" on inactive block "
1777 <<PP(blockpos_o)<<std::endl;
1779 // If known by some client, don't immediately delete.
1780 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1783 Update the static data
1786 // Create new static object
1787 std::string staticdata_new = obj->getStaticData();
1788 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1790 bool stays_in_same_block = false;
1791 bool data_changed = true;
1793 if(obj->m_static_exists){
1794 if(obj->m_static_block == blockpos_o)
1795 stays_in_same_block = true;
1797 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1799 core::map<u16, StaticObject>::Node *n =
1800 block->m_static_objects.m_active.find(id);
1802 StaticObject static_old = n->getValue();
1804 float save_movem = obj->getMinimumSavedMovement();
1806 if(static_old.data == staticdata_new &&
1807 (static_old.pos - objectpos).getLength() < save_movem)
1808 data_changed = false;
1810 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1811 <<"id="<<id<<" m_static_exists=true but "
1812 <<"static data doesn't actually exist in "
1813 <<PP(obj->m_static_block)<<std::endl;
1817 bool shall_be_written = (!stays_in_same_block || data_changed);
1819 // Delete old static object
1820 if(obj->m_static_exists)
1822 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1825 block->m_static_objects.remove(id);
1826 obj->m_static_exists = false;
1827 // Only mark block as modified if data changed considerably
1828 if(shall_be_written)
1829 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1830 "deactivateFarObjects: Static data "
1831 "changed considerably");
1835 // Add to the block where the object is located in
1836 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1837 // Get or generate the block
1838 MapBlock *block = m_map->emergeBlock(blockpos);
1842 if(block->m_static_objects.m_stored.size() >= 49){
1843 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1844 <<" statically but block "<<PP(blockpos)
1845 <<" already contains "
1846 <<block->m_static_objects.m_stored.size()
1847 <<" (over 49) objects."
1848 <<" Forcing delete."<<std::endl;
1849 force_delete = true;
1851 u16 new_id = pending_delete ? id : 0;
1852 block->m_static_objects.insert(new_id, s_obj);
1854 // Only mark block as modified if data changed considerably
1855 if(shall_be_written)
1856 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1857 "deactivateFarObjects: Static data "
1858 "changed considerably");
1860 obj->m_static_exists = true;
1861 obj->m_static_block = block->getPos();
1865 errorstream<<"ServerEnv: Could not find or generate "
1866 <<"a block for storing id="<<obj->getId()
1867 <<" statically"<<std::endl;
1872 If known by some client, set pending deactivation.
1873 Otherwise delete it immediately.
1878 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1879 <<"object id="<<id<<" is known by clients"
1880 <<"; not deleting yet"<<std::endl;
1882 obj->m_pending_deactivation = true;
1886 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1887 <<"object id="<<id<<" is not known by clients"
1888 <<"; deleting"<<std::endl;
1890 // Deregister in scripting api
1891 scriptapi_rm_object_reference(m_lua, obj);
1893 // Delete active object
1895 // Id to be removed from m_active_objects
1896 objects_to_remove.push_back(id);
1899 // Remove references from m_active_objects
1900 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1901 i != objects_to_remove.end(); i++)
1903 m_active_objects.remove(*i);
1914 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1915 ITextureSource *texturesource, IGameDef *gamedef):
1918 m_texturesource(texturesource),
1925 ClientEnvironment::~ClientEnvironment()
1927 // delete active objects
1928 for(core::map<u16, ClientActiveObject*>::Iterator
1929 i = m_active_objects.getIterator();
1930 i.atEnd()==false; i++)
1932 delete i.getNode()->getValue();
1939 void ClientEnvironment::addPlayer(Player *player)
1941 DSTACK(__FUNCTION_NAME);
1943 It is a failure if player is local and there already is a local
1946 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1948 Environment::addPlayer(player);
1951 LocalPlayer * ClientEnvironment::getLocalPlayer()
1953 for(core::list<Player*>::Iterator i = m_players.begin();
1954 i != m_players.end(); i++)
1956 Player *player = *i;
1957 if(player->isLocal())
1958 return (LocalPlayer*)player;
1963 void ClientEnvironment::step(float dtime)
1965 DSTACK(__FUNCTION_NAME);
1967 // Get some settings
1968 bool free_move = g_settings->getBool("free_move");
1969 bool footprints = g_settings->getBool("footprints");
1972 LocalPlayer *lplayer = getLocalPlayer();
1974 // collision info queue
1975 core::list<CollisionInfo> player_collisions;
1978 Get the speed the player is going
1980 bool is_climbing = lplayer->is_climbing;
1982 f32 player_speed = lplayer->getSpeed().getLength();
1985 Maximum position increment
1987 //f32 position_max_increment = 0.05*BS;
1988 f32 position_max_increment = 0.1*BS;
1990 // Maximum time increment (for collision detection etc)
1991 // time = distance / speed
1992 f32 dtime_max_increment = 1;
1993 if(player_speed > 0.001)
1994 dtime_max_increment = position_max_increment / player_speed;
1996 // Maximum time increment is 10ms or lower
1997 if(dtime_max_increment > 0.01)
1998 dtime_max_increment = 0.01;
2000 // Don't allow overly huge dtime
2004 f32 dtime_downcount = dtime;
2007 Stuff that has a maximum time increment
2016 if(dtime_downcount > dtime_max_increment)
2018 dtime_part = dtime_max_increment;
2019 dtime_downcount -= dtime_part;
2023 dtime_part = dtime_downcount;
2025 Setting this to 0 (no -=dtime_part) disables an infinite loop
2026 when dtime_part is so small that dtime_downcount -= dtime_part
2029 dtime_downcount = 0;
2037 v3f lplayerpos = lplayer->getPosition();
2040 if(free_move == false && is_climbing == false)
2043 v3f speed = lplayer->getSpeed();
2044 if(lplayer->swimming_up == false)
2045 speed.Y -= 9.81 * BS * dtime_part * 2;
2048 if(lplayer->in_water_stable || lplayer->in_water)
2050 f32 max_down = 2.0*BS;
2051 if(speed.Y < -max_down) speed.Y = -max_down;
2054 if(speed.getLength() > max)
2056 speed = speed / speed.getLength() * max;
2060 lplayer->setSpeed(speed);
2065 This also does collision detection.
2067 lplayer->move(dtime_part, *m_map, position_max_increment,
2068 &player_collisions);
2071 while(dtime_downcount > 0.001);
2073 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2075 for(core::list<CollisionInfo>::Iterator
2076 i = player_collisions.begin();
2077 i != player_collisions.end(); i++)
2079 CollisionInfo &info = *i;
2080 if(info.t == COLLISION_FALL)
2082 //f32 tolerance = BS*10; // 2 without damage
2083 f32 tolerance = BS*12; // 3 without damage
2085 if(info.speed > tolerance)
2087 f32 damage_f = (info.speed - tolerance)/BS*factor;
2088 u16 damage = (u16)(damage_f+0.5);
2089 if(lplayer->hp > damage)
2090 lplayer->hp -= damage;
2094 ClientEnvEvent event;
2095 event.type = CEE_PLAYER_DAMAGE;
2096 event.player_damage.amount = damage;
2097 m_client_event_queue.push_back(event);
2103 A quick draft of lava damage
2105 if(m_lava_hurt_interval.step(dtime, 1.0))
2107 v3f pf = lplayer->getPosition();
2109 // Feet, middle and head
2110 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2111 MapNode n1 = m_map->getNodeNoEx(p1);
2112 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2113 MapNode n2 = m_map->getNodeNoEx(p2);
2114 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2115 MapNode n3 = m_map->getNodeNoEx(p2);
2117 u32 damage_per_second = 0;
2118 damage_per_second = MYMAX(damage_per_second,
2119 m_gamedef->ndef()->get(n1).damage_per_second);
2120 damage_per_second = MYMAX(damage_per_second,
2121 m_gamedef->ndef()->get(n2).damage_per_second);
2122 damage_per_second = MYMAX(damage_per_second,
2123 m_gamedef->ndef()->get(n3).damage_per_second);
2125 if(damage_per_second != 0)
2127 ClientEnvEvent event;
2128 event.type = CEE_PLAYER_DAMAGE;
2129 event.player_damage.amount = damage_per_second;
2130 m_client_event_queue.push_back(event);
2135 Stuff that can be done in an arbitarily large dtime
2137 for(core::list<Player*>::Iterator i = m_players.begin();
2138 i != m_players.end(); i++)
2140 Player *player = *i;
2141 v3f playerpos = player->getPosition();
2144 Handle non-local players
2146 if(player->isLocal() == false)
2149 player->move(dtime, *m_map, 100*BS);
2153 // Update lighting on all players on client
2154 u8 light = LIGHT_MAX;
2157 v3s16 p = player->getLightPosition();
2158 MapNode n = m_map->getNode(p);
2159 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2161 catch(InvalidPositionException &e) {}
2162 player->updateLight(light);
2165 Add footsteps to grass
2169 // Get node that is at BS/4 under player
2170 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2172 MapNode n = m_map->getNode(bottompos);
2173 if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
2175 n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
2176 m_map->setNode(bottompos, n);
2177 // Update mesh on client
2178 if(m_map->mapType() == MAPTYPE_CLIENT)
2180 v3s16 p_blocks = getNodeBlockPos(bottompos);
2181 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2182 //b->updateMesh(getDayNightRatio());
2183 b->setMeshExpired(true);
2187 catch(InvalidPositionException &e)
2194 Step active objects and update lighting of them
2197 for(core::map<u16, ClientActiveObject*>::Iterator
2198 i = m_active_objects.getIterator();
2199 i.atEnd()==false; i++)
2201 ClientActiveObject* obj = i.getNode()->getValue();
2203 obj->step(dtime, this);
2205 if(m_active_object_light_update_interval.step(dtime, 0.21))
2208 //u8 light = LIGHT_MAX;
2212 v3s16 p = obj->getLightPosition();
2213 MapNode n = m_map->getNode(p);
2214 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2216 catch(InvalidPositionException &e) {}
2217 obj->updateLight(light);
2222 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2224 m_map->updateMeshes(blockpos, getDayNightRatio());
2227 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2229 m_map->expireMeshes(only_daynight_diffed);
2232 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2234 core::map<u16, ClientActiveObject*>::Node *n;
2235 n = m_active_objects.find(id);
2238 return n->getValue();
2241 bool isFreeClientActiveObjectId(u16 id,
2242 core::map<u16, ClientActiveObject*> &objects)
2247 for(core::map<u16, ClientActiveObject*>::Iterator
2248 i = objects.getIterator();
2249 i.atEnd()==false; i++)
2251 if(i.getNode()->getKey() == id)
2257 u16 getFreeClientActiveObjectId(
2258 core::map<u16, ClientActiveObject*> &objects)
2263 if(isFreeClientActiveObjectId(new_id, objects))
2273 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2276 if(object->getId() == 0)
2278 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2281 infostream<<"ClientEnvironment::addActiveObject(): "
2282 <<"no free ids available"<<std::endl;
2286 object->setId(new_id);
2288 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2290 infostream<<"ClientEnvironment::addActiveObject(): "
2291 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2295 infostream<<"ClientEnvironment::addActiveObject(): "
2296 <<"added (id="<<object->getId()<<")"<<std::endl;
2297 m_active_objects.insert(object->getId(), object);
2298 object->addToScene(m_smgr, m_texturesource);
2299 { // Update lighting immediately
2303 v3s16 p = object->getLightPosition();
2304 MapNode n = m_map->getNode(p);
2305 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2307 catch(InvalidPositionException &e) {}
2308 object->updateLight(light);
2310 return object->getId();
2313 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2314 const std::string &init_data)
2316 ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
2319 infostream<<"ClientEnvironment::addActiveObject(): "
2320 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2327 obj->initialize(init_data);
2329 addActiveObject(obj);
2332 void ClientEnvironment::removeActiveObject(u16 id)
2334 infostream<<"ClientEnvironment::removeActiveObject(): "
2335 <<"id="<<id<<std::endl;
2336 ClientActiveObject* obj = getActiveObject(id);
2339 infostream<<"ClientEnvironment::removeActiveObject(): "
2340 <<"id="<<id<<" not found"<<std::endl;
2343 obj->removeFromScene();
2345 m_active_objects.remove(id);
2348 void ClientEnvironment::processActiveObjectMessage(u16 id,
2349 const std::string &data)
2351 ClientActiveObject* obj = getActiveObject(id);
2354 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2355 <<" got message for id="<<id<<", which doesn't exist."
2359 obj->processMessage(data);
2363 Callbacks for activeobjects
2366 void ClientEnvironment::damageLocalPlayer(u8 damage)
2368 LocalPlayer *lplayer = getLocalPlayer();
2371 if(lplayer->hp > damage)
2372 lplayer->hp -= damage;
2376 ClientEnvEvent event;
2377 event.type = CEE_PLAYER_DAMAGE;
2378 event.player_damage.amount = damage;
2379 m_client_event_queue.push_back(event);
2383 Client likes to call these
2386 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2387 core::array<DistanceSortedActiveObject> &dest)
2389 for(core::map<u16, ClientActiveObject*>::Iterator
2390 i = m_active_objects.getIterator();
2391 i.atEnd()==false; i++)
2393 ClientActiveObject* obj = i.getNode()->getValue();
2395 f32 d = (obj->getPosition() - origin).getLength();
2400 DistanceSortedActiveObject dso(obj, d);
2402 dest.push_back(dso);
2406 ClientEnvEvent ClientEnvironment::getClientEvent()
2408 if(m_client_event_queue.size() == 0)
2410 ClientEnvEvent event;
2411 event.type = CEE_NONE;
2414 return m_client_event_queue.pop_front();
2417 #endif // #ifndef SERVER