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 static void getMob_dungeon_master(Settings &properties)
652 properties.set("looks", "dungeon_master");
653 properties.setFloat("yaw", 1.57);
654 properties.setFloat("hp", 30);
655 properties.setBool("bright_shooting", true);
656 properties.set("shoot_type", "fireball");
657 properties.set("shoot_y", "0.7");
658 properties.set("player_hit_damage", "1");
659 properties.set("player_hit_distance", "1.0");
660 properties.set("player_hit_interval", "0.5");
661 properties.setBool("mindless_rage", myrand_range(0,100)==0);
664 void ServerEnvironment::step(float dtime)
666 DSTACK(__FUNCTION_NAME);
668 //TimeTaker timer("ServerEnv step");
671 bool footprints = g_settings->getBool("footprints");
677 m_game_time_fraction_counter += dtime;
678 u32 inc_i = (u32)m_game_time_fraction_counter;
679 m_game_time += inc_i;
680 m_game_time_fraction_counter -= (float)inc_i;
687 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
688 for(core::list<Player*>::Iterator i = m_players.begin();
689 i != m_players.end(); i++)
693 // Ignore disconnected players
694 if(player->peer_id == 0)
697 v3f playerpos = player->getPosition();
700 player->move(dtime, *m_map, 100*BS);
703 Add footsteps to grass
707 // Get node that is at BS/4 under player
708 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
710 MapNode n = m_map->getNode(bottompos);
711 if(n.getContent() == CONTENT_GRASS)
713 n.setContent(CONTENT_GRASS_FOOTSTEPS);
714 m_map->setNode(bottompos, n);
717 catch(InvalidPositionException &e)
725 Manage active block list
727 if(m_active_blocks_management_interval.step(dtime, 2.0))
729 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
731 Get player block positions
733 core::list<v3s16> players_blockpos;
734 for(core::list<Player*>::Iterator
735 i = m_players.begin();
736 i != m_players.end(); i++)
739 // Ignore disconnected players
740 if(player->peer_id == 0)
742 v3s16 blockpos = getNodeBlockPos(
743 floatToInt(player->getPosition(), BS));
744 players_blockpos.push_back(blockpos);
748 Update list of active blocks, collecting changes
750 const s16 active_block_range = g_settings->getS16("active_block_range");
751 core::map<v3s16, bool> blocks_removed;
752 core::map<v3s16, bool> blocks_added;
753 m_active_blocks.update(players_blockpos, active_block_range,
754 blocks_removed, blocks_added);
757 Handle removed blocks
760 // Convert active objects that are no more in active blocks to static
761 deactivateFarObjects(false);
763 for(core::map<v3s16, bool>::Iterator
764 i = blocks_removed.getIterator();
765 i.atEnd()==false; i++)
767 v3s16 p = i.getNode()->getKey();
769 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
770 <<") became inactive"<<std::endl;*/
772 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
776 // Set current time as timestamp (and let it set ChangedFlag)
777 block->setTimestamp(m_game_time);
784 for(core::map<v3s16, bool>::Iterator
785 i = blocks_added.getIterator();
786 i.atEnd()==false; i++)
788 v3s16 p = i.getNode()->getKey();
790 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
791 <<") became active"<<std::endl;*/
793 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
797 activateBlock(block);
802 Mess around in active blocks
804 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
806 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
810 for(core::map<v3s16, bool>::Iterator
811 i = m_active_blocks.m_list.getIterator();
812 i.atEnd()==false; i++)
814 v3s16 p = i.getNode()->getKey();
816 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
817 <<") being handled"<<std::endl;*/
819 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
823 // Reset block usage timer
824 block->resetUsageTimer();
826 // Set current time as timestamp
827 block->setTimestampNoChangedFlag(m_game_time);
830 bool changed = block->m_node_metadata.step(dtime);
834 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
836 m_map->dispatchEvent(&event);
838 block->setChangedFlag();
843 if(m_active_blocks_test_interval.step(dtime, 10.0))
845 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
846 //float dtime = 10.0;
848 for(core::map<v3s16, bool>::Iterator
849 i = m_active_blocks.m_list.getIterator();
850 i.atEnd()==false; i++)
852 v3s16 p = i.getNode()->getKey();
854 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
855 <<") being handled"<<std::endl;*/
857 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
861 // Set current time as timestamp
862 block->setTimestampNoChangedFlag(m_game_time);
867 Note that map modifications should be done using the event-
868 making map methods so that the server gets information
871 Reading can be done quickly directly from the block.
873 Everything should bind to inside this single content
874 searching loop to keep things fast.
876 // TODO: Implement usage of ActiveBlockModifier
878 // Find out how many objects the block contains
879 //u32 active_object_count = block->m_static_objects.m_active.size();
880 // Find out how many objects this and all the neighbors contain
881 u32 active_object_count_wider = 0;
882 for(s16 x=-1; x<=1; x++)
883 for(s16 y=-1; y<=1; y++)
884 for(s16 z=-1; z<=1; z++)
886 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
889 active_object_count_wider +=
890 block->m_static_objects.m_active.size();
894 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
895 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
896 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
898 v3s16 p = p0 + block->getPosRelative();
899 MapNode n = block->getNodeNoEx(p0);
903 Convert mud under proper lighting to grass
905 if(n.getContent() == CONTENT_MUD)
909 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
910 if(content_features(n_top).air_equivalent &&
911 n_top.getLightBlend(getDayNightRatio()) >= 13)
913 n.setContent(CONTENT_GRASS);
914 m_map->addNodeWithEvent(p, n);
919 Convert grass into mud if under something else than air
921 if(n.getContent() == CONTENT_GRASS)
923 //if(myrand()%20 == 0)
925 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
926 if(content_features(n_top).air_equivalent == false)
928 n.setContent(CONTENT_MUD);
929 m_map->addNodeWithEvent(p, n);
934 Rats spawn around regular trees
936 if(n.getContent() == CONTENT_TREE ||
937 n.getContent() == CONTENT_JUNGLETREE)
939 if(myrand()%200 == 0 && active_object_count_wider == 0)
941 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
942 0, myrand_range(-2, 2));
943 MapNode n1 = m_map->getNodeNoEx(p1);
944 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
945 if(n1b.getContent() == CONTENT_GRASS &&
946 n1.getContent() == CONTENT_AIR)
948 v3f pos = intToFloat(p1, BS);
949 ServerActiveObject *obj = new RatSAO(this, 0, pos);
950 addActiveObject(obj);
955 Fun things spawn in caves and dungeons
957 if(n.getContent() == CONTENT_STONE ||
958 n.getContent() == CONTENT_MOSSYCOBBLE)
960 if(myrand()%200 == 0 && active_object_count_wider == 0)
962 v3s16 p1 = p + v3s16(0,1,0);
963 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
964 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
965 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
966 if(n1a.getContent() == CONTENT_AIR &&
967 n1b.getContent() == CONTENT_AIR)
969 v3f pos = intToFloat(p1, BS);
971 if(i == 0 || i == 1){
972 actionstream<<"A dungeon master spawns at "
975 getMob_dungeon_master(properties);
976 ServerActiveObject *obj = new MobV2SAO(
977 this, 0, pos, &properties);
978 addActiveObject(obj);
979 } else if(i == 2 || i == 3){
980 actionstream<<"Rats spawn at "
982 for(int j=0; j<3; j++){
983 ServerActiveObject *obj = new RatSAO(
985 addActiveObject(obj);
988 actionstream<<"An oerkki spawns at "
990 ServerActiveObject *obj = new Oerkki1SAO(
992 addActiveObject(obj);
999 Make trees from saplings!
1001 if(n.getContent() == CONTENT_SAPLING)
1003 if(myrand()%50 == 0)
1005 actionstream<<"A sapling grows into a tree at "
1008 core::map<v3s16, MapBlock*> modified_blocks;
1010 ManualMapVoxelManipulator vmanip(m_map);
1011 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1012 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1013 bool is_apple_tree = myrand()%4 == 0;
1014 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1015 vmanip.blitBackAll(&modified_blocks);
1018 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1025 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1027 // Send a MEET_OTHER event
1029 event.type = MEET_OTHER;
1030 for(core::map<v3s16, MapBlock*>::Iterator
1031 i = modified_blocks.getIterator();
1032 i.atEnd() == false; i++)
1034 v3s16 p = i.getNode()->getKey();
1035 event.modified_blocks.insert(p, true);
1037 m_map->dispatchEvent(&event);
1048 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1049 //TimeTaker timer("Step active objects");
1051 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1053 // This helps the objects to send data at the same time
1054 bool send_recommended = false;
1055 m_send_recommended_timer += dtime;
1056 if(m_send_recommended_timer > 0.10)
1058 m_send_recommended_timer = 0;
1059 send_recommended = true;
1062 for(core::map<u16, ServerActiveObject*>::Iterator
1063 i = m_active_objects.getIterator();
1064 i.atEnd()==false; i++)
1066 ServerActiveObject* obj = i.getNode()->getValue();
1067 // Remove non-peaceful mobs on peaceful mode
1068 if(g_settings->getBool("only_peaceful_mobs")){
1069 if(!obj->isPeaceful())
1070 obj->m_removed = true;
1072 // Don't step if is to be removed or stored statically
1073 if(obj->m_removed || obj->m_pending_deactivation)
1076 obj->step(dtime, send_recommended);
1077 // Read messages from object
1078 while(obj->m_messages_out.size() > 0)
1080 m_active_object_messages.push_back(
1081 obj->m_messages_out.pop_front());
1087 Manage active objects
1089 if(m_object_management_interval.step(dtime, 0.5))
1091 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1093 Remove objects that satisfy (m_removed && m_known_by_count==0)
1095 removeRemovedObjects();
1098 if(g_settings->getBool("enable_experimental"))
1105 m_random_spawn_timer -= dtime;
1106 if(m_random_spawn_timer < 0)
1108 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1109 //m_random_spawn_timer += 2.0;
1110 m_random_spawn_timer += 200.0;
1116 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1117 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1118 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1120 Player *player = getRandomConnectedPlayer();
1123 pos = player->getPosition();
1125 myrand_range(-3,3)*BS,
1127 myrand_range(-3,3)*BS
1131 Create a ServerActiveObject
1134 //TestSAO *obj = new TestSAO(this, 0, pos);
1135 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1136 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1137 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1138 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1140 infostream<<"Server: Spawning MobV2SAO at "
1141 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1143 Settings properties;
1144 getMob_dungeon_master(properties);
1145 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1146 addActiveObject(obj);
1150 } // enable_experimental
1153 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1155 core::map<u16, ServerActiveObject*>::Node *n;
1156 n = m_active_objects.find(id);
1159 return n->getValue();
1162 bool isFreeServerActiveObjectId(u16 id,
1163 core::map<u16, ServerActiveObject*> &objects)
1168 for(core::map<u16, ServerActiveObject*>::Iterator
1169 i = objects.getIterator();
1170 i.atEnd()==false; i++)
1172 if(i.getNode()->getKey() == id)
1178 u16 getFreeServerActiveObjectId(
1179 core::map<u16, ServerActiveObject*> &objects)
1184 if(isFreeServerActiveObjectId(new_id, objects))
1194 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1197 u16 id = addActiveObjectRaw(object, true);
1201 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1205 v3f objectpos = obj->getBasePosition();
1207 // The block in which the object resides in
1208 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1211 Update the static data
1214 // Create new static object
1215 std::string staticdata = obj->getStaticData();
1216 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1217 // Add to the block where the object is located in
1218 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1219 // Get or generate the block
1220 MapBlock *block = m_map->emergeBlock(blockpos);
1222 bool succeeded = false;
1226 block->m_static_objects.insert(0, s_obj);
1227 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1231 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1232 <<"Could not find or generate "
1233 <<"a block for storing static object"<<std::endl;
1243 Finds out what new objects have been added to
1244 inside a radius around a position
1246 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1247 core::map<u16, bool> ¤t_objects,
1248 core::map<u16, bool> &added_objects)
1250 v3f pos_f = intToFloat(pos, BS);
1251 f32 radius_f = radius * BS;
1253 Go through the object list,
1254 - discard m_removed objects,
1255 - discard objects that are too far away,
1256 - discard objects that are found in current_objects.
1257 - add remaining objects to added_objects
1259 for(core::map<u16, ServerActiveObject*>::Iterator
1260 i = m_active_objects.getIterator();
1261 i.atEnd()==false; i++)
1263 u16 id = i.getNode()->getKey();
1265 ServerActiveObject *object = i.getNode()->getValue();
1268 // Discard if removed
1269 if(object->m_removed)
1271 // Discard if too far
1272 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1273 if(distance_f > radius_f)
1275 // Discard if already on current_objects
1276 core::map<u16, bool>::Node *n;
1277 n = current_objects.find(id);
1280 // Add to added_objects
1281 added_objects.insert(id, false);
1286 Finds out what objects have been removed from
1287 inside a radius around a position
1289 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1290 core::map<u16, bool> ¤t_objects,
1291 core::map<u16, bool> &removed_objects)
1293 v3f pos_f = intToFloat(pos, BS);
1294 f32 radius_f = radius * BS;
1296 Go through current_objects; object is removed if:
1297 - object is not found in m_active_objects (this is actually an
1298 error condition; objects should be set m_removed=true and removed
1299 only after all clients have been informed about removal), or
1300 - object has m_removed=true, or
1301 - object is too far away
1303 for(core::map<u16, bool>::Iterator
1304 i = current_objects.getIterator();
1305 i.atEnd()==false; i++)
1307 u16 id = i.getNode()->getKey();
1308 ServerActiveObject *object = getActiveObject(id);
1311 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1312 <<" object in current_objects is NULL"<<std::endl;
1314 else if(object->m_removed == false)
1316 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1317 /*infostream<<"removed == false"
1318 <<"distance_f = "<<distance_f
1319 <<", radius_f = "<<radius_f<<std::endl;*/
1320 if(distance_f < radius_f)
1326 removed_objects.insert(id, false);
1330 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1332 if(m_active_object_messages.size() == 0)
1333 return ActiveObjectMessage(0);
1335 return m_active_object_messages.pop_front();
1339 ************ Private methods *************
1342 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1346 if(object->getId() == 0){
1347 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1350 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1351 <<"no free ids available"<<std::endl;
1355 object->setId(new_id);
1358 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1359 <<"supplied with id "<<object->getId()<<std::endl;
1361 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1363 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1364 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1368 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1369 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1371 m_active_objects.insert(object->getId(), object);
1373 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1374 <<"Added id="<<object->getId()<<"; there are now "
1375 <<m_active_objects.size()<<" active objects."
1378 // Add static object to active static list of the block
1379 v3f objectpos = object->getBasePosition();
1380 std::string staticdata = object->getStaticData();
1381 StaticObject s_obj(object->getType(), objectpos, staticdata);
1382 // Add to the block where the object is located in
1383 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1384 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1387 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1388 object->m_static_exists = true;
1389 object->m_static_block = blockpos;
1392 block->setChangedFlag();
1395 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1396 <<"could not find block for storing id="<<object->getId()
1397 <<" statically"<<std::endl;
1400 return object->getId();
1404 Remove objects that satisfy (m_removed && m_known_by_count==0)
1406 void ServerEnvironment::removeRemovedObjects()
1408 core::list<u16> objects_to_remove;
1409 for(core::map<u16, ServerActiveObject*>::Iterator
1410 i = m_active_objects.getIterator();
1411 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 ServerActiveObject* obj = i.getNode()->getValue();
1415 // This shouldn't happen but check it
1418 infostream<<"NULL object found in ServerEnvironment"
1419 <<" while finding removed objects. id="<<id<<std::endl;
1420 // Id to be removed from m_active_objects
1421 objects_to_remove.push_back(id);
1426 We will delete objects that are marked as removed or thatare
1427 waiting for deletion after deactivation
1429 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1433 Delete static data from block if is marked as removed
1435 if(obj->m_static_exists && obj->m_removed)
1437 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1440 block->m_static_objects.remove(id);
1441 block->setChangedFlag();
1445 // If m_known_by_count > 0, don't actually remove.
1446 if(obj->m_known_by_count > 0)
1451 // Id to be removed from m_active_objects
1452 objects_to_remove.push_back(id);
1454 // Remove references from m_active_objects
1455 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1456 i != objects_to_remove.end(); i++)
1458 m_active_objects.remove(*i);
1462 static void print_hexdump(std::ostream &o, const std::string &data)
1464 const int linelength = 16;
1465 for(int l=0; ; l++){
1466 int i0 = linelength * l;
1467 bool at_end = false;
1468 int thislinelength = linelength;
1469 if(i0 + thislinelength > (int)data.size()){
1470 thislinelength = data.size() - i0;
1473 for(int di=0; di<linelength; di++){
1476 if(di<thislinelength)
1477 snprintf(buf, 4, "%.2x ", data[i]);
1479 snprintf(buf, 4, " ");
1483 for(int di=0; di<thislinelength; di++){
1497 Convert stored objects from blocks near the players to active.
1499 void ServerEnvironment::activateObjects(MapBlock *block)
1503 // Ignore if no stored objects (to not set changed flag)
1504 if(block->m_static_objects.m_stored.size() == 0)
1506 verbosestream<<"ServerEnvironment::activateObjects(): "
1507 <<"activating objects of block "<<PP(block->getPos())
1508 <<" ("<<block->m_static_objects.m_stored.size()
1509 <<" objects)"<<std::endl;
1510 bool large_amount = (block->m_static_objects.m_stored.size() >= 50);
1512 errorstream<<"suspiciously large amount of objects detected: "
1513 <<block->m_static_objects.m_stored.size()<<" in "
1514 <<PP(block->getPos())
1515 <<"; not activating."<<std::endl;
1518 // A list for objects that couldn't be converted to static for some
1519 // reason. They will be stored back.
1520 core::list<StaticObject> new_stored;
1521 // Loop through stored static objects
1522 for(core::list<StaticObject>::Iterator
1523 i = block->m_static_objects.m_stored.begin();
1524 i != block->m_static_objects.m_stored.end(); i++)
1526 /*infostream<<"Server: Creating an active object from "
1527 <<"static data"<<std::endl;*/
1528 StaticObject &s_obj = *i;
1529 // Create an active object from the data
1530 ServerActiveObject *obj = ServerActiveObject::create
1531 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1532 // If couldn't create object, store static data back.
1535 errorstream<<"ServerEnvironment::activateObjects(): "
1536 <<"failed to create active object from static object "
1537 <<"in block "<<PP(s_obj.pos/BS)
1538 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1539 print_hexdump(verbosestream, s_obj.data);
1541 new_stored.push_back(s_obj);
1544 verbosestream<<"ServerEnvironment::activateObjects(): "
1545 <<"activated static object pos="<<PP(s_obj.pos/BS)
1546 <<" type="<<(int)s_obj.type<<std::endl;
1547 // This will also add the object to the active static list
1548 addActiveObjectRaw(obj, false);
1549 //u16 id = addActiveObjectRaw(obj, false);
1551 // Clear stored list
1552 block->m_static_objects.m_stored.clear();
1553 // Add leftover failed stuff to stored list
1554 for(core::list<StaticObject>::Iterator
1555 i = new_stored.begin();
1556 i != new_stored.end(); i++)
1558 StaticObject &s_obj = *i;
1559 block->m_static_objects.m_stored.push_back(s_obj);
1561 // Block has been modified
1562 // NOTE: No it has not really. Save I/O here.
1563 //block->setChangedFlag();
1567 Convert objects that are not in active blocks to static.
1569 If m_known_by_count != 0, active object is not deleted, but static
1570 data is still updated.
1572 If force_delete is set, active object is deleted nevertheless. It
1573 shall only be set so in the destructor of the environment.
1575 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1577 core::list<u16> objects_to_remove;
1578 for(core::map<u16, ServerActiveObject*>::Iterator
1579 i = m_active_objects.getIterator();
1580 i.atEnd()==false; i++)
1582 ServerActiveObject* obj = i.getNode()->getValue();
1584 // This shouldn't happen but check it
1587 infostream<<"NULL object found in ServerEnvironment"
1593 u16 id = i.getNode()->getKey();
1594 v3f objectpos = obj->getBasePosition();
1596 // The block in which the object resides in
1597 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1599 // If block is active, don't remove
1600 if(m_active_blocks.contains(blockpos_o))
1603 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1604 <<"deactivating object id="<<id<<" on inactive block "
1605 <<PP(blockpos_o)<<std::endl;
1608 Update the static data
1611 // Delete old static object
1612 MapBlock *oldblock = NULL;
1613 if(obj->m_static_exists)
1615 MapBlock *block = m_map->getBlockNoCreateNoEx
1616 (obj->m_static_block);
1619 block->m_static_objects.remove(id);
1622 obj->m_static_exists = false;
1625 // Create new static object
1626 std::string staticdata = obj->getStaticData();
1627 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1628 // Add to the block where the object is located in
1629 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1630 // Get or generate the block
1631 MapBlock *block = m_map->emergeBlock(blockpos);
1633 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1636 // Block not found. Is the old block still ok?
1639 // Load from disk or generate
1641 block = m_map->emergeBlock(blockpos);
1646 if(block->m_static_objects.m_stored.size() >= 50){
1647 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1648 <<" statically but block "<<PP(blockpos)
1649 <<" already contains over 50 objects."
1650 <<" Forcing delete."<<std::endl;
1651 force_delete = true;
1653 block->m_static_objects.insert(0, s_obj);
1654 block->setChangedFlag();
1655 obj->m_static_exists = true;
1656 obj->m_static_block = block->getPos();
1660 errorstream<<"ServerEnv: Could not find or generate "
1661 <<"a block for storing id="<<obj->getId()
1662 <<" statically"<<std::endl;
1667 Delete active object if not known by some client,
1668 else set pending deactivation
1671 // If known by some client, don't delete.
1672 if(obj->m_known_by_count > 0 && force_delete == false)
1674 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1675 <<"object id="<<id<<" is known by clients"
1676 <<"; not deleting yet"<<std::endl;
1678 obj->m_pending_deactivation = true;
1682 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1683 <<"object id="<<id<<" is not known by clients"
1684 <<"; deleting"<<std::endl;
1685 // Delete active object
1687 // Id to be removed from m_active_objects
1688 objects_to_remove.push_back(id);
1691 // Remove references from m_active_objects
1692 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1693 i != objects_to_remove.end(); i++)
1695 m_active_objects.remove(*i);
1706 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1714 ClientEnvironment::~ClientEnvironment()
1716 // delete active objects
1717 for(core::map<u16, ClientActiveObject*>::Iterator
1718 i = m_active_objects.getIterator();
1719 i.atEnd()==false; i++)
1721 delete i.getNode()->getValue();
1728 void ClientEnvironment::addPlayer(Player *player)
1730 DSTACK(__FUNCTION_NAME);
1732 It is a failure if player is local and there already is a local
1735 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1737 Environment::addPlayer(player);
1740 LocalPlayer * ClientEnvironment::getLocalPlayer()
1742 for(core::list<Player*>::Iterator i = m_players.begin();
1743 i != m_players.end(); i++)
1745 Player *player = *i;
1746 if(player->isLocal())
1747 return (LocalPlayer*)player;
1752 void ClientEnvironment::step(float dtime)
1754 DSTACK(__FUNCTION_NAME);
1756 // Get some settings
1757 bool free_move = g_settings->getBool("free_move");
1758 bool footprints = g_settings->getBool("footprints");
1761 LocalPlayer *lplayer = getLocalPlayer();
1763 // collision info queue
1764 core::list<CollisionInfo> player_collisions;
1767 Get the speed the player is going
1769 bool is_climbing = lplayer->is_climbing;
1771 f32 player_speed = 0.001; // just some small value
1772 player_speed = lplayer->getSpeed().getLength();
1775 Maximum position increment
1777 //f32 position_max_increment = 0.05*BS;
1778 f32 position_max_increment = 0.1*BS;
1780 // Maximum time increment (for collision detection etc)
1781 // time = distance / speed
1782 f32 dtime_max_increment = position_max_increment / player_speed;
1784 // Maximum time increment is 10ms or lower
1785 if(dtime_max_increment > 0.01)
1786 dtime_max_increment = 0.01;
1788 // Don't allow overly huge dtime
1792 f32 dtime_downcount = dtime;
1795 Stuff that has a maximum time increment
1804 if(dtime_downcount > dtime_max_increment)
1806 dtime_part = dtime_max_increment;
1807 dtime_downcount -= dtime_part;
1811 dtime_part = dtime_downcount;
1813 Setting this to 0 (no -=dtime_part) disables an infinite loop
1814 when dtime_part is so small that dtime_downcount -= dtime_part
1817 dtime_downcount = 0;
1825 v3f lplayerpos = lplayer->getPosition();
1828 if(free_move == false && is_climbing == false)
1831 v3f speed = lplayer->getSpeed();
1832 if(lplayer->swimming_up == false)
1833 speed.Y -= 9.81 * BS * dtime_part * 2;
1836 if(lplayer->in_water_stable || lplayer->in_water)
1838 f32 max_down = 2.0*BS;
1839 if(speed.Y < -max_down) speed.Y = -max_down;
1842 if(speed.getLength() > max)
1844 speed = speed / speed.getLength() * max;
1848 lplayer->setSpeed(speed);
1853 This also does collision detection.
1855 lplayer->move(dtime_part, *m_map, position_max_increment,
1856 &player_collisions);
1859 while(dtime_downcount > 0.001);
1861 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1863 for(core::list<CollisionInfo>::Iterator
1864 i = player_collisions.begin();
1865 i != player_collisions.end(); i++)
1867 CollisionInfo &info = *i;
1868 if(info.t == COLLISION_FALL)
1870 //f32 tolerance = BS*10; // 2 without damage
1871 f32 tolerance = BS*12; // 3 without damage
1873 if(info.speed > tolerance)
1875 f32 damage_f = (info.speed - tolerance)/BS*factor;
1876 u16 damage = (u16)(damage_f+0.5);
1877 if(lplayer->hp > damage)
1878 lplayer->hp -= damage;
1882 ClientEnvEvent event;
1883 event.type = CEE_PLAYER_DAMAGE;
1884 event.player_damage.amount = damage;
1885 m_client_event_queue.push_back(event);
1891 A quick draft of lava damage
1893 if(m_lava_hurt_interval.step(dtime, 1.0))
1895 v3f pf = lplayer->getPosition();
1897 // Feet, middle and head
1898 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1899 MapNode n1 = m_map->getNodeNoEx(p1);
1900 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1901 MapNode n2 = m_map->getNodeNoEx(p2);
1902 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1903 MapNode n3 = m_map->getNodeNoEx(p2);
1905 u32 damage_per_second = 0;
1906 damage_per_second = MYMAX(damage_per_second,
1907 content_features(n1).damage_per_second);
1908 damage_per_second = MYMAX(damage_per_second,
1909 content_features(n2).damage_per_second);
1910 damage_per_second = MYMAX(damage_per_second,
1911 content_features(n3).damage_per_second);
1913 if(damage_per_second != 0)
1915 ClientEnvEvent event;
1916 event.type = CEE_PLAYER_DAMAGE;
1917 event.player_damage.amount = damage_per_second;
1918 m_client_event_queue.push_back(event);
1923 Stuff that can be done in an arbitarily large dtime
1925 for(core::list<Player*>::Iterator i = m_players.begin();
1926 i != m_players.end(); i++)
1928 Player *player = *i;
1929 v3f playerpos = player->getPosition();
1932 Handle non-local players
1934 if(player->isLocal() == false)
1937 player->move(dtime, *m_map, 100*BS);
1941 // Update lighting on all players on client
1942 u8 light = LIGHT_MAX;
1945 v3s16 p = player->getLightPosition();
1946 MapNode n = m_map->getNode(p);
1947 light = n.getLightBlend(getDayNightRatio());
1949 catch(InvalidPositionException &e) {}
1950 player->updateLight(light);
1953 Add footsteps to grass
1957 // Get node that is at BS/4 under player
1958 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1960 MapNode n = m_map->getNode(bottompos);
1961 if(n.getContent() == CONTENT_GRASS)
1963 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1964 m_map->setNode(bottompos, n);
1965 // Update mesh on client
1966 if(m_map->mapType() == MAPTYPE_CLIENT)
1968 v3s16 p_blocks = getNodeBlockPos(bottompos);
1969 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1970 //b->updateMesh(getDayNightRatio());
1971 b->setMeshExpired(true);
1975 catch(InvalidPositionException &e)
1982 Step active objects and update lighting of them
1985 for(core::map<u16, ClientActiveObject*>::Iterator
1986 i = m_active_objects.getIterator();
1987 i.atEnd()==false; i++)
1989 ClientActiveObject* obj = i.getNode()->getValue();
1991 obj->step(dtime, this);
1993 if(m_active_object_light_update_interval.step(dtime, 0.21))
1996 //u8 light = LIGHT_MAX;
2000 v3s16 p = obj->getLightPosition();
2001 MapNode n = m_map->getNode(p);
2002 light = n.getLightBlend(getDayNightRatio());
2004 catch(InvalidPositionException &e) {}
2005 obj->updateLight(light);
2010 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2012 m_map->updateMeshes(blockpos, getDayNightRatio());
2015 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2017 m_map->expireMeshes(only_daynight_diffed);
2020 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2022 core::map<u16, ClientActiveObject*>::Node *n;
2023 n = m_active_objects.find(id);
2026 return n->getValue();
2029 bool isFreeClientActiveObjectId(u16 id,
2030 core::map<u16, ClientActiveObject*> &objects)
2035 for(core::map<u16, ClientActiveObject*>::Iterator
2036 i = objects.getIterator();
2037 i.atEnd()==false; i++)
2039 if(i.getNode()->getKey() == id)
2045 u16 getFreeClientActiveObjectId(
2046 core::map<u16, ClientActiveObject*> &objects)
2051 if(isFreeClientActiveObjectId(new_id, objects))
2061 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2064 if(object->getId() == 0)
2066 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2069 infostream<<"ClientEnvironment::addActiveObject(): "
2070 <<"no free ids available"<<std::endl;
2074 object->setId(new_id);
2076 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2078 infostream<<"ClientEnvironment::addActiveObject(): "
2079 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2083 infostream<<"ClientEnvironment::addActiveObject(): "
2084 <<"added (id="<<object->getId()<<")"<<std::endl;
2085 m_active_objects.insert(object->getId(), object);
2086 object->addToScene(m_smgr);
2087 return object->getId();
2090 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2091 const std::string &init_data)
2093 ClientActiveObject* obj = ClientActiveObject::create(type);
2096 infostream<<"ClientEnvironment::addActiveObject(): "
2097 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2104 obj->initialize(init_data);
2106 addActiveObject(obj);
2109 void ClientEnvironment::removeActiveObject(u16 id)
2111 infostream<<"ClientEnvironment::removeActiveObject(): "
2112 <<"id="<<id<<std::endl;
2113 ClientActiveObject* obj = getActiveObject(id);
2116 infostream<<"ClientEnvironment::removeActiveObject(): "
2117 <<"id="<<id<<" not found"<<std::endl;
2120 obj->removeFromScene();
2122 m_active_objects.remove(id);
2125 void ClientEnvironment::processActiveObjectMessage(u16 id,
2126 const std::string &data)
2128 ClientActiveObject* obj = getActiveObject(id);
2131 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2132 <<" got message for id="<<id<<", which doesn't exist."
2136 obj->processMessage(data);
2140 Callbacks for activeobjects
2143 void ClientEnvironment::damageLocalPlayer(u8 damage)
2145 LocalPlayer *lplayer = getLocalPlayer();
2148 if(lplayer->hp > damage)
2149 lplayer->hp -= damage;
2153 ClientEnvEvent event;
2154 event.type = CEE_PLAYER_DAMAGE;
2155 event.player_damage.amount = damage;
2156 m_client_event_queue.push_back(event);
2160 Client likes to call these
2163 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2164 core::array<DistanceSortedActiveObject> &dest)
2166 for(core::map<u16, ClientActiveObject*>::Iterator
2167 i = m_active_objects.getIterator();
2168 i.atEnd()==false; i++)
2170 ClientActiveObject* obj = i.getNode()->getValue();
2172 f32 d = (obj->getPosition() - origin).getLength();
2177 DistanceSortedActiveObject dso(obj, d);
2179 dest.push_back(dso);
2183 ClientEnvEvent ClientEnvironment::getClientEvent()
2185 if(m_client_event_queue.size() == 0)
2187 ClientEnvEvent event;
2188 event.type = CEE_NONE;
2191 return m_client_event_queue.pop_front();
2194 #endif // #ifndef SERVER