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();
892 if(block->m_static_objects.m_stored.size() != 0){
893 errorstream<<"ServerEnvironment::step(): "
894 <<PP(block->getPos())<<" contains "
895 <<block->m_static_objects.m_stored.size()
896 <<" stored objects; "
897 <<"when spawning objects, when counting active "
898 <<"objects in wide area. relative position: "
899 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
904 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
905 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
906 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
908 v3s16 p = p0 + block->getPosRelative();
909 MapNode n = block->getNodeNoEx(p0);
913 Convert mud under proper lighting to grass
915 if(n.getContent() == CONTENT_MUD)
919 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
920 if(content_features(n_top).air_equivalent &&
921 n_top.getLightBlend(getDayNightRatio()) >= 13)
923 n.setContent(CONTENT_GRASS);
924 m_map->addNodeWithEvent(p, n);
929 Convert grass into mud if under something else than air
931 if(n.getContent() == CONTENT_GRASS)
933 //if(myrand()%20 == 0)
935 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
936 if(content_features(n_top).air_equivalent == false)
938 n.setContent(CONTENT_MUD);
939 m_map->addNodeWithEvent(p, n);
944 Rats spawn around regular trees
946 if(n.getContent() == CONTENT_TREE ||
947 n.getContent() == CONTENT_JUNGLETREE)
949 if(myrand()%200 == 0 && active_object_count_wider == 0)
951 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
952 0, myrand_range(-2, 2));
953 MapNode n1 = m_map->getNodeNoEx(p1);
954 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
955 if(n1b.getContent() == CONTENT_GRASS &&
956 n1.getContent() == CONTENT_AIR)
958 v3f pos = intToFloat(p1, BS);
959 ServerActiveObject *obj = new RatSAO(this, 0, pos);
960 addActiveObject(obj);
965 Fun things spawn in caves and dungeons
967 if(n.getContent() == CONTENT_STONE ||
968 n.getContent() == CONTENT_MOSSYCOBBLE)
970 if(myrand()%200 == 0 && active_object_count_wider == 0)
972 v3s16 p1 = p + v3s16(0,1,0);
973 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
974 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
975 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
976 if(n1a.getContent() == CONTENT_AIR &&
977 n1b.getContent() == CONTENT_AIR)
979 v3f pos = intToFloat(p1, BS);
981 if(i == 0 || i == 1){
982 actionstream<<"A dungeon master spawns at "
985 getMob_dungeon_master(properties);
986 ServerActiveObject *obj = new MobV2SAO(
987 this, 0, pos, &properties);
988 addActiveObject(obj);
989 } else if(i == 2 || i == 3){
990 actionstream<<"Rats spawn at "
992 for(int j=0; j<3; j++){
993 ServerActiveObject *obj = new RatSAO(
995 addActiveObject(obj);
998 actionstream<<"An oerkki spawns at "
1000 ServerActiveObject *obj = new Oerkki1SAO(
1002 addActiveObject(obj);
1009 Make trees from saplings!
1011 if(n.getContent() == CONTENT_SAPLING)
1013 if(myrand()%50 == 0)
1015 actionstream<<"A sapling grows into a tree at "
1018 core::map<v3s16, MapBlock*> modified_blocks;
1020 ManualMapVoxelManipulator vmanip(m_map);
1021 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1022 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1023 bool is_apple_tree = myrand()%4 == 0;
1024 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1025 vmanip.blitBackAll(&modified_blocks);
1028 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1029 for(core::map<v3s16, MapBlock*>::Iterator
1030 i = modified_blocks.getIterator();
1031 i.atEnd() == false; i++)
1033 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1035 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1037 // Send a MEET_OTHER event
1039 event.type = MEET_OTHER;
1040 for(core::map<v3s16, MapBlock*>::Iterator
1041 i = modified_blocks.getIterator();
1042 i.atEnd() == false; i++)
1044 v3s16 p = i.getNode()->getKey();
1045 event.modified_blocks.insert(p, true);
1047 m_map->dispatchEvent(&event);
1058 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1059 //TimeTaker timer("Step active objects");
1061 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1063 // This helps the objects to send data at the same time
1064 bool send_recommended = false;
1065 m_send_recommended_timer += dtime;
1066 if(m_send_recommended_timer > 0.10)
1068 m_send_recommended_timer = 0;
1069 send_recommended = true;
1072 for(core::map<u16, ServerActiveObject*>::Iterator
1073 i = m_active_objects.getIterator();
1074 i.atEnd()==false; i++)
1076 ServerActiveObject* obj = i.getNode()->getValue();
1077 // Remove non-peaceful mobs on peaceful mode
1078 if(g_settings->getBool("only_peaceful_mobs")){
1079 if(!obj->isPeaceful())
1080 obj->m_removed = true;
1082 // Don't step if is to be removed or stored statically
1083 if(obj->m_removed || obj->m_pending_deactivation)
1086 obj->step(dtime, send_recommended);
1087 // Read messages from object
1088 while(obj->m_messages_out.size() > 0)
1090 m_active_object_messages.push_back(
1091 obj->m_messages_out.pop_front());
1097 Manage active objects
1099 if(m_object_management_interval.step(dtime, 0.5))
1101 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1103 Remove objects that satisfy (m_removed && m_known_by_count==0)
1105 removeRemovedObjects();
1108 if(g_settings->getBool("enable_experimental"))
1115 m_random_spawn_timer -= dtime;
1116 if(m_random_spawn_timer < 0)
1118 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1119 //m_random_spawn_timer += 2.0;
1120 m_random_spawn_timer += 200.0;
1126 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1127 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1128 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1130 Player *player = getRandomConnectedPlayer();
1133 pos = player->getPosition();
1135 myrand_range(-3,3)*BS,
1137 myrand_range(-3,3)*BS
1141 Create a ServerActiveObject
1144 //TestSAO *obj = new TestSAO(this, 0, pos);
1145 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1146 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1147 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1148 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1150 infostream<<"Server: Spawning MobV2SAO at "
1151 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1153 Settings properties;
1154 getMob_dungeon_master(properties);
1155 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1156 addActiveObject(obj);
1160 } // enable_experimental
1163 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1165 core::map<u16, ServerActiveObject*>::Node *n;
1166 n = m_active_objects.find(id);
1169 return n->getValue();
1172 bool isFreeServerActiveObjectId(u16 id,
1173 core::map<u16, ServerActiveObject*> &objects)
1178 for(core::map<u16, ServerActiveObject*>::Iterator
1179 i = objects.getIterator();
1180 i.atEnd()==false; i++)
1182 if(i.getNode()->getKey() == id)
1188 u16 getFreeServerActiveObjectId(
1189 core::map<u16, ServerActiveObject*> &objects)
1194 if(isFreeServerActiveObjectId(new_id, objects))
1204 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1207 u16 id = addActiveObjectRaw(object, true);
1211 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1215 v3f objectpos = obj->getBasePosition();
1217 // The block in which the object resides in
1218 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1221 Update the static data
1224 // Create new static object
1225 std::string staticdata = obj->getStaticData();
1226 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1227 // Add to the block where the object is located in
1228 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1229 // Get or generate the block
1230 MapBlock *block = m_map->emergeBlock(blockpos);
1232 bool succeeded = false;
1236 block->m_static_objects.insert(0, s_obj);
1237 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1241 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1242 <<"Could not find or generate "
1243 <<"a block for storing static object"<<std::endl;
1253 Finds out what new objects have been added to
1254 inside a radius around a position
1256 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1257 core::map<u16, bool> ¤t_objects,
1258 core::map<u16, bool> &added_objects)
1260 v3f pos_f = intToFloat(pos, BS);
1261 f32 radius_f = radius * BS;
1263 Go through the object list,
1264 - discard m_removed objects,
1265 - discard objects that are too far away,
1266 - discard objects that are found in current_objects.
1267 - add remaining objects to added_objects
1269 for(core::map<u16, ServerActiveObject*>::Iterator
1270 i = m_active_objects.getIterator();
1271 i.atEnd()==false; i++)
1273 u16 id = i.getNode()->getKey();
1275 ServerActiveObject *object = i.getNode()->getValue();
1278 // Discard if removed
1279 if(object->m_removed)
1281 // Discard if too far
1282 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1283 if(distance_f > radius_f)
1285 // Discard if already on current_objects
1286 core::map<u16, bool>::Node *n;
1287 n = current_objects.find(id);
1290 // Add to added_objects
1291 added_objects.insert(id, false);
1296 Finds out what objects have been removed from
1297 inside a radius around a position
1299 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1300 core::map<u16, bool> ¤t_objects,
1301 core::map<u16, bool> &removed_objects)
1303 v3f pos_f = intToFloat(pos, BS);
1304 f32 radius_f = radius * BS;
1306 Go through current_objects; object is removed if:
1307 - object is not found in m_active_objects (this is actually an
1308 error condition; objects should be set m_removed=true and removed
1309 only after all clients have been informed about removal), or
1310 - object has m_removed=true, or
1311 - object is too far away
1313 for(core::map<u16, bool>::Iterator
1314 i = current_objects.getIterator();
1315 i.atEnd()==false; i++)
1317 u16 id = i.getNode()->getKey();
1318 ServerActiveObject *object = getActiveObject(id);
1321 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1322 <<" object in current_objects is NULL"<<std::endl;
1324 else if(object->m_removed == false)
1326 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1327 /*infostream<<"removed == false"
1328 <<"distance_f = "<<distance_f
1329 <<", radius_f = "<<radius_f<<std::endl;*/
1330 if(distance_f < radius_f)
1336 removed_objects.insert(id, false);
1340 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1342 if(m_active_object_messages.size() == 0)
1343 return ActiveObjectMessage(0);
1345 return m_active_object_messages.pop_front();
1349 ************ Private methods *************
1352 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1356 if(object->getId() == 0){
1357 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1360 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1361 <<"no free ids available"<<std::endl;
1365 object->setId(new_id);
1368 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1369 <<"supplied with id "<<object->getId()<<std::endl;
1371 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1373 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1374 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1378 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1379 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1381 m_active_objects.insert(object->getId(), object);
1383 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1384 <<"Added id="<<object->getId()<<"; there are now "
1385 <<m_active_objects.size()<<" active objects."
1388 // Add static object to active static list of the block
1389 v3f objectpos = object->getBasePosition();
1390 std::string staticdata = object->getStaticData();
1391 StaticObject s_obj(object->getType(), objectpos, staticdata);
1392 // Add to the block where the object is located in
1393 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1394 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1397 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1398 object->m_static_exists = true;
1399 object->m_static_block = blockpos;
1402 block->setChangedFlag();
1405 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1406 <<"could not find block for storing id="<<object->getId()
1407 <<" statically"<<std::endl;
1410 return object->getId();
1414 Remove objects that satisfy (m_removed && m_known_by_count==0)
1416 void ServerEnvironment::removeRemovedObjects()
1418 core::list<u16> objects_to_remove;
1419 for(core::map<u16, ServerActiveObject*>::Iterator
1420 i = m_active_objects.getIterator();
1421 i.atEnd()==false; i++)
1423 u16 id = i.getNode()->getKey();
1424 ServerActiveObject* obj = i.getNode()->getValue();
1425 // This shouldn't happen but check it
1428 infostream<<"NULL object found in ServerEnvironment"
1429 <<" while finding removed objects. id="<<id<<std::endl;
1430 // Id to be removed from m_active_objects
1431 objects_to_remove.push_back(id);
1436 We will delete objects that are marked as removed or thatare
1437 waiting for deletion after deactivation
1439 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1443 Delete static data from block if is marked as removed
1445 if(obj->m_static_exists && obj->m_removed)
1447 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1450 block->m_static_objects.remove(id);
1451 block->setChangedFlag();
1455 // If m_known_by_count > 0, don't actually remove.
1456 if(obj->m_known_by_count > 0)
1461 // Id to be removed from m_active_objects
1462 objects_to_remove.push_back(id);
1464 // Remove references from m_active_objects
1465 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1466 i != objects_to_remove.end(); i++)
1468 m_active_objects.remove(*i);
1472 static void print_hexdump(std::ostream &o, const std::string &data)
1474 const int linelength = 16;
1475 for(int l=0; ; l++){
1476 int i0 = linelength * l;
1477 bool at_end = false;
1478 int thislinelength = linelength;
1479 if(i0 + thislinelength > (int)data.size()){
1480 thislinelength = data.size() - i0;
1483 for(int di=0; di<linelength; di++){
1486 if(di<thislinelength)
1487 snprintf(buf, 4, "%.2x ", data[i]);
1489 snprintf(buf, 4, " ");
1493 for(int di=0; di<thislinelength; di++){
1507 Convert stored objects from blocks near the players to active.
1509 void ServerEnvironment::activateObjects(MapBlock *block)
1513 // Ignore if no stored objects (to not set changed flag)
1514 if(block->m_static_objects.m_stored.size() == 0)
1516 verbosestream<<"ServerEnvironment::activateObjects(): "
1517 <<"activating objects of block "<<PP(block->getPos())
1518 <<" ("<<block->m_static_objects.m_stored.size()
1519 <<" objects)"<<std::endl;
1520 bool large_amount = (block->m_static_objects.m_stored.size() >= 51);
1522 errorstream<<"suspiciously large amount of objects detected: "
1523 <<block->m_static_objects.m_stored.size()<<" in "
1524 <<PP(block->getPos())
1525 <<"; not activating."<<std::endl;
1528 // A list for objects that couldn't be converted to static for some
1529 // reason. They will be stored back.
1530 core::list<StaticObject> new_stored;
1531 // Loop through stored static objects
1532 for(core::list<StaticObject>::Iterator
1533 i = block->m_static_objects.m_stored.begin();
1534 i != block->m_static_objects.m_stored.end(); i++)
1536 /*infostream<<"Server: Creating an active object from "
1537 <<"static data"<<std::endl;*/
1538 StaticObject &s_obj = *i;
1539 // Create an active object from the data
1540 ServerActiveObject *obj = ServerActiveObject::create
1541 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1542 // If couldn't create object, store static data back.
1545 errorstream<<"ServerEnvironment::activateObjects(): "
1546 <<"failed to create active object from static object "
1547 <<"in block "<<PP(s_obj.pos/BS)
1548 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1549 print_hexdump(verbosestream, s_obj.data);
1551 new_stored.push_back(s_obj);
1554 verbosestream<<"ServerEnvironment::activateObjects(): "
1555 <<"activated static object pos="<<PP(s_obj.pos/BS)
1556 <<" type="<<(int)s_obj.type<<std::endl;
1557 // This will also add the object to the active static list
1558 addActiveObjectRaw(obj, false);
1559 //u16 id = addActiveObjectRaw(obj, false);
1561 // Clear stored list
1562 block->m_static_objects.m_stored.clear();
1563 // Add leftover failed stuff to stored list
1564 for(core::list<StaticObject>::Iterator
1565 i = new_stored.begin();
1566 i != new_stored.end(); i++)
1568 StaticObject &s_obj = *i;
1569 block->m_static_objects.m_stored.push_back(s_obj);
1571 // Block has been modified
1572 // NOTE: No it has not really. Save I/O here.
1573 //block->setChangedFlag();
1577 Convert objects that are not in active blocks to static.
1579 If m_known_by_count != 0, active object is not deleted, but static
1580 data is still updated.
1582 If force_delete is set, active object is deleted nevertheless. It
1583 shall only be set so in the destructor of the environment.
1585 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1587 core::list<u16> objects_to_remove;
1588 for(core::map<u16, ServerActiveObject*>::Iterator
1589 i = m_active_objects.getIterator();
1590 i.atEnd()==false; i++)
1592 ServerActiveObject* obj = i.getNode()->getValue();
1594 // This shouldn't happen but check it
1597 infostream<<"NULL object found in ServerEnvironment"
1603 u16 id = i.getNode()->getKey();
1604 v3f objectpos = obj->getBasePosition();
1606 // The block in which the object resides in
1607 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1609 // If block is active, don't remove
1610 if(m_active_blocks.contains(blockpos_o))
1613 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1614 <<"deactivating object id="<<id<<" on inactive block "
1615 <<PP(blockpos_o)<<std::endl;
1618 Update the static data
1621 // Delete old static object
1622 MapBlock *oldblock = NULL;
1623 if(obj->m_static_exists)
1625 MapBlock *block = m_map->getBlockNoCreateNoEx
1626 (obj->m_static_block);
1629 block->m_static_objects.remove(id);
1632 obj->m_static_exists = false;
1635 // Create new static object
1636 std::string staticdata = obj->getStaticData();
1637 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1638 // Add to the block where the object is located in
1639 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1640 // Get or generate the block
1641 MapBlock *block = m_map->emergeBlock(blockpos);
1643 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1646 // Block not found. Is the old block still ok?
1649 // Load from disk or generate
1651 block = m_map->emergeBlock(blockpos);
1656 if(block->m_static_objects.m_stored.size() >= 50){
1657 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1658 <<" statically but block "<<PP(blockpos)
1659 <<" already contains over 50 objects."
1660 <<" Forcing delete."<<std::endl;
1661 force_delete = true;
1663 block->m_static_objects.insert(0, s_obj);
1664 block->setChangedFlag();
1665 obj->m_static_exists = true;
1666 obj->m_static_block = block->getPos();
1670 errorstream<<"ServerEnv: Could not find or generate "
1671 <<"a block for storing id="<<obj->getId()
1672 <<" statically"<<std::endl;
1677 Delete active object if not known by some client,
1678 else set pending deactivation
1681 // If known by some client, don't delete.
1682 if(obj->m_known_by_count > 0 && force_delete == false)
1684 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1685 <<"object id="<<id<<" is known by clients"
1686 <<"; not deleting yet"<<std::endl;
1688 obj->m_pending_deactivation = true;
1692 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1693 <<"object id="<<id<<" is not known by clients"
1694 <<"; deleting"<<std::endl;
1695 // Delete active object
1697 // Id to be removed from m_active_objects
1698 objects_to_remove.push_back(id);
1701 // Remove references from m_active_objects
1702 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1703 i != objects_to_remove.end(); i++)
1705 m_active_objects.remove(*i);
1716 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1724 ClientEnvironment::~ClientEnvironment()
1726 // delete active objects
1727 for(core::map<u16, ClientActiveObject*>::Iterator
1728 i = m_active_objects.getIterator();
1729 i.atEnd()==false; i++)
1731 delete i.getNode()->getValue();
1738 void ClientEnvironment::addPlayer(Player *player)
1740 DSTACK(__FUNCTION_NAME);
1742 It is a failure if player is local and there already is a local
1745 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1747 Environment::addPlayer(player);
1750 LocalPlayer * ClientEnvironment::getLocalPlayer()
1752 for(core::list<Player*>::Iterator i = m_players.begin();
1753 i != m_players.end(); i++)
1755 Player *player = *i;
1756 if(player->isLocal())
1757 return (LocalPlayer*)player;
1762 void ClientEnvironment::step(float dtime)
1764 DSTACK(__FUNCTION_NAME);
1766 // Get some settings
1767 bool free_move = g_settings->getBool("free_move");
1768 bool footprints = g_settings->getBool("footprints");
1771 LocalPlayer *lplayer = getLocalPlayer();
1773 // collision info queue
1774 core::list<CollisionInfo> player_collisions;
1777 Get the speed the player is going
1779 bool is_climbing = lplayer->is_climbing;
1781 f32 player_speed = 0.001; // just some small value
1782 player_speed = lplayer->getSpeed().getLength();
1785 Maximum position increment
1787 //f32 position_max_increment = 0.05*BS;
1788 f32 position_max_increment = 0.1*BS;
1790 // Maximum time increment (for collision detection etc)
1791 // time = distance / speed
1792 f32 dtime_max_increment = position_max_increment / player_speed;
1794 // Maximum time increment is 10ms or lower
1795 if(dtime_max_increment > 0.01)
1796 dtime_max_increment = 0.01;
1798 // Don't allow overly huge dtime
1802 f32 dtime_downcount = dtime;
1805 Stuff that has a maximum time increment
1814 if(dtime_downcount > dtime_max_increment)
1816 dtime_part = dtime_max_increment;
1817 dtime_downcount -= dtime_part;
1821 dtime_part = dtime_downcount;
1823 Setting this to 0 (no -=dtime_part) disables an infinite loop
1824 when dtime_part is so small that dtime_downcount -= dtime_part
1827 dtime_downcount = 0;
1835 v3f lplayerpos = lplayer->getPosition();
1838 if(free_move == false && is_climbing == false)
1841 v3f speed = lplayer->getSpeed();
1842 if(lplayer->swimming_up == false)
1843 speed.Y -= 9.81 * BS * dtime_part * 2;
1846 if(lplayer->in_water_stable || lplayer->in_water)
1848 f32 max_down = 2.0*BS;
1849 if(speed.Y < -max_down) speed.Y = -max_down;
1852 if(speed.getLength() > max)
1854 speed = speed / speed.getLength() * max;
1858 lplayer->setSpeed(speed);
1863 This also does collision detection.
1865 lplayer->move(dtime_part, *m_map, position_max_increment,
1866 &player_collisions);
1869 while(dtime_downcount > 0.001);
1871 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1873 for(core::list<CollisionInfo>::Iterator
1874 i = player_collisions.begin();
1875 i != player_collisions.end(); i++)
1877 CollisionInfo &info = *i;
1878 if(info.t == COLLISION_FALL)
1880 //f32 tolerance = BS*10; // 2 without damage
1881 f32 tolerance = BS*12; // 3 without damage
1883 if(info.speed > tolerance)
1885 f32 damage_f = (info.speed - tolerance)/BS*factor;
1886 u16 damage = (u16)(damage_f+0.5);
1887 if(lplayer->hp > damage)
1888 lplayer->hp -= damage;
1892 ClientEnvEvent event;
1893 event.type = CEE_PLAYER_DAMAGE;
1894 event.player_damage.amount = damage;
1895 m_client_event_queue.push_back(event);
1901 A quick draft of lava damage
1903 if(m_lava_hurt_interval.step(dtime, 1.0))
1905 v3f pf = lplayer->getPosition();
1907 // Feet, middle and head
1908 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1909 MapNode n1 = m_map->getNodeNoEx(p1);
1910 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1911 MapNode n2 = m_map->getNodeNoEx(p2);
1912 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1913 MapNode n3 = m_map->getNodeNoEx(p2);
1915 u32 damage_per_second = 0;
1916 damage_per_second = MYMAX(damage_per_second,
1917 content_features(n1).damage_per_second);
1918 damage_per_second = MYMAX(damage_per_second,
1919 content_features(n2).damage_per_second);
1920 damage_per_second = MYMAX(damage_per_second,
1921 content_features(n3).damage_per_second);
1923 if(damage_per_second != 0)
1925 ClientEnvEvent event;
1926 event.type = CEE_PLAYER_DAMAGE;
1927 event.player_damage.amount = damage_per_second;
1928 m_client_event_queue.push_back(event);
1933 Stuff that can be done in an arbitarily large dtime
1935 for(core::list<Player*>::Iterator i = m_players.begin();
1936 i != m_players.end(); i++)
1938 Player *player = *i;
1939 v3f playerpos = player->getPosition();
1942 Handle non-local players
1944 if(player->isLocal() == false)
1947 player->move(dtime, *m_map, 100*BS);
1951 // Update lighting on all players on client
1952 u8 light = LIGHT_MAX;
1955 v3s16 p = player->getLightPosition();
1956 MapNode n = m_map->getNode(p);
1957 light = n.getLightBlend(getDayNightRatio());
1959 catch(InvalidPositionException &e) {}
1960 player->updateLight(light);
1963 Add footsteps to grass
1967 // Get node that is at BS/4 under player
1968 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1970 MapNode n = m_map->getNode(bottompos);
1971 if(n.getContent() == CONTENT_GRASS)
1973 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1974 m_map->setNode(bottompos, n);
1975 // Update mesh on client
1976 if(m_map->mapType() == MAPTYPE_CLIENT)
1978 v3s16 p_blocks = getNodeBlockPos(bottompos);
1979 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1980 //b->updateMesh(getDayNightRatio());
1981 b->setMeshExpired(true);
1985 catch(InvalidPositionException &e)
1992 Step active objects and update lighting of them
1995 for(core::map<u16, ClientActiveObject*>::Iterator
1996 i = m_active_objects.getIterator();
1997 i.atEnd()==false; i++)
1999 ClientActiveObject* obj = i.getNode()->getValue();
2001 obj->step(dtime, this);
2003 if(m_active_object_light_update_interval.step(dtime, 0.21))
2006 //u8 light = LIGHT_MAX;
2010 v3s16 p = obj->getLightPosition();
2011 MapNode n = m_map->getNode(p);
2012 light = n.getLightBlend(getDayNightRatio());
2014 catch(InvalidPositionException &e) {}
2015 obj->updateLight(light);
2020 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2022 m_map->updateMeshes(blockpos, getDayNightRatio());
2025 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2027 m_map->expireMeshes(only_daynight_diffed);
2030 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2032 core::map<u16, ClientActiveObject*>::Node *n;
2033 n = m_active_objects.find(id);
2036 return n->getValue();
2039 bool isFreeClientActiveObjectId(u16 id,
2040 core::map<u16, ClientActiveObject*> &objects)
2045 for(core::map<u16, ClientActiveObject*>::Iterator
2046 i = objects.getIterator();
2047 i.atEnd()==false; i++)
2049 if(i.getNode()->getKey() == id)
2055 u16 getFreeClientActiveObjectId(
2056 core::map<u16, ClientActiveObject*> &objects)
2061 if(isFreeClientActiveObjectId(new_id, objects))
2071 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2074 if(object->getId() == 0)
2076 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2079 infostream<<"ClientEnvironment::addActiveObject(): "
2080 <<"no free ids available"<<std::endl;
2084 object->setId(new_id);
2086 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2088 infostream<<"ClientEnvironment::addActiveObject(): "
2089 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2093 infostream<<"ClientEnvironment::addActiveObject(): "
2094 <<"added (id="<<object->getId()<<")"<<std::endl;
2095 m_active_objects.insert(object->getId(), object);
2096 object->addToScene(m_smgr);
2097 return object->getId();
2100 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2101 const std::string &init_data)
2103 ClientActiveObject* obj = ClientActiveObject::create(type);
2106 infostream<<"ClientEnvironment::addActiveObject(): "
2107 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2114 obj->initialize(init_data);
2116 addActiveObject(obj);
2119 void ClientEnvironment::removeActiveObject(u16 id)
2121 infostream<<"ClientEnvironment::removeActiveObject(): "
2122 <<"id="<<id<<std::endl;
2123 ClientActiveObject* obj = getActiveObject(id);
2126 infostream<<"ClientEnvironment::removeActiveObject(): "
2127 <<"id="<<id<<" not found"<<std::endl;
2130 obj->removeFromScene();
2132 m_active_objects.remove(id);
2135 void ClientEnvironment::processActiveObjectMessage(u16 id,
2136 const std::string &data)
2138 ClientActiveObject* obj = getActiveObject(id);
2141 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2142 <<" got message for id="<<id<<", which doesn't exist."
2146 obj->processMessage(data);
2150 Callbacks for activeobjects
2153 void ClientEnvironment::damageLocalPlayer(u8 damage)
2155 LocalPlayer *lplayer = getLocalPlayer();
2158 if(lplayer->hp > damage)
2159 lplayer->hp -= damage;
2163 ClientEnvEvent event;
2164 event.type = CEE_PLAYER_DAMAGE;
2165 event.player_damage.amount = damage;
2166 m_client_event_queue.push_back(event);
2170 Client likes to call these
2173 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2174 core::array<DistanceSortedActiveObject> &dest)
2176 for(core::map<u16, ClientActiveObject*>::Iterator
2177 i = m_active_objects.getIterator();
2178 i.atEnd()==false; i++)
2180 ClientActiveObject* obj = i.getNode()->getValue();
2182 f32 d = (obj->getPosition() - origin).getLength();
2187 DistanceSortedActiveObject dso(obj, d);
2189 dest.push_back(dso);
2193 ClientEnvEvent ClientEnvironment::getClientEvent()
2195 if(m_client_event_queue.size() == 0)
2197 ClientEnvEvent event;
2198 event.type = CEE_NONE;
2201 return m_client_event_queue.pop_front();
2204 #endif // #ifndef SERVER