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"
33 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
35 Environment::Environment():
40 Environment::~Environment()
43 for(core::list<Player*>::Iterator i = m_players.begin();
44 i != m_players.end(); i++)
50 void Environment::addPlayer(Player *player)
52 DSTACK(__FUNCTION_NAME);
54 Check that peer_ids are unique.
55 Also check that names are unique.
56 Exception: there can be multiple players with peer_id=0
58 // If peer id is non-zero, it has to be unique.
59 if(player->peer_id != 0)
60 assert(getPlayer(player->peer_id) == NULL);
61 // Name has to be unique.
62 assert(getPlayer(player->getName()) == NULL);
64 m_players.push_back(player);
67 void Environment::removePlayer(u16 peer_id)
69 DSTACK(__FUNCTION_NAME);
71 for(core::list<Player*>::Iterator i = m_players.begin();
72 i != m_players.end(); i++)
75 if(player->peer_id != peer_id)
80 // See if there is an another one
81 // (shouldn't be, but just to be sure)
86 Player * Environment::getPlayer(u16 peer_id)
88 for(core::list<Player*>::Iterator i = m_players.begin();
89 i != m_players.end(); i++)
92 if(player->peer_id == peer_id)
98 Player * Environment::getPlayer(const char *name)
100 for(core::list<Player*>::Iterator i = m_players.begin();
101 i != m_players.end(); i++)
104 if(strcmp(player->getName(), name) == 0)
110 Player * Environment::getRandomConnectedPlayer()
112 core::list<Player*> connected_players = getPlayers(true);
113 u32 chosen_one = myrand() % connected_players.size();
115 for(core::list<Player*>::Iterator
116 i = connected_players.begin();
117 i != connected_players.end(); i++)
129 Player * Environment::getNearestConnectedPlayer(v3f pos)
131 core::list<Player*> connected_players = getPlayers(true);
133 Player *nearest_player = NULL;
134 for(core::list<Player*>::Iterator
135 i = connected_players.begin();
136 i != connected_players.end(); i++)
139 f32 d = player->getPosition().getDistanceFrom(pos);
140 if(d < nearest_d || nearest_player == NULL)
143 nearest_player = player;
146 return nearest_player;
149 core::list<Player*> Environment::getPlayers()
154 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
156 core::list<Player*> newlist;
157 for(core::list<Player*>::Iterator
158 i = m_players.begin();
159 i != m_players.end(); i++)
163 if(ignore_disconnected)
165 // Ignore disconnected players
166 if(player->peer_id == 0)
170 newlist.push_back(player);
175 void Environment::printPlayers(std::ostream &o)
177 o<<"Players in environment:"<<std::endl;
178 for(core::list<Player*>::Iterator i = m_players.begin();
179 i != m_players.end(); i++)
182 o<<"Player peer_id="<<player->peer_id<<std::endl;
186 /*void Environment::setDayNightRatio(u32 r)
188 getDayNightRatio() = r;
191 u32 Environment::getDayNightRatio()
193 //return getDayNightRatio();
194 return time_to_daynight_ratio(m_time_of_day);
201 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
204 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
205 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
206 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
213 void ActiveBlockList::update(core::list<v3s16> &active_positions,
215 core::map<v3s16, bool> &blocks_removed,
216 core::map<v3s16, bool> &blocks_added)
221 core::map<v3s16, bool> newlist;
222 for(core::list<v3s16>::Iterator i = active_positions.begin();
223 i != active_positions.end(); i++)
225 fillRadiusBlock(*i, radius, newlist);
229 Find out which blocks on the old list are not on the new list
231 // Go through old list
232 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
233 i.atEnd()==false; i++)
235 v3s16 p = i.getNode()->getKey();
236 // If not on new list, it's been removed
237 if(newlist.find(p) == NULL)
238 blocks_removed.insert(p, true);
242 Find out which blocks on the new list are not on the old list
244 // Go through new list
245 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
246 i.atEnd()==false; i++)
248 v3s16 p = i.getNode()->getKey();
249 // If not on old list, it's been added
250 if(m_list.find(p) == NULL)
251 blocks_added.insert(p, true);
258 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
259 i.atEnd()==false; i++)
261 v3s16 p = i.getNode()->getKey();
262 m_list.insert(p, true);
270 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
273 m_random_spawn_timer(3),
274 m_send_recommended_timer(0),
276 m_game_time_fraction_counter(0)
280 ServerEnvironment::~ServerEnvironment()
282 // Clear active block list.
283 // This makes the next one delete all active objects.
284 m_active_blocks.clear();
286 // Convert all objects to static and delete the active objects
287 deactivateFarObjects(true);
293 void ServerEnvironment::serializePlayers(const std::string &savedir)
295 std::string players_path = savedir + "/players";
296 fs::CreateDir(players_path);
298 core::map<Player*, bool> saved_players;
300 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
301 for(u32 i=0; i<player_files.size(); i++)
303 if(player_files[i].dir)
306 // Full path to this file
307 std::string path = players_path + "/" + player_files[i].name;
309 //infostream<<"Checking player file "<<path<<std::endl;
311 // Load player to see what is its name
312 ServerRemotePlayer testplayer;
314 // Open file and deserialize
315 std::ifstream is(path.c_str(), std::ios_base::binary);
316 if(is.good() == false)
318 infostream<<"Failed to read "<<path<<std::endl;
321 testplayer.deSerialize(is);
324 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
326 // Search for the player
327 std::string playername = testplayer.getName();
328 Player *player = getPlayer(playername.c_str());
331 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
335 //infostream<<"Found matching player, overwriting."<<std::endl;
337 // OK, found. Save player there.
339 // Open file and serialize
340 std::ofstream os(path.c_str(), std::ios_base::binary);
341 if(os.good() == false)
343 infostream<<"Failed to overwrite "<<path<<std::endl;
346 player->serialize(os);
347 saved_players.insert(player, true);
351 for(core::list<Player*>::Iterator i = m_players.begin();
352 i != m_players.end(); i++)
355 if(saved_players.find(player) != NULL)
357 /*infostream<<"Player "<<player->getName()
358 <<" was already saved."<<std::endl;*/
361 std::string playername = player->getName();
362 // Don't save unnamed player
365 //infostream<<"Not saving unnamed player."<<std::endl;
371 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
372 playername = "player";
373 std::string path = players_path + "/" + playername;
375 for(u32 i=0; i<1000; i++)
377 if(fs::PathExists(path) == false)
382 path = players_path + "/" + playername + itos(i);
386 infostream<<"Didn't find free file for player"<<std::endl;
391 /*infostream<<"Saving player "<<player->getName()<<" to "
393 // Open file and serialize
394 std::ofstream os(path.c_str(), std::ios_base::binary);
395 if(os.good() == false)
397 infostream<<"Failed to overwrite "<<path<<std::endl;
400 player->serialize(os);
401 saved_players.insert(player, true);
405 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
408 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
410 std::string players_path = savedir + "/players";
412 core::map<Player*, bool> saved_players;
414 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
415 for(u32 i=0; i<player_files.size(); i++)
417 if(player_files[i].dir)
420 // Full path to this file
421 std::string path = players_path + "/" + player_files[i].name;
423 infostream<<"Checking player file "<<path<<std::endl;
425 // Load player to see what is its name
426 ServerRemotePlayer testplayer;
428 // Open file and deserialize
429 std::ifstream is(path.c_str(), std::ios_base::binary);
430 if(is.good() == false)
432 infostream<<"Failed to read "<<path<<std::endl;
435 testplayer.deSerialize(is);
438 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
440 infostream<<"Not loading player with invalid name: "
441 <<testplayer.getName()<<std::endl;
444 infostream<<"Loaded test player with name "<<testplayer.getName()
447 // Search for the player
448 std::string playername = testplayer.getName();
449 Player *player = getPlayer(playername.c_str());
450 bool newplayer = false;
453 infostream<<"Is a new player"<<std::endl;
454 player = new ServerRemotePlayer();
460 infostream<<"Reading player "<<testplayer.getName()<<" from "
462 // Open file and deserialize
463 std::ifstream is(path.c_str(), std::ios_base::binary);
464 if(is.good() == false)
466 infostream<<"Failed to read "<<path<<std::endl;
469 player->deSerialize(is);
477 void ServerEnvironment::saveMeta(const std::string &savedir)
479 std::string path = savedir + "/env_meta.txt";
481 // Open file and serialize
482 std::ofstream os(path.c_str(), std::ios_base::binary);
483 if(os.good() == false)
485 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
487 throw SerializationError("Couldn't save env meta");
491 args.setU64("game_time", m_game_time);
492 args.setU64("time_of_day", getTimeOfDay());
497 void ServerEnvironment::loadMeta(const std::string &savedir)
499 std::string path = savedir + "/env_meta.txt";
501 // Open file and deserialize
502 std::ifstream is(path.c_str(), std::ios_base::binary);
503 if(is.good() == false)
505 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
507 throw SerializationError("Couldn't load env meta");
515 throw SerializationError
516 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
518 std::getline(is, line);
519 std::string trimmedline = trim(line);
520 if(trimmedline == "EnvArgsEnd")
522 args.parseConfigLine(line);
526 m_game_time = args.getU64("game_time");
527 }catch(SettingNotFoundException &e){
528 // Getting this is crucial, otherwise timestamps are useless
529 throw SerializationError("Couldn't load env meta game_time");
533 m_time_of_day = args.getU64("time_of_day");
534 }catch(SettingNotFoundException &e){
535 // This is not as important
536 m_time_of_day = 9000;
541 // This is probably very useless
542 void spawnRandomObjects(MapBlock *block)
544 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
545 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
547 bool last_node_walkable = false;
548 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
551 MapNode n = block->getNodeNoEx(p);
552 if(n.getContent() == CONTENT_IGNORE)
554 if(content_features(n).liquid_type != LIQUID_NONE)
556 if(content_features(n).walkable)
558 last_node_walkable = true;
561 if(last_node_walkable)
563 // If block contains light information
564 if(content_features(n).param_type == CPT_LIGHT)
566 if(n.getLight(LIGHTBANK_DAY) <= 5)
568 if(myrand() % 1000 == 0)
570 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
572 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
573 std::string data = obj->getStaticData();
574 StaticObject s_obj(obj->getType(),
575 obj->getBasePosition(), data);
577 block->m_static_objects.insert(0, s_obj);
579 block->setChangedFlag();
584 last_node_walkable = false;
590 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
592 // Get time difference
594 u32 stamp = block->getTimestamp();
595 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
596 dtime_s = m_game_time - block->getTimestamp();
597 dtime_s += additional_dtime;
599 // Set current time as timestamp (and let it set ChangedFlag)
600 block->setTimestamp(m_game_time);
602 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
604 // Activate stored objects
605 activateObjects(block);
608 bool changed = block->m_node_metadata.step((float)dtime_s);
612 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
613 event.p = block->getPos();
614 m_map->dispatchEvent(&event);
616 block->setChangedFlag();
619 // TODO: Do something
620 // TODO: Implement usage of ActiveBlockModifier
622 // Here's a quick demonstration
624 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
625 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
626 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
628 v3s16 p = p0 + block->getPosRelative();
629 MapNode n = block->getNodeNoEx(p0);
632 // Convert all mud under proper day lighting to grass
633 if(n.getContent() == CONTENT_MUD)
637 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
638 if(content_features(n_top).air_equivalent &&
639 n_top.getLight(LIGHTBANK_DAY) >= 13)
641 n.setContent(CONTENT_GRASS);
642 m_map->addNodeWithEvent(p, n);
650 void ServerEnvironment::clearAllObjects()
652 infostream<<"ServerEnvironment::clearAllObjects(): "
653 <<"Removing all active objects"<<std::endl;
654 core::list<u16> objects_to_remove;
655 for(core::map<u16, ServerActiveObject*>::Iterator
656 i = m_active_objects.getIterator();
657 i.atEnd()==false; i++)
659 ServerActiveObject* obj = i.getNode()->getValue();
660 u16 id = i.getNode()->getKey();
661 v3f objectpos = obj->getBasePosition();
662 // Delete static object if block is loaded
663 if(obj->m_static_exists){
664 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
666 block->m_static_objects.remove(id);
667 block->raiseModified(MOD_STATE_WRITE_NEEDED);
668 obj->m_static_exists = false;
671 // If known by some client, don't delete immediately
672 if(obj->m_known_by_count > 0){
673 obj->m_pending_deactivation = true;
674 obj->m_removed = true;
677 // Delete active object
679 // Id to be removed from m_active_objects
680 objects_to_remove.push_back(id);
682 // Remove references from m_active_objects
683 for(core::list<u16>::Iterator i = objects_to_remove.begin();
684 i != objects_to_remove.end(); i++)
686 m_active_objects.remove(*i);
689 core::list<v3s16> loadable_blocks;
690 infostream<<"ServerEnvironment::clearAllObjects(): "
691 <<"Listing all loadable blocks"<<std::endl;
692 m_map->listAllLoadableBlocks(loadable_blocks);
693 infostream<<"ServerEnvironment::clearAllObjects(): "
694 <<"Done listing all loadable blocks: "
695 <<loadable_blocks.size()
696 <<", now clearing"<<std::endl;
697 u32 report_interval = loadable_blocks.size() / 10;
698 u32 num_blocks_checked = 0;
699 u32 num_blocks_cleared = 0;
700 u32 num_objs_cleared = 0;
701 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
702 i != loadable_blocks.end(); i++)
705 MapBlock *block = m_map->emergeBlock(p, false);
707 errorstream<<"ServerEnvironment::clearAllObjects(): "
708 <<"Failed to emerge block "<<PP(p)<<std::endl;
711 u32 num_stored = block->m_static_objects.m_stored.size();
712 u32 num_active = block->m_static_objects.m_active.size();
713 if(num_stored != 0 || num_active != 0){
714 block->m_static_objects.m_stored.clear();
715 block->m_static_objects.m_active.clear();
716 block->raiseModified(MOD_STATE_WRITE_NEEDED);
717 num_objs_cleared += num_stored + num_active;
718 num_blocks_cleared++;
720 num_blocks_checked++;
722 if(num_blocks_checked % report_interval == 0){
723 float percent = 100.0 * (float)num_blocks_checked /
724 loadable_blocks.size();
725 infostream<<"ServerEnvironment::clearAllObjects(): "
726 <<"Cleared "<<num_objs_cleared<<" objects"
727 <<" in "<<num_blocks_cleared<<" blocks ("
728 <<percent<<"%)"<<std::endl;
731 infostream<<"ServerEnvironment::clearAllObjects(): "
732 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
733 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
736 static void getMob_dungeon_master(Settings &properties)
738 properties.set("looks", "dungeon_master");
739 properties.setFloat("yaw", 1.57);
740 properties.setFloat("hp", 30);
741 properties.setBool("bright_shooting", true);
742 properties.set("shoot_type", "fireball");
743 properties.set("shoot_y", "0.7");
744 properties.set("player_hit_damage", "1");
745 properties.set("player_hit_distance", "1.0");
746 properties.set("player_hit_interval", "0.5");
747 properties.setBool("mindless_rage", myrand_range(0,100)==0);
750 void ServerEnvironment::step(float dtime)
752 DSTACK(__FUNCTION_NAME);
754 //TimeTaker timer("ServerEnv step");
757 bool footprints = g_settings->getBool("footprints");
763 m_game_time_fraction_counter += dtime;
764 u32 inc_i = (u32)m_game_time_fraction_counter;
765 m_game_time += inc_i;
766 m_game_time_fraction_counter -= (float)inc_i;
773 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
774 for(core::list<Player*>::Iterator i = m_players.begin();
775 i != m_players.end(); i++)
779 // Ignore disconnected players
780 if(player->peer_id == 0)
783 v3f playerpos = player->getPosition();
786 player->move(dtime, *m_map, 100*BS);
789 Add footsteps to grass
793 // Get node that is at BS/4 under player
794 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
796 MapNode n = m_map->getNode(bottompos);
797 if(n.getContent() == CONTENT_GRASS)
799 n.setContent(CONTENT_GRASS_FOOTSTEPS);
800 m_map->setNode(bottompos, n);
803 catch(InvalidPositionException &e)
811 Manage active block list
813 if(m_active_blocks_management_interval.step(dtime, 2.0))
815 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
817 Get player block positions
819 core::list<v3s16> players_blockpos;
820 for(core::list<Player*>::Iterator
821 i = m_players.begin();
822 i != m_players.end(); i++)
825 // Ignore disconnected players
826 if(player->peer_id == 0)
828 v3s16 blockpos = getNodeBlockPos(
829 floatToInt(player->getPosition(), BS));
830 players_blockpos.push_back(blockpos);
834 Update list of active blocks, collecting changes
836 const s16 active_block_range = g_settings->getS16("active_block_range");
837 core::map<v3s16, bool> blocks_removed;
838 core::map<v3s16, bool> blocks_added;
839 m_active_blocks.update(players_blockpos, active_block_range,
840 blocks_removed, blocks_added);
843 Handle removed blocks
846 // Convert active objects that are no more in active blocks to static
847 deactivateFarObjects(false);
849 for(core::map<v3s16, bool>::Iterator
850 i = blocks_removed.getIterator();
851 i.atEnd()==false; i++)
853 v3s16 p = i.getNode()->getKey();
855 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
856 <<") became inactive"<<std::endl;*/
858 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
862 // Set current time as timestamp (and let it set ChangedFlag)
863 block->setTimestamp(m_game_time);
870 for(core::map<v3s16, bool>::Iterator
871 i = blocks_added.getIterator();
872 i.atEnd()==false; i++)
874 v3s16 p = i.getNode()->getKey();
876 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
877 <<") became active"<<std::endl;*/
879 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
883 activateBlock(block);
888 Mess around in active blocks
890 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
892 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
896 for(core::map<v3s16, bool>::Iterator
897 i = m_active_blocks.m_list.getIterator();
898 i.atEnd()==false; i++)
900 v3s16 p = i.getNode()->getKey();
902 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
903 <<") being handled"<<std::endl;*/
905 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
909 // Reset block usage timer
910 block->resetUsageTimer();
912 // Set current time as timestamp
913 block->setTimestampNoChangedFlag(m_game_time);
916 bool changed = block->m_node_metadata.step(dtime);
920 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
922 m_map->dispatchEvent(&event);
924 block->setChangedFlag();
929 if(m_active_blocks_test_interval.step(dtime, 10.0))
931 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
932 //float dtime = 10.0;
934 for(core::map<v3s16, bool>::Iterator
935 i = m_active_blocks.m_list.getIterator();
936 i.atEnd()==false; i++)
938 v3s16 p = i.getNode()->getKey();
940 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
941 <<") being handled"<<std::endl;*/
943 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
947 // Set current time as timestamp
948 block->setTimestampNoChangedFlag(m_game_time);
953 Note that map modifications should be done using the event-
954 making map methods so that the server gets information
957 Reading can be done quickly directly from the block.
959 Everything should bind to inside this single content
960 searching loop to keep things fast.
962 // TODO: Implement usage of ActiveBlockModifier
964 // Find out how many objects the block contains
965 //u32 active_object_count = block->m_static_objects.m_active.size();
966 // Find out how many objects this and all the neighbors contain
967 u32 active_object_count_wider = 0;
968 for(s16 x=-1; x<=1; x++)
969 for(s16 y=-1; y<=1; y++)
970 for(s16 z=-1; z<=1; z++)
972 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
975 active_object_count_wider +=
976 block->m_static_objects.m_active.size()
977 + block->m_static_objects.m_stored.size();
979 /*if(block->m_static_objects.m_stored.size() != 0){
980 errorstream<<"ServerEnvironment::step(): "
981 <<PP(block->getPos())<<" contains "
982 <<block->m_static_objects.m_stored.size()
983 <<" stored objects; "
984 <<"when spawning objects, when counting active "
985 <<"objects in wide area. relative position: "
986 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
991 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
992 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
993 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
995 v3s16 p = p0 + block->getPosRelative();
996 MapNode n = block->getNodeNoEx(p0);
1000 Convert mud under proper lighting to grass
1002 if(n.getContent() == CONTENT_MUD)
1004 if(myrand()%20 == 0)
1006 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1007 if(content_features(n_top).air_equivalent &&
1008 n_top.getLightBlend(getDayNightRatio()) >= 13)
1010 n.setContent(CONTENT_GRASS);
1011 m_map->addNodeWithEvent(p, n);
1016 Convert grass into mud if under something else than air
1018 if(n.getContent() == CONTENT_GRASS)
1020 //if(myrand()%20 == 0)
1022 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
1023 if(content_features(n_top).air_equivalent == false)
1025 n.setContent(CONTENT_MUD);
1026 m_map->addNodeWithEvent(p, n);
1031 Rats spawn around regular trees
1033 if(n.getContent() == CONTENT_TREE ||
1034 n.getContent() == CONTENT_JUNGLETREE)
1036 if(myrand()%200 == 0 && active_object_count_wider == 0)
1038 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
1039 0, myrand_range(-2, 2));
1040 MapNode n1 = m_map->getNodeNoEx(p1);
1041 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
1042 if(n1b.getContent() == CONTENT_GRASS &&
1043 n1.getContent() == CONTENT_AIR)
1045 v3f pos = intToFloat(p1, BS);
1046 ServerActiveObject *obj = new RatSAO(this, 0, pos);
1047 addActiveObject(obj);
1052 Fun things spawn in caves and dungeons
1054 if(n.getContent() == CONTENT_STONE ||
1055 n.getContent() == CONTENT_MOSSYCOBBLE)
1057 if(myrand()%200 == 0 && active_object_count_wider == 0)
1059 v3s16 p1 = p + v3s16(0,1,0);
1060 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
1061 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
1062 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
1063 if(n1a.getContent() == CONTENT_AIR &&
1064 n1b.getContent() == CONTENT_AIR)
1066 v3f pos = intToFloat(p1, BS);
1068 if(i == 0 || i == 1){
1069 actionstream<<"A dungeon master spawns at "
1070 <<PP(p1)<<std::endl;
1071 Settings properties;
1072 getMob_dungeon_master(properties);
1073 ServerActiveObject *obj = new MobV2SAO(
1074 this, 0, pos, &properties);
1075 addActiveObject(obj);
1076 } else if(i == 2 || i == 3){
1077 actionstream<<"Rats spawn at "
1078 <<PP(p1)<<std::endl;
1079 for(int j=0; j<3; j++){
1080 ServerActiveObject *obj = new RatSAO(
1082 addActiveObject(obj);
1085 actionstream<<"An oerkki spawns at "
1086 <<PP(p1)<<std::endl;
1087 ServerActiveObject *obj = new Oerkki1SAO(
1089 addActiveObject(obj);
1096 Make trees from saplings!
1098 if(n.getContent() == CONTENT_SAPLING)
1100 if(myrand()%50 == 0)
1102 actionstream<<"A sapling grows into a tree at "
1105 core::map<v3s16, MapBlock*> modified_blocks;
1107 ManualMapVoxelManipulator vmanip(m_map);
1108 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1109 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1110 bool is_apple_tree = myrand()%4 == 0;
1111 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1112 vmanip.blitBackAll(&modified_blocks);
1115 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1116 for(core::map<v3s16, MapBlock*>::Iterator
1117 i = modified_blocks.getIterator();
1118 i.atEnd() == false; i++)
1120 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1122 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1124 // Send a MEET_OTHER event
1126 event.type = MEET_OTHER;
1127 for(core::map<v3s16, MapBlock*>::Iterator
1128 i = modified_blocks.getIterator();
1129 i.atEnd() == false; i++)
1131 v3s16 p = i.getNode()->getKey();
1132 event.modified_blocks.insert(p, true);
1134 m_map->dispatchEvent(&event);
1145 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1146 //TimeTaker timer("Step active objects");
1148 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1150 // This helps the objects to send data at the same time
1151 bool send_recommended = false;
1152 m_send_recommended_timer += dtime;
1153 if(m_send_recommended_timer > 0.10)
1155 m_send_recommended_timer = 0;
1156 send_recommended = true;
1159 for(core::map<u16, ServerActiveObject*>::Iterator
1160 i = m_active_objects.getIterator();
1161 i.atEnd()==false; i++)
1163 ServerActiveObject* obj = i.getNode()->getValue();
1164 // Remove non-peaceful mobs on peaceful mode
1165 if(g_settings->getBool("only_peaceful_mobs")){
1166 if(!obj->isPeaceful())
1167 obj->m_removed = true;
1169 // Don't step if is to be removed or stored statically
1170 if(obj->m_removed || obj->m_pending_deactivation)
1173 obj->step(dtime, send_recommended);
1174 // Read messages from object
1175 while(obj->m_messages_out.size() > 0)
1177 m_active_object_messages.push_back(
1178 obj->m_messages_out.pop_front());
1184 Manage active objects
1186 if(m_object_management_interval.step(dtime, 0.5))
1188 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1190 Remove objects that satisfy (m_removed && m_known_by_count==0)
1192 removeRemovedObjects();
1195 if(g_settings->getBool("enable_experimental"))
1202 m_random_spawn_timer -= dtime;
1203 if(m_random_spawn_timer < 0)
1205 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1206 //m_random_spawn_timer += 2.0;
1207 m_random_spawn_timer += 200.0;
1213 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1214 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1215 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1217 Player *player = getRandomConnectedPlayer();
1220 pos = player->getPosition();
1222 myrand_range(-3,3)*BS,
1224 myrand_range(-3,3)*BS
1228 Create a ServerActiveObject
1231 //TestSAO *obj = new TestSAO(this, 0, pos);
1232 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1233 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1234 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1235 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1237 infostream<<"Server: Spawning MobV2SAO at "
1238 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1240 Settings properties;
1241 getMob_dungeon_master(properties);
1242 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1243 addActiveObject(obj);
1247 } // enable_experimental
1250 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1252 core::map<u16, ServerActiveObject*>::Node *n;
1253 n = m_active_objects.find(id);
1256 return n->getValue();
1259 bool isFreeServerActiveObjectId(u16 id,
1260 core::map<u16, ServerActiveObject*> &objects)
1265 for(core::map<u16, ServerActiveObject*>::Iterator
1266 i = objects.getIterator();
1267 i.atEnd()==false; i++)
1269 if(i.getNode()->getKey() == id)
1275 u16 getFreeServerActiveObjectId(
1276 core::map<u16, ServerActiveObject*> &objects)
1281 if(isFreeServerActiveObjectId(new_id, objects))
1291 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1294 u16 id = addActiveObjectRaw(object, true);
1298 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1302 v3f objectpos = obj->getBasePosition();
1304 // The block in which the object resides in
1305 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1308 Update the static data
1311 // Create new static object
1312 std::string staticdata = obj->getStaticData();
1313 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1314 // Add to the block where the object is located in
1315 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1316 // Get or generate the block
1317 MapBlock *block = m_map->emergeBlock(blockpos);
1319 bool succeeded = false;
1323 block->m_static_objects.insert(0, s_obj);
1324 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1328 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1329 <<"Could not find or generate "
1330 <<"a block for storing static object"<<std::endl;
1340 Finds out what new objects have been added to
1341 inside a radius around a position
1343 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1344 core::map<u16, bool> ¤t_objects,
1345 core::map<u16, bool> &added_objects)
1347 v3f pos_f = intToFloat(pos, BS);
1348 f32 radius_f = radius * BS;
1350 Go through the object list,
1351 - discard m_removed objects,
1352 - discard objects that are too far away,
1353 - discard objects that are found in current_objects.
1354 - add remaining objects to added_objects
1356 for(core::map<u16, ServerActiveObject*>::Iterator
1357 i = m_active_objects.getIterator();
1358 i.atEnd()==false; i++)
1360 u16 id = i.getNode()->getKey();
1362 ServerActiveObject *object = i.getNode()->getValue();
1365 // Discard if removed
1366 if(object->m_removed)
1368 // Discard if too far
1369 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1370 if(distance_f > radius_f)
1372 // Discard if already on current_objects
1373 core::map<u16, bool>::Node *n;
1374 n = current_objects.find(id);
1377 // Add to added_objects
1378 added_objects.insert(id, false);
1383 Finds out what objects have been removed from
1384 inside a radius around a position
1386 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1387 core::map<u16, bool> ¤t_objects,
1388 core::map<u16, bool> &removed_objects)
1390 v3f pos_f = intToFloat(pos, BS);
1391 f32 radius_f = radius * BS;
1393 Go through current_objects; object is removed if:
1394 - object is not found in m_active_objects (this is actually an
1395 error condition; objects should be set m_removed=true and removed
1396 only after all clients have been informed about removal), or
1397 - object has m_removed=true, or
1398 - object is too far away
1400 for(core::map<u16, bool>::Iterator
1401 i = current_objects.getIterator();
1402 i.atEnd()==false; i++)
1404 u16 id = i.getNode()->getKey();
1405 ServerActiveObject *object = getActiveObject(id);
1408 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1409 <<" object in current_objects is NULL"<<std::endl;
1411 else if(object->m_removed == false)
1413 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1414 /*infostream<<"removed == false"
1415 <<"distance_f = "<<distance_f
1416 <<", radius_f = "<<radius_f<<std::endl;*/
1417 if(distance_f < radius_f)
1423 removed_objects.insert(id, false);
1427 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1429 if(m_active_object_messages.size() == 0)
1430 return ActiveObjectMessage(0);
1432 return m_active_object_messages.pop_front();
1436 ************ Private methods *************
1439 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1443 if(object->getId() == 0){
1444 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1447 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1448 <<"no free ids available"<<std::endl;
1452 object->setId(new_id);
1455 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1456 <<"supplied with id "<<object->getId()<<std::endl;
1458 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1460 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1461 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1465 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1466 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1468 m_active_objects.insert(object->getId(), object);
1470 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1471 <<"Added id="<<object->getId()<<"; there are now "
1472 <<m_active_objects.size()<<" active objects."
1475 // Add static object to active static list of the block
1476 v3f objectpos = object->getBasePosition();
1477 std::string staticdata = object->getStaticData();
1478 StaticObject s_obj(object->getType(), objectpos, staticdata);
1479 // Add to the block where the object is located in
1480 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1481 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1484 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1485 object->m_static_exists = true;
1486 object->m_static_block = blockpos;
1489 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1492 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1493 <<"could not find block for storing id="<<object->getId()
1494 <<" statically"<<std::endl;
1497 return object->getId();
1501 Remove objects that satisfy (m_removed && m_known_by_count==0)
1503 void ServerEnvironment::removeRemovedObjects()
1505 core::list<u16> objects_to_remove;
1506 for(core::map<u16, ServerActiveObject*>::Iterator
1507 i = m_active_objects.getIterator();
1508 i.atEnd()==false; i++)
1510 u16 id = i.getNode()->getKey();
1511 ServerActiveObject* obj = i.getNode()->getValue();
1512 // This shouldn't happen but check it
1515 infostream<<"NULL object found in ServerEnvironment"
1516 <<" while finding removed objects. id="<<id<<std::endl;
1517 // Id to be removed from m_active_objects
1518 objects_to_remove.push_back(id);
1523 We will delete objects that are marked as removed or thatare
1524 waiting for deletion after deactivation
1526 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1530 Delete static data from block if is marked as removed
1532 if(obj->m_static_exists && obj->m_removed)
1534 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1537 block->m_static_objects.remove(id);
1538 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1539 obj->m_static_exists = false;
1543 // If m_known_by_count > 0, don't actually remove.
1544 if(obj->m_known_by_count > 0)
1549 // Id to be removed from m_active_objects
1550 objects_to_remove.push_back(id);
1552 // Remove references from m_active_objects
1553 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1554 i != objects_to_remove.end(); i++)
1556 m_active_objects.remove(*i);
1560 static void print_hexdump(std::ostream &o, const std::string &data)
1562 const int linelength = 16;
1563 for(int l=0; ; l++){
1564 int i0 = linelength * l;
1565 bool at_end = false;
1566 int thislinelength = linelength;
1567 if(i0 + thislinelength > (int)data.size()){
1568 thislinelength = data.size() - i0;
1571 for(int di=0; di<linelength; di++){
1574 if(di<thislinelength)
1575 snprintf(buf, 4, "%.2x ", data[i]);
1577 snprintf(buf, 4, " ");
1581 for(int di=0; di<thislinelength; di++){
1595 Convert stored objects from blocks near the players to active.
1597 void ServerEnvironment::activateObjects(MapBlock *block)
1601 // Ignore if no stored objects (to not set changed flag)
1602 if(block->m_static_objects.m_stored.size() == 0)
1604 verbosestream<<"ServerEnvironment::activateObjects(): "
1605 <<"activating objects of block "<<PP(block->getPos())
1606 <<" ("<<block->m_static_objects.m_stored.size()
1607 <<" objects)"<<std::endl;
1608 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1610 errorstream<<"suspiciously large amount of objects detected: "
1611 <<block->m_static_objects.m_stored.size()<<" in "
1612 <<PP(block->getPos())
1613 <<"; removing all of them."<<std::endl;
1614 // Clear stored list
1615 block->m_static_objects.m_stored.clear();
1616 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1619 // A list for objects that couldn't be converted to static for some
1620 // reason. They will be stored back.
1621 core::list<StaticObject> new_stored;
1622 // Loop through stored static objects
1623 for(core::list<StaticObject>::Iterator
1624 i = block->m_static_objects.m_stored.begin();
1625 i != block->m_static_objects.m_stored.end(); i++)
1627 /*infostream<<"Server: Creating an active object from "
1628 <<"static data"<<std::endl;*/
1629 StaticObject &s_obj = *i;
1630 // Create an active object from the data
1631 ServerActiveObject *obj = ServerActiveObject::create
1632 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1633 // If couldn't create object, store static data back.
1636 errorstream<<"ServerEnvironment::activateObjects(): "
1637 <<"failed to create active object from static object "
1638 <<"in block "<<PP(s_obj.pos/BS)
1639 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1640 print_hexdump(verbosestream, s_obj.data);
1642 new_stored.push_back(s_obj);
1645 verbosestream<<"ServerEnvironment::activateObjects(): "
1646 <<"activated static object pos="<<PP(s_obj.pos/BS)
1647 <<" type="<<(int)s_obj.type<<std::endl;
1648 // This will also add the object to the active static list
1649 addActiveObjectRaw(obj, false);
1651 // Clear stored list
1652 block->m_static_objects.m_stored.clear();
1653 // Add leftover failed stuff to stored list
1654 for(core::list<StaticObject>::Iterator
1655 i = new_stored.begin();
1656 i != new_stored.end(); i++)
1658 StaticObject &s_obj = *i;
1659 block->m_static_objects.m_stored.push_back(s_obj);
1662 Note: Block hasn't really been modified here.
1663 The objects have just been activated and moved from the stored
1664 static list to the active static list.
1665 As such, the block is essentially the same.
1666 Thus, do not call block->setChangedFlag().
1667 Otherwise there would be a huge amount of unnecessary I/O.
1672 Convert objects that are not standing inside active blocks to static.
1674 If m_known_by_count != 0, active object is not deleted, but static
1675 data is still updated.
1677 If force_delete is set, active object is deleted nevertheless. It
1678 shall only be set so in the destructor of the environment.
1680 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1682 core::list<u16> objects_to_remove;
1683 for(core::map<u16, ServerActiveObject*>::Iterator
1684 i = m_active_objects.getIterator();
1685 i.atEnd()==false; i++)
1687 ServerActiveObject* obj = i.getNode()->getValue();
1689 // This shouldn't happen but check it
1692 errorstream<<"NULL object found in ServerEnvironment"
1698 // If pending deactivation, let removeRemovedObjects() do it
1699 if(obj->m_pending_deactivation)
1702 u16 id = i.getNode()->getKey();
1703 v3f objectpos = obj->getBasePosition();
1705 // The block in which the object resides in
1706 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1708 // If block is active, don't remove
1709 if(m_active_blocks.contains(blockpos_o))
1712 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1713 <<"deactivating object id="<<id<<" on inactive block "
1714 <<PP(blockpos_o)<<std::endl;
1716 // If known by some client, don't immediately delete.
1717 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1720 Update the static data
1723 // Create new static object
1724 std::string staticdata_new = obj->getStaticData();
1725 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1727 bool stays_in_same_block = false;
1728 bool data_changed = true;
1730 if(obj->m_static_exists){
1731 if(obj->m_static_block == blockpos_o)
1732 stays_in_same_block = true;
1734 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1736 core::map<u16, StaticObject>::Node *n =
1737 block->m_static_objects.m_active.find(id);
1739 StaticObject static_old = n->getValue();
1741 if(static_old.data == staticdata_new &&
1742 (static_old.pos - objectpos).getLength() < 2*BS)
1743 data_changed = false;
1745 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1746 <<"id="<<id<<" m_static_exists=true but "
1747 <<"static data doesn't actually exist in "
1748 <<PP(obj->m_static_block)<<std::endl;
1752 // Delete old static object
1753 if(obj->m_static_exists)
1755 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1758 block->m_static_objects.remove(id);
1759 obj->m_static_exists = false;
1760 // Only mark block as modified if data changed considerably
1761 if(!stays_in_same_block || data_changed)
1762 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1766 // Add to the block where the object is located in
1767 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1768 // Get or generate the block
1769 MapBlock *block = m_map->emergeBlock(blockpos);
1773 if(block->m_static_objects.m_stored.size() >= 49){
1774 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1775 <<" statically but block "<<PP(blockpos)
1776 <<" already contains "
1777 <<block->m_static_objects.m_stored.size()
1778 <<" (over 49) objects."
1779 <<" Forcing delete."<<std::endl;
1780 force_delete = true;
1782 u16 new_id = pending_delete ? id : 0;
1783 block->m_static_objects.insert(new_id, s_obj);
1785 // Only mark block as modified if data changed considerably
1786 if(!stays_in_same_block || data_changed)
1787 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1789 obj->m_static_exists = true;
1790 obj->m_static_block = block->getPos();
1794 errorstream<<"ServerEnv: Could not find or generate "
1795 <<"a block for storing id="<<obj->getId()
1796 <<" statically"<<std::endl;
1801 If known by some client, set pending deactivation.
1802 Otherwise delete it immediately.
1807 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1808 <<"object id="<<id<<" is known by clients"
1809 <<"; not deleting yet"<<std::endl;
1811 obj->m_pending_deactivation = true;
1815 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1816 <<"object id="<<id<<" is not known by clients"
1817 <<"; deleting"<<std::endl;
1818 // Delete active object
1820 // Id to be removed from m_active_objects
1821 objects_to_remove.push_back(id);
1824 // Remove references from m_active_objects
1825 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1826 i != objects_to_remove.end(); i++)
1828 m_active_objects.remove(*i);
1839 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1847 ClientEnvironment::~ClientEnvironment()
1849 // delete active objects
1850 for(core::map<u16, ClientActiveObject*>::Iterator
1851 i = m_active_objects.getIterator();
1852 i.atEnd()==false; i++)
1854 delete i.getNode()->getValue();
1861 void ClientEnvironment::addPlayer(Player *player)
1863 DSTACK(__FUNCTION_NAME);
1865 It is a failure if player is local and there already is a local
1868 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1870 Environment::addPlayer(player);
1873 LocalPlayer * ClientEnvironment::getLocalPlayer()
1875 for(core::list<Player*>::Iterator i = m_players.begin();
1876 i != m_players.end(); i++)
1878 Player *player = *i;
1879 if(player->isLocal())
1880 return (LocalPlayer*)player;
1885 void ClientEnvironment::step(float dtime)
1887 DSTACK(__FUNCTION_NAME);
1889 // Get some settings
1890 bool free_move = g_settings->getBool("free_move");
1891 bool footprints = g_settings->getBool("footprints");
1894 LocalPlayer *lplayer = getLocalPlayer();
1896 // collision info queue
1897 core::list<CollisionInfo> player_collisions;
1900 Get the speed the player is going
1902 bool is_climbing = lplayer->is_climbing;
1904 f32 player_speed = 0.001; // just some small value
1905 player_speed = lplayer->getSpeed().getLength();
1908 Maximum position increment
1910 //f32 position_max_increment = 0.05*BS;
1911 f32 position_max_increment = 0.1*BS;
1913 // Maximum time increment (for collision detection etc)
1914 // time = distance / speed
1915 f32 dtime_max_increment = position_max_increment / player_speed;
1917 // Maximum time increment is 10ms or lower
1918 if(dtime_max_increment > 0.01)
1919 dtime_max_increment = 0.01;
1921 // Don't allow overly huge dtime
1925 f32 dtime_downcount = dtime;
1928 Stuff that has a maximum time increment
1937 if(dtime_downcount > dtime_max_increment)
1939 dtime_part = dtime_max_increment;
1940 dtime_downcount -= dtime_part;
1944 dtime_part = dtime_downcount;
1946 Setting this to 0 (no -=dtime_part) disables an infinite loop
1947 when dtime_part is so small that dtime_downcount -= dtime_part
1950 dtime_downcount = 0;
1958 v3f lplayerpos = lplayer->getPosition();
1961 if(free_move == false && is_climbing == false)
1964 v3f speed = lplayer->getSpeed();
1965 if(lplayer->swimming_up == false)
1966 speed.Y -= 9.81 * BS * dtime_part * 2;
1969 if(lplayer->in_water_stable || lplayer->in_water)
1971 f32 max_down = 2.0*BS;
1972 if(speed.Y < -max_down) speed.Y = -max_down;
1975 if(speed.getLength() > max)
1977 speed = speed / speed.getLength() * max;
1981 lplayer->setSpeed(speed);
1986 This also does collision detection.
1988 lplayer->move(dtime_part, *m_map, position_max_increment,
1989 &player_collisions);
1992 while(dtime_downcount > 0.001);
1994 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1996 for(core::list<CollisionInfo>::Iterator
1997 i = player_collisions.begin();
1998 i != player_collisions.end(); i++)
2000 CollisionInfo &info = *i;
2001 if(info.t == COLLISION_FALL)
2003 //f32 tolerance = BS*10; // 2 without damage
2004 f32 tolerance = BS*12; // 3 without damage
2006 if(info.speed > tolerance)
2008 f32 damage_f = (info.speed - tolerance)/BS*factor;
2009 u16 damage = (u16)(damage_f+0.5);
2010 if(lplayer->hp > damage)
2011 lplayer->hp -= damage;
2015 ClientEnvEvent event;
2016 event.type = CEE_PLAYER_DAMAGE;
2017 event.player_damage.amount = damage;
2018 m_client_event_queue.push_back(event);
2024 A quick draft of lava damage
2026 if(m_lava_hurt_interval.step(dtime, 1.0))
2028 v3f pf = lplayer->getPosition();
2030 // Feet, middle and head
2031 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2032 MapNode n1 = m_map->getNodeNoEx(p1);
2033 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2034 MapNode n2 = m_map->getNodeNoEx(p2);
2035 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2036 MapNode n3 = m_map->getNodeNoEx(p2);
2038 u32 damage_per_second = 0;
2039 damage_per_second = MYMAX(damage_per_second,
2040 content_features(n1).damage_per_second);
2041 damage_per_second = MYMAX(damage_per_second,
2042 content_features(n2).damage_per_second);
2043 damage_per_second = MYMAX(damage_per_second,
2044 content_features(n3).damage_per_second);
2046 if(damage_per_second != 0)
2048 ClientEnvEvent event;
2049 event.type = CEE_PLAYER_DAMAGE;
2050 event.player_damage.amount = damage_per_second;
2051 m_client_event_queue.push_back(event);
2056 Stuff that can be done in an arbitarily large dtime
2058 for(core::list<Player*>::Iterator i = m_players.begin();
2059 i != m_players.end(); i++)
2061 Player *player = *i;
2062 v3f playerpos = player->getPosition();
2065 Handle non-local players
2067 if(player->isLocal() == false)
2070 player->move(dtime, *m_map, 100*BS);
2074 // Update lighting on all players on client
2075 u8 light = LIGHT_MAX;
2078 v3s16 p = player->getLightPosition();
2079 MapNode n = m_map->getNode(p);
2080 light = n.getLightBlend(getDayNightRatio());
2082 catch(InvalidPositionException &e) {}
2083 player->updateLight(light);
2086 Add footsteps to grass
2090 // Get node that is at BS/4 under player
2091 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2093 MapNode n = m_map->getNode(bottompos);
2094 if(n.getContent() == CONTENT_GRASS)
2096 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2097 m_map->setNode(bottompos, n);
2098 // Update mesh on client
2099 if(m_map->mapType() == MAPTYPE_CLIENT)
2101 v3s16 p_blocks = getNodeBlockPos(bottompos);
2102 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2103 //b->updateMesh(getDayNightRatio());
2104 b->setMeshExpired(true);
2108 catch(InvalidPositionException &e)
2115 Step active objects and update lighting of them
2118 for(core::map<u16, ClientActiveObject*>::Iterator
2119 i = m_active_objects.getIterator();
2120 i.atEnd()==false; i++)
2122 ClientActiveObject* obj = i.getNode()->getValue();
2124 obj->step(dtime, this);
2126 if(m_active_object_light_update_interval.step(dtime, 0.21))
2129 //u8 light = LIGHT_MAX;
2133 v3s16 p = obj->getLightPosition();
2134 MapNode n = m_map->getNode(p);
2135 light = n.getLightBlend(getDayNightRatio());
2137 catch(InvalidPositionException &e) {}
2138 obj->updateLight(light);
2143 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2145 m_map->updateMeshes(blockpos, getDayNightRatio());
2148 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2150 m_map->expireMeshes(only_daynight_diffed);
2153 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2155 core::map<u16, ClientActiveObject*>::Node *n;
2156 n = m_active_objects.find(id);
2159 return n->getValue();
2162 bool isFreeClientActiveObjectId(u16 id,
2163 core::map<u16, ClientActiveObject*> &objects)
2168 for(core::map<u16, ClientActiveObject*>::Iterator
2169 i = objects.getIterator();
2170 i.atEnd()==false; i++)
2172 if(i.getNode()->getKey() == id)
2178 u16 getFreeClientActiveObjectId(
2179 core::map<u16, ClientActiveObject*> &objects)
2184 if(isFreeClientActiveObjectId(new_id, objects))
2194 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2197 if(object->getId() == 0)
2199 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2202 infostream<<"ClientEnvironment::addActiveObject(): "
2203 <<"no free ids available"<<std::endl;
2207 object->setId(new_id);
2209 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2211 infostream<<"ClientEnvironment::addActiveObject(): "
2212 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2216 infostream<<"ClientEnvironment::addActiveObject(): "
2217 <<"added (id="<<object->getId()<<")"<<std::endl;
2218 m_active_objects.insert(object->getId(), object);
2219 object->addToScene(m_smgr);
2220 return object->getId();
2223 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2224 const std::string &init_data)
2226 ClientActiveObject* obj = ClientActiveObject::create(type);
2229 infostream<<"ClientEnvironment::addActiveObject(): "
2230 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2237 obj->initialize(init_data);
2239 addActiveObject(obj);
2242 void ClientEnvironment::removeActiveObject(u16 id)
2244 infostream<<"ClientEnvironment::removeActiveObject(): "
2245 <<"id="<<id<<std::endl;
2246 ClientActiveObject* obj = getActiveObject(id);
2249 infostream<<"ClientEnvironment::removeActiveObject(): "
2250 <<"id="<<id<<" not found"<<std::endl;
2253 obj->removeFromScene();
2255 m_active_objects.remove(id);
2258 void ClientEnvironment::processActiveObjectMessage(u16 id,
2259 const std::string &data)
2261 ClientActiveObject* obj = getActiveObject(id);
2264 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2265 <<" got message for id="<<id<<", which doesn't exist."
2269 obj->processMessage(data);
2273 Callbacks for activeobjects
2276 void ClientEnvironment::damageLocalPlayer(u8 damage)
2278 LocalPlayer *lplayer = getLocalPlayer();
2281 if(lplayer->hp > damage)
2282 lplayer->hp -= damage;
2286 ClientEnvEvent event;
2287 event.type = CEE_PLAYER_DAMAGE;
2288 event.player_damage.amount = damage;
2289 m_client_event_queue.push_back(event);
2293 Client likes to call these
2296 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2297 core::array<DistanceSortedActiveObject> &dest)
2299 for(core::map<u16, ClientActiveObject*>::Iterator
2300 i = m_active_objects.getIterator();
2301 i.atEnd()==false; i++)
2303 ClientActiveObject* obj = i.getNode()->getValue();
2305 f32 d = (obj->getPosition() - origin).getLength();
2310 DistanceSortedActiveObject dso(obj, d);
2312 dest.push_back(dso);
2316 ClientEnvEvent ClientEnvironment::getClientEvent()
2318 if(m_client_event_queue.size() == 0)
2320 ClientEnvEvent event;
2321 event.type = CEE_NONE;
2324 return m_client_event_queue.pop_front();
2327 #endif // #ifndef SERVER