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 u16 id = i.getNode()->getKey();
1699 v3f objectpos = obj->getBasePosition();
1701 // The block in which the object resides in
1702 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1704 // If block is active, don't remove
1705 if(m_active_blocks.contains(blockpos_o))
1708 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1709 <<"deactivating object id="<<id<<" on inactive block "
1710 <<PP(blockpos_o)<<std::endl;
1713 Update the static data
1716 // Delete old static object
1717 if(obj->m_static_exists)
1719 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1722 block->m_static_objects.remove(id);
1723 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1724 obj->m_static_exists = false;
1728 // Create new static object
1729 std::string staticdata = obj->getStaticData();
1730 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1731 // Add to the block where the object is located in
1732 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1733 // Get or generate the block
1734 MapBlock *block = m_map->emergeBlock(blockpos);
1738 if(block->m_static_objects.m_stored.size() >= 49){
1739 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1740 <<" statically but block "<<PP(blockpos)
1741 <<" already contains "
1742 <<block->m_static_objects.m_stored.size()
1743 <<" (over 49) objects."
1744 <<" Forcing delete."<<std::endl;
1745 force_delete = true;
1747 block->m_static_objects.insert(0, s_obj);
1748 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1749 obj->m_static_exists = true;
1750 obj->m_static_block = block->getPos();
1754 errorstream<<"ServerEnv: Could not find or generate "
1755 <<"a block for storing id="<<obj->getId()
1756 <<" statically"<<std::endl;
1761 Delete active object if not known by some client,
1762 else set pending deactivation
1765 // If known by some client, don't delete.
1766 if(obj->m_known_by_count > 0 && force_delete == false)
1768 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1769 <<"object id="<<id<<" is known by clients"
1770 <<"; not deleting yet"<<std::endl;
1772 obj->m_pending_deactivation = true;
1776 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1777 <<"object id="<<id<<" is not known by clients"
1778 <<"; deleting"<<std::endl;
1779 // Delete active object
1781 // Id to be removed from m_active_objects
1782 objects_to_remove.push_back(id);
1785 // Remove references from m_active_objects
1786 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1787 i != objects_to_remove.end(); i++)
1789 m_active_objects.remove(*i);
1800 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1808 ClientEnvironment::~ClientEnvironment()
1810 // delete active objects
1811 for(core::map<u16, ClientActiveObject*>::Iterator
1812 i = m_active_objects.getIterator();
1813 i.atEnd()==false; i++)
1815 delete i.getNode()->getValue();
1822 void ClientEnvironment::addPlayer(Player *player)
1824 DSTACK(__FUNCTION_NAME);
1826 It is a failure if player is local and there already is a local
1829 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1831 Environment::addPlayer(player);
1834 LocalPlayer * ClientEnvironment::getLocalPlayer()
1836 for(core::list<Player*>::Iterator i = m_players.begin();
1837 i != m_players.end(); i++)
1839 Player *player = *i;
1840 if(player->isLocal())
1841 return (LocalPlayer*)player;
1846 void ClientEnvironment::step(float dtime)
1848 DSTACK(__FUNCTION_NAME);
1850 // Get some settings
1851 bool free_move = g_settings->getBool("free_move");
1852 bool footprints = g_settings->getBool("footprints");
1855 LocalPlayer *lplayer = getLocalPlayer();
1857 // collision info queue
1858 core::list<CollisionInfo> player_collisions;
1861 Get the speed the player is going
1863 bool is_climbing = lplayer->is_climbing;
1865 f32 player_speed = 0.001; // just some small value
1866 player_speed = lplayer->getSpeed().getLength();
1869 Maximum position increment
1871 //f32 position_max_increment = 0.05*BS;
1872 f32 position_max_increment = 0.1*BS;
1874 // Maximum time increment (for collision detection etc)
1875 // time = distance / speed
1876 f32 dtime_max_increment = position_max_increment / player_speed;
1878 // Maximum time increment is 10ms or lower
1879 if(dtime_max_increment > 0.01)
1880 dtime_max_increment = 0.01;
1882 // Don't allow overly huge dtime
1886 f32 dtime_downcount = dtime;
1889 Stuff that has a maximum time increment
1898 if(dtime_downcount > dtime_max_increment)
1900 dtime_part = dtime_max_increment;
1901 dtime_downcount -= dtime_part;
1905 dtime_part = dtime_downcount;
1907 Setting this to 0 (no -=dtime_part) disables an infinite loop
1908 when dtime_part is so small that dtime_downcount -= dtime_part
1911 dtime_downcount = 0;
1919 v3f lplayerpos = lplayer->getPosition();
1922 if(free_move == false && is_climbing == false)
1925 v3f speed = lplayer->getSpeed();
1926 if(lplayer->swimming_up == false)
1927 speed.Y -= 9.81 * BS * dtime_part * 2;
1930 if(lplayer->in_water_stable || lplayer->in_water)
1932 f32 max_down = 2.0*BS;
1933 if(speed.Y < -max_down) speed.Y = -max_down;
1936 if(speed.getLength() > max)
1938 speed = speed / speed.getLength() * max;
1942 lplayer->setSpeed(speed);
1947 This also does collision detection.
1949 lplayer->move(dtime_part, *m_map, position_max_increment,
1950 &player_collisions);
1953 while(dtime_downcount > 0.001);
1955 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1957 for(core::list<CollisionInfo>::Iterator
1958 i = player_collisions.begin();
1959 i != player_collisions.end(); i++)
1961 CollisionInfo &info = *i;
1962 if(info.t == COLLISION_FALL)
1964 //f32 tolerance = BS*10; // 2 without damage
1965 f32 tolerance = BS*12; // 3 without damage
1967 if(info.speed > tolerance)
1969 f32 damage_f = (info.speed - tolerance)/BS*factor;
1970 u16 damage = (u16)(damage_f+0.5);
1971 if(lplayer->hp > damage)
1972 lplayer->hp -= damage;
1976 ClientEnvEvent event;
1977 event.type = CEE_PLAYER_DAMAGE;
1978 event.player_damage.amount = damage;
1979 m_client_event_queue.push_back(event);
1985 A quick draft of lava damage
1987 if(m_lava_hurt_interval.step(dtime, 1.0))
1989 v3f pf = lplayer->getPosition();
1991 // Feet, middle and head
1992 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1993 MapNode n1 = m_map->getNodeNoEx(p1);
1994 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1995 MapNode n2 = m_map->getNodeNoEx(p2);
1996 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1997 MapNode n3 = m_map->getNodeNoEx(p2);
1999 u32 damage_per_second = 0;
2000 damage_per_second = MYMAX(damage_per_second,
2001 content_features(n1).damage_per_second);
2002 damage_per_second = MYMAX(damage_per_second,
2003 content_features(n2).damage_per_second);
2004 damage_per_second = MYMAX(damage_per_second,
2005 content_features(n3).damage_per_second);
2007 if(damage_per_second != 0)
2009 ClientEnvEvent event;
2010 event.type = CEE_PLAYER_DAMAGE;
2011 event.player_damage.amount = damage_per_second;
2012 m_client_event_queue.push_back(event);
2017 Stuff that can be done in an arbitarily large dtime
2019 for(core::list<Player*>::Iterator i = m_players.begin();
2020 i != m_players.end(); i++)
2022 Player *player = *i;
2023 v3f playerpos = player->getPosition();
2026 Handle non-local players
2028 if(player->isLocal() == false)
2031 player->move(dtime, *m_map, 100*BS);
2035 // Update lighting on all players on client
2036 u8 light = LIGHT_MAX;
2039 v3s16 p = player->getLightPosition();
2040 MapNode n = m_map->getNode(p);
2041 light = n.getLightBlend(getDayNightRatio());
2043 catch(InvalidPositionException &e) {}
2044 player->updateLight(light);
2047 Add footsteps to grass
2051 // Get node that is at BS/4 under player
2052 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
2054 MapNode n = m_map->getNode(bottompos);
2055 if(n.getContent() == CONTENT_GRASS)
2057 n.setContent(CONTENT_GRASS_FOOTSTEPS);
2058 m_map->setNode(bottompos, n);
2059 // Update mesh on client
2060 if(m_map->mapType() == MAPTYPE_CLIENT)
2062 v3s16 p_blocks = getNodeBlockPos(bottompos);
2063 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
2064 //b->updateMesh(getDayNightRatio());
2065 b->setMeshExpired(true);
2069 catch(InvalidPositionException &e)
2076 Step active objects and update lighting of them
2079 for(core::map<u16, ClientActiveObject*>::Iterator
2080 i = m_active_objects.getIterator();
2081 i.atEnd()==false; i++)
2083 ClientActiveObject* obj = i.getNode()->getValue();
2085 obj->step(dtime, this);
2087 if(m_active_object_light_update_interval.step(dtime, 0.21))
2090 //u8 light = LIGHT_MAX;
2094 v3s16 p = obj->getLightPosition();
2095 MapNode n = m_map->getNode(p);
2096 light = n.getLightBlend(getDayNightRatio());
2098 catch(InvalidPositionException &e) {}
2099 obj->updateLight(light);
2104 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2106 m_map->updateMeshes(blockpos, getDayNightRatio());
2109 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2111 m_map->expireMeshes(only_daynight_diffed);
2114 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2116 core::map<u16, ClientActiveObject*>::Node *n;
2117 n = m_active_objects.find(id);
2120 return n->getValue();
2123 bool isFreeClientActiveObjectId(u16 id,
2124 core::map<u16, ClientActiveObject*> &objects)
2129 for(core::map<u16, ClientActiveObject*>::Iterator
2130 i = objects.getIterator();
2131 i.atEnd()==false; i++)
2133 if(i.getNode()->getKey() == id)
2139 u16 getFreeClientActiveObjectId(
2140 core::map<u16, ClientActiveObject*> &objects)
2145 if(isFreeClientActiveObjectId(new_id, objects))
2155 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2158 if(object->getId() == 0)
2160 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2163 infostream<<"ClientEnvironment::addActiveObject(): "
2164 <<"no free ids available"<<std::endl;
2168 object->setId(new_id);
2170 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2172 infostream<<"ClientEnvironment::addActiveObject(): "
2173 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2177 infostream<<"ClientEnvironment::addActiveObject(): "
2178 <<"added (id="<<object->getId()<<")"<<std::endl;
2179 m_active_objects.insert(object->getId(), object);
2180 object->addToScene(m_smgr);
2181 return object->getId();
2184 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2185 const std::string &init_data)
2187 ClientActiveObject* obj = ClientActiveObject::create(type);
2190 infostream<<"ClientEnvironment::addActiveObject(): "
2191 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2198 obj->initialize(init_data);
2200 addActiveObject(obj);
2203 void ClientEnvironment::removeActiveObject(u16 id)
2205 infostream<<"ClientEnvironment::removeActiveObject(): "
2206 <<"id="<<id<<std::endl;
2207 ClientActiveObject* obj = getActiveObject(id);
2210 infostream<<"ClientEnvironment::removeActiveObject(): "
2211 <<"id="<<id<<" not found"<<std::endl;
2214 obj->removeFromScene();
2216 m_active_objects.remove(id);
2219 void ClientEnvironment::processActiveObjectMessage(u16 id,
2220 const std::string &data)
2222 ClientActiveObject* obj = getActiveObject(id);
2225 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2226 <<" got message for id="<<id<<", which doesn't exist."
2230 obj->processMessage(data);
2234 Callbacks for activeobjects
2237 void ClientEnvironment::damageLocalPlayer(u8 damage)
2239 LocalPlayer *lplayer = getLocalPlayer();
2242 if(lplayer->hp > damage)
2243 lplayer->hp -= damage;
2247 ClientEnvEvent event;
2248 event.type = CEE_PLAYER_DAMAGE;
2249 event.player_damage.amount = damage;
2250 m_client_event_queue.push_back(event);
2254 Client likes to call these
2257 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2258 core::array<DistanceSortedActiveObject> &dest)
2260 for(core::map<u16, ClientActiveObject*>::Iterator
2261 i = m_active_objects.getIterator();
2262 i.atEnd()==false; i++)
2264 ClientActiveObject* obj = i.getNode()->getValue();
2266 f32 d = (obj->getPosition() - origin).getLength();
2271 DistanceSortedActiveObject dso(obj, d);
2273 dest.push_back(dso);
2277 ClientEnvEvent ClientEnvironment::getClientEvent()
2279 if(m_client_event_queue.size() == 0)
2281 ClientEnvEvent event;
2282 event.type = CEE_NONE;
2285 return m_client_event_queue.pop_front();
2288 #endif // #ifndef SERVER