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()
891 + block->m_static_objects.m_stored.size();
893 /*if(block->m_static_objects.m_stored.size() != 0){
894 errorstream<<"ServerEnvironment::step(): "
895 <<PP(block->getPos())<<" contains "
896 <<block->m_static_objects.m_stored.size()
897 <<" stored objects; "
898 <<"when spawning objects, when counting active "
899 <<"objects in wide area. relative position: "
900 <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
905 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
906 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
907 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
909 v3s16 p = p0 + block->getPosRelative();
910 MapNode n = block->getNodeNoEx(p0);
914 Convert mud under proper lighting to grass
916 if(n.getContent() == CONTENT_MUD)
920 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
921 if(content_features(n_top).air_equivalent &&
922 n_top.getLightBlend(getDayNightRatio()) >= 13)
924 n.setContent(CONTENT_GRASS);
925 m_map->addNodeWithEvent(p, n);
930 Convert grass into mud if under something else than air
932 if(n.getContent() == CONTENT_GRASS)
934 //if(myrand()%20 == 0)
936 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
937 if(content_features(n_top).air_equivalent == false)
939 n.setContent(CONTENT_MUD);
940 m_map->addNodeWithEvent(p, n);
945 Rats spawn around regular trees
947 if(n.getContent() == CONTENT_TREE ||
948 n.getContent() == CONTENT_JUNGLETREE)
950 if(myrand()%200 == 0 && active_object_count_wider == 0)
952 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
953 0, myrand_range(-2, 2));
954 MapNode n1 = m_map->getNodeNoEx(p1);
955 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
956 if(n1b.getContent() == CONTENT_GRASS &&
957 n1.getContent() == CONTENT_AIR)
959 v3f pos = intToFloat(p1, BS);
960 ServerActiveObject *obj = new RatSAO(this, 0, pos);
961 addActiveObject(obj);
966 Fun things spawn in caves and dungeons
968 if(n.getContent() == CONTENT_STONE ||
969 n.getContent() == CONTENT_MOSSYCOBBLE)
971 if(myrand()%200 == 0 && active_object_count_wider == 0)
973 v3s16 p1 = p + v3s16(0,1,0);
974 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
975 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
976 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
977 if(n1a.getContent() == CONTENT_AIR &&
978 n1b.getContent() == CONTENT_AIR)
980 v3f pos = intToFloat(p1, BS);
982 if(i == 0 || i == 1){
983 actionstream<<"A dungeon master spawns at "
986 getMob_dungeon_master(properties);
987 ServerActiveObject *obj = new MobV2SAO(
988 this, 0, pos, &properties);
989 addActiveObject(obj);
990 } else if(i == 2 || i == 3){
991 actionstream<<"Rats spawn at "
993 for(int j=0; j<3; j++){
994 ServerActiveObject *obj = new RatSAO(
996 addActiveObject(obj);
999 actionstream<<"An oerkki spawns at "
1000 <<PP(p1)<<std::endl;
1001 ServerActiveObject *obj = new Oerkki1SAO(
1003 addActiveObject(obj);
1010 Make trees from saplings!
1012 if(n.getContent() == CONTENT_SAPLING)
1014 if(myrand()%50 == 0)
1016 actionstream<<"A sapling grows into a tree at "
1019 core::map<v3s16, MapBlock*> modified_blocks;
1021 ManualMapVoxelManipulator vmanip(m_map);
1022 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1023 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1024 bool is_apple_tree = myrand()%4 == 0;
1025 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1026 vmanip.blitBackAll(&modified_blocks);
1029 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1030 for(core::map<v3s16, MapBlock*>::Iterator
1031 i = modified_blocks.getIterator();
1032 i.atEnd() == false; i++)
1034 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1036 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1038 // Send a MEET_OTHER event
1040 event.type = MEET_OTHER;
1041 for(core::map<v3s16, MapBlock*>::Iterator
1042 i = modified_blocks.getIterator();
1043 i.atEnd() == false; i++)
1045 v3s16 p = i.getNode()->getKey();
1046 event.modified_blocks.insert(p, true);
1048 m_map->dispatchEvent(&event);
1059 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1060 //TimeTaker timer("Step active objects");
1062 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1064 // This helps the objects to send data at the same time
1065 bool send_recommended = false;
1066 m_send_recommended_timer += dtime;
1067 if(m_send_recommended_timer > 0.10)
1069 m_send_recommended_timer = 0;
1070 send_recommended = true;
1073 for(core::map<u16, ServerActiveObject*>::Iterator
1074 i = m_active_objects.getIterator();
1075 i.atEnd()==false; i++)
1077 ServerActiveObject* obj = i.getNode()->getValue();
1078 // Remove non-peaceful mobs on peaceful mode
1079 if(g_settings->getBool("only_peaceful_mobs")){
1080 if(!obj->isPeaceful())
1081 obj->m_removed = true;
1083 // Don't step if is to be removed or stored statically
1084 if(obj->m_removed || obj->m_pending_deactivation)
1087 obj->step(dtime, send_recommended);
1088 // Read messages from object
1089 while(obj->m_messages_out.size() > 0)
1091 m_active_object_messages.push_back(
1092 obj->m_messages_out.pop_front());
1098 Manage active objects
1100 if(m_object_management_interval.step(dtime, 0.5))
1102 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1104 Remove objects that satisfy (m_removed && m_known_by_count==0)
1106 removeRemovedObjects();
1109 if(g_settings->getBool("enable_experimental"))
1116 m_random_spawn_timer -= dtime;
1117 if(m_random_spawn_timer < 0)
1119 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1120 //m_random_spawn_timer += 2.0;
1121 m_random_spawn_timer += 200.0;
1127 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1128 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1129 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1131 Player *player = getRandomConnectedPlayer();
1134 pos = player->getPosition();
1136 myrand_range(-3,3)*BS,
1138 myrand_range(-3,3)*BS
1142 Create a ServerActiveObject
1145 //TestSAO *obj = new TestSAO(this, 0, pos);
1146 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1147 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1148 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1149 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1151 infostream<<"Server: Spawning MobV2SAO at "
1152 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1154 Settings properties;
1155 getMob_dungeon_master(properties);
1156 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1157 addActiveObject(obj);
1161 } // enable_experimental
1164 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1166 core::map<u16, ServerActiveObject*>::Node *n;
1167 n = m_active_objects.find(id);
1170 return n->getValue();
1173 bool isFreeServerActiveObjectId(u16 id,
1174 core::map<u16, ServerActiveObject*> &objects)
1179 for(core::map<u16, ServerActiveObject*>::Iterator
1180 i = objects.getIterator();
1181 i.atEnd()==false; i++)
1183 if(i.getNode()->getKey() == id)
1189 u16 getFreeServerActiveObjectId(
1190 core::map<u16, ServerActiveObject*> &objects)
1195 if(isFreeServerActiveObjectId(new_id, objects))
1205 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1208 u16 id = addActiveObjectRaw(object, true);
1212 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1216 v3f objectpos = obj->getBasePosition();
1218 // The block in which the object resides in
1219 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1222 Update the static data
1225 // Create new static object
1226 std::string staticdata = obj->getStaticData();
1227 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1228 // Add to the block where the object is located in
1229 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1230 // Get or generate the block
1231 MapBlock *block = m_map->emergeBlock(blockpos);
1233 bool succeeded = false;
1237 block->m_static_objects.insert(0, s_obj);
1238 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1242 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1243 <<"Could not find or generate "
1244 <<"a block for storing static object"<<std::endl;
1254 Finds out what new objects have been added to
1255 inside a radius around a position
1257 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1258 core::map<u16, bool> ¤t_objects,
1259 core::map<u16, bool> &added_objects)
1261 v3f pos_f = intToFloat(pos, BS);
1262 f32 radius_f = radius * BS;
1264 Go through the object list,
1265 - discard m_removed objects,
1266 - discard objects that are too far away,
1267 - discard objects that are found in current_objects.
1268 - add remaining objects to added_objects
1270 for(core::map<u16, ServerActiveObject*>::Iterator
1271 i = m_active_objects.getIterator();
1272 i.atEnd()==false; i++)
1274 u16 id = i.getNode()->getKey();
1276 ServerActiveObject *object = i.getNode()->getValue();
1279 // Discard if removed
1280 if(object->m_removed)
1282 // Discard if too far
1283 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1284 if(distance_f > radius_f)
1286 // Discard if already on current_objects
1287 core::map<u16, bool>::Node *n;
1288 n = current_objects.find(id);
1291 // Add to added_objects
1292 added_objects.insert(id, false);
1297 Finds out what objects have been removed from
1298 inside a radius around a position
1300 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1301 core::map<u16, bool> ¤t_objects,
1302 core::map<u16, bool> &removed_objects)
1304 v3f pos_f = intToFloat(pos, BS);
1305 f32 radius_f = radius * BS;
1307 Go through current_objects; object is removed if:
1308 - object is not found in m_active_objects (this is actually an
1309 error condition; objects should be set m_removed=true and removed
1310 only after all clients have been informed about removal), or
1311 - object has m_removed=true, or
1312 - object is too far away
1314 for(core::map<u16, bool>::Iterator
1315 i = current_objects.getIterator();
1316 i.atEnd()==false; i++)
1318 u16 id = i.getNode()->getKey();
1319 ServerActiveObject *object = getActiveObject(id);
1322 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1323 <<" object in current_objects is NULL"<<std::endl;
1325 else if(object->m_removed == false)
1327 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1328 /*infostream<<"removed == false"
1329 <<"distance_f = "<<distance_f
1330 <<", radius_f = "<<radius_f<<std::endl;*/
1331 if(distance_f < radius_f)
1337 removed_objects.insert(id, false);
1341 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1343 if(m_active_object_messages.size() == 0)
1344 return ActiveObjectMessage(0);
1346 return m_active_object_messages.pop_front();
1350 ************ Private methods *************
1353 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1357 if(object->getId() == 0){
1358 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1361 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1362 <<"no free ids available"<<std::endl;
1366 object->setId(new_id);
1369 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1370 <<"supplied with id "<<object->getId()<<std::endl;
1372 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1374 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1375 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1379 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1380 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1382 m_active_objects.insert(object->getId(), object);
1384 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1385 <<"Added id="<<object->getId()<<"; there are now "
1386 <<m_active_objects.size()<<" active objects."
1389 // Add static object to active static list of the block
1390 v3f objectpos = object->getBasePosition();
1391 std::string staticdata = object->getStaticData();
1392 StaticObject s_obj(object->getType(), objectpos, staticdata);
1393 // Add to the block where the object is located in
1394 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1395 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1398 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1399 object->m_static_exists = true;
1400 object->m_static_block = blockpos;
1403 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1406 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1407 <<"could not find block for storing id="<<object->getId()
1408 <<" statically"<<std::endl;
1411 return object->getId();
1415 Remove objects that satisfy (m_removed && m_known_by_count==0)
1417 void ServerEnvironment::removeRemovedObjects()
1419 core::list<u16> objects_to_remove;
1420 for(core::map<u16, ServerActiveObject*>::Iterator
1421 i = m_active_objects.getIterator();
1422 i.atEnd()==false; i++)
1424 u16 id = i.getNode()->getKey();
1425 ServerActiveObject* obj = i.getNode()->getValue();
1426 // This shouldn't happen but check it
1429 infostream<<"NULL object found in ServerEnvironment"
1430 <<" while finding removed objects. id="<<id<<std::endl;
1431 // Id to be removed from m_active_objects
1432 objects_to_remove.push_back(id);
1437 We will delete objects that are marked as removed or thatare
1438 waiting for deletion after deactivation
1440 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1444 Delete static data from block if is marked as removed
1446 if(obj->m_static_exists && obj->m_removed)
1448 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1451 block->m_static_objects.remove(id);
1452 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1453 obj->m_static_exists = false;
1457 // If m_known_by_count > 0, don't actually remove.
1458 if(obj->m_known_by_count > 0)
1463 // Id to be removed from m_active_objects
1464 objects_to_remove.push_back(id);
1466 // Remove references from m_active_objects
1467 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1468 i != objects_to_remove.end(); i++)
1470 m_active_objects.remove(*i);
1474 static void print_hexdump(std::ostream &o, const std::string &data)
1476 const int linelength = 16;
1477 for(int l=0; ; l++){
1478 int i0 = linelength * l;
1479 bool at_end = false;
1480 int thislinelength = linelength;
1481 if(i0 + thislinelength > (int)data.size()){
1482 thislinelength = data.size() - i0;
1485 for(int di=0; di<linelength; di++){
1488 if(di<thislinelength)
1489 snprintf(buf, 4, "%.2x ", data[i]);
1491 snprintf(buf, 4, " ");
1495 for(int di=0; di<thislinelength; di++){
1509 Convert stored objects from blocks near the players to active.
1511 void ServerEnvironment::activateObjects(MapBlock *block)
1515 // Ignore if no stored objects (to not set changed flag)
1516 if(block->m_static_objects.m_stored.size() == 0)
1518 verbosestream<<"ServerEnvironment::activateObjects(): "
1519 <<"activating objects of block "<<PP(block->getPos())
1520 <<" ("<<block->m_static_objects.m_stored.size()
1521 <<" objects)"<<std::endl;
1522 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1524 errorstream<<"suspiciously large amount of objects detected: "
1525 <<block->m_static_objects.m_stored.size()<<" in "
1526 <<PP(block->getPos())
1527 <<"; removing all of them."<<std::endl;
1528 // Clear stored list
1529 block->m_static_objects.m_stored.clear();
1530 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1533 // A list for objects that couldn't be converted to static for some
1534 // reason. They will be stored back.
1535 core::list<StaticObject> new_stored;
1536 // Loop through stored static objects
1537 for(core::list<StaticObject>::Iterator
1538 i = block->m_static_objects.m_stored.begin();
1539 i != block->m_static_objects.m_stored.end(); i++)
1541 /*infostream<<"Server: Creating an active object from "
1542 <<"static data"<<std::endl;*/
1543 StaticObject &s_obj = *i;
1544 // Create an active object from the data
1545 ServerActiveObject *obj = ServerActiveObject::create
1546 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1547 // If couldn't create object, store static data back.
1550 errorstream<<"ServerEnvironment::activateObjects(): "
1551 <<"failed to create active object from static object "
1552 <<"in block "<<PP(s_obj.pos/BS)
1553 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1554 print_hexdump(verbosestream, s_obj.data);
1556 new_stored.push_back(s_obj);
1559 verbosestream<<"ServerEnvironment::activateObjects(): "
1560 <<"activated static object pos="<<PP(s_obj.pos/BS)
1561 <<" type="<<(int)s_obj.type<<std::endl;
1562 // This will also add the object to the active static list
1563 addActiveObjectRaw(obj, false);
1565 // Clear stored list
1566 block->m_static_objects.m_stored.clear();
1567 // Add leftover failed stuff to stored list
1568 for(core::list<StaticObject>::Iterator
1569 i = new_stored.begin();
1570 i != new_stored.end(); i++)
1572 StaticObject &s_obj = *i;
1573 block->m_static_objects.m_stored.push_back(s_obj);
1576 Note: Block hasn't really been modified here.
1577 The objects have just been activated and moved from the stored
1578 static list to the active static list.
1579 As such, the block is essentially the same.
1580 Thus, do not call block->setChangedFlag().
1581 Otherwise there would be a huge amount of unnecessary I/O.
1586 Convert objects that are not standing inside active blocks to static.
1588 If m_known_by_count != 0, active object is not deleted, but static
1589 data is still updated.
1591 If force_delete is set, active object is deleted nevertheless. It
1592 shall only be set so in the destructor of the environment.
1594 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1596 core::list<u16> objects_to_remove;
1597 for(core::map<u16, ServerActiveObject*>::Iterator
1598 i = m_active_objects.getIterator();
1599 i.atEnd()==false; i++)
1601 ServerActiveObject* obj = i.getNode()->getValue();
1603 // This shouldn't happen but check it
1606 errorstream<<"NULL object found in ServerEnvironment"
1612 u16 id = i.getNode()->getKey();
1613 v3f objectpos = obj->getBasePosition();
1615 // The block in which the object resides in
1616 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1618 // If block is active, don't remove
1619 if(m_active_blocks.contains(blockpos_o))
1622 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1623 <<"deactivating object id="<<id<<" on inactive block "
1624 <<PP(blockpos_o)<<std::endl;
1627 Update the static data
1630 // Delete old static object
1631 if(obj->m_static_exists)
1633 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1636 block->m_static_objects.remove(id);
1637 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1638 obj->m_static_exists = false;
1642 // Create new static object
1643 std::string staticdata = obj->getStaticData();
1644 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1645 // Add to the block where the object is located in
1646 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1647 // Get or generate the block
1648 MapBlock *block = m_map->emergeBlock(blockpos);
1652 if(block->m_static_objects.m_stored.size() >= 49){
1653 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1654 <<" statically but block "<<PP(blockpos)
1655 <<" already contains "
1656 <<block->m_static_objects.m_stored.size()
1657 <<" (over 49) objects."
1658 <<" Forcing delete."<<std::endl;
1659 force_delete = true;
1661 block->m_static_objects.insert(0, s_obj);
1662 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1663 obj->m_static_exists = true;
1664 obj->m_static_block = block->getPos();
1668 errorstream<<"ServerEnv: Could not find or generate "
1669 <<"a block for storing id="<<obj->getId()
1670 <<" statically"<<std::endl;
1675 Delete active object if not known by some client,
1676 else set pending deactivation
1679 // If known by some client, don't delete.
1680 if(obj->m_known_by_count > 0 && force_delete == false)
1682 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1683 <<"object id="<<id<<" is known by clients"
1684 <<"; not deleting yet"<<std::endl;
1686 obj->m_pending_deactivation = true;
1690 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1691 <<"object id="<<id<<" is not known by clients"
1692 <<"; deleting"<<std::endl;
1693 // Delete active object
1695 // Id to be removed from m_active_objects
1696 objects_to_remove.push_back(id);
1699 // Remove references from m_active_objects
1700 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1701 i != objects_to_remove.end(); i++)
1703 m_active_objects.remove(*i);
1714 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1722 ClientEnvironment::~ClientEnvironment()
1724 // delete active objects
1725 for(core::map<u16, ClientActiveObject*>::Iterator
1726 i = m_active_objects.getIterator();
1727 i.atEnd()==false; i++)
1729 delete i.getNode()->getValue();
1736 void ClientEnvironment::addPlayer(Player *player)
1738 DSTACK(__FUNCTION_NAME);
1740 It is a failure if player is local and there already is a local
1743 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1745 Environment::addPlayer(player);
1748 LocalPlayer * ClientEnvironment::getLocalPlayer()
1750 for(core::list<Player*>::Iterator i = m_players.begin();
1751 i != m_players.end(); i++)
1753 Player *player = *i;
1754 if(player->isLocal())
1755 return (LocalPlayer*)player;
1760 void ClientEnvironment::step(float dtime)
1762 DSTACK(__FUNCTION_NAME);
1764 // Get some settings
1765 bool free_move = g_settings->getBool("free_move");
1766 bool footprints = g_settings->getBool("footprints");
1769 LocalPlayer *lplayer = getLocalPlayer();
1771 // collision info queue
1772 core::list<CollisionInfo> player_collisions;
1775 Get the speed the player is going
1777 bool is_climbing = lplayer->is_climbing;
1779 f32 player_speed = 0.001; // just some small value
1780 player_speed = lplayer->getSpeed().getLength();
1783 Maximum position increment
1785 //f32 position_max_increment = 0.05*BS;
1786 f32 position_max_increment = 0.1*BS;
1788 // Maximum time increment (for collision detection etc)
1789 // time = distance / speed
1790 f32 dtime_max_increment = position_max_increment / player_speed;
1792 // Maximum time increment is 10ms or lower
1793 if(dtime_max_increment > 0.01)
1794 dtime_max_increment = 0.01;
1796 // Don't allow overly huge dtime
1800 f32 dtime_downcount = dtime;
1803 Stuff that has a maximum time increment
1812 if(dtime_downcount > dtime_max_increment)
1814 dtime_part = dtime_max_increment;
1815 dtime_downcount -= dtime_part;
1819 dtime_part = dtime_downcount;
1821 Setting this to 0 (no -=dtime_part) disables an infinite loop
1822 when dtime_part is so small that dtime_downcount -= dtime_part
1825 dtime_downcount = 0;
1833 v3f lplayerpos = lplayer->getPosition();
1836 if(free_move == false && is_climbing == false)
1839 v3f speed = lplayer->getSpeed();
1840 if(lplayer->swimming_up == false)
1841 speed.Y -= 9.81 * BS * dtime_part * 2;
1844 if(lplayer->in_water_stable || lplayer->in_water)
1846 f32 max_down = 2.0*BS;
1847 if(speed.Y < -max_down) speed.Y = -max_down;
1850 if(speed.getLength() > max)
1852 speed = speed / speed.getLength() * max;
1856 lplayer->setSpeed(speed);
1861 This also does collision detection.
1863 lplayer->move(dtime_part, *m_map, position_max_increment,
1864 &player_collisions);
1867 while(dtime_downcount > 0.001);
1869 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1871 for(core::list<CollisionInfo>::Iterator
1872 i = player_collisions.begin();
1873 i != player_collisions.end(); i++)
1875 CollisionInfo &info = *i;
1876 if(info.t == COLLISION_FALL)
1878 //f32 tolerance = BS*10; // 2 without damage
1879 f32 tolerance = BS*12; // 3 without damage
1881 if(info.speed > tolerance)
1883 f32 damage_f = (info.speed - tolerance)/BS*factor;
1884 u16 damage = (u16)(damage_f+0.5);
1885 if(lplayer->hp > damage)
1886 lplayer->hp -= damage;
1890 ClientEnvEvent event;
1891 event.type = CEE_PLAYER_DAMAGE;
1892 event.player_damage.amount = damage;
1893 m_client_event_queue.push_back(event);
1899 A quick draft of lava damage
1901 if(m_lava_hurt_interval.step(dtime, 1.0))
1903 v3f pf = lplayer->getPosition();
1905 // Feet, middle and head
1906 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1907 MapNode n1 = m_map->getNodeNoEx(p1);
1908 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1909 MapNode n2 = m_map->getNodeNoEx(p2);
1910 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1911 MapNode n3 = m_map->getNodeNoEx(p2);
1913 u32 damage_per_second = 0;
1914 damage_per_second = MYMAX(damage_per_second,
1915 content_features(n1).damage_per_second);
1916 damage_per_second = MYMAX(damage_per_second,
1917 content_features(n2).damage_per_second);
1918 damage_per_second = MYMAX(damage_per_second,
1919 content_features(n3).damage_per_second);
1921 if(damage_per_second != 0)
1923 ClientEnvEvent event;
1924 event.type = CEE_PLAYER_DAMAGE;
1925 event.player_damage.amount = damage_per_second;
1926 m_client_event_queue.push_back(event);
1931 Stuff that can be done in an arbitarily large dtime
1933 for(core::list<Player*>::Iterator i = m_players.begin();
1934 i != m_players.end(); i++)
1936 Player *player = *i;
1937 v3f playerpos = player->getPosition();
1940 Handle non-local players
1942 if(player->isLocal() == false)
1945 player->move(dtime, *m_map, 100*BS);
1949 // Update lighting on all players on client
1950 u8 light = LIGHT_MAX;
1953 v3s16 p = player->getLightPosition();
1954 MapNode n = m_map->getNode(p);
1955 light = n.getLightBlend(getDayNightRatio());
1957 catch(InvalidPositionException &e) {}
1958 player->updateLight(light);
1961 Add footsteps to grass
1965 // Get node that is at BS/4 under player
1966 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1968 MapNode n = m_map->getNode(bottompos);
1969 if(n.getContent() == CONTENT_GRASS)
1971 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1972 m_map->setNode(bottompos, n);
1973 // Update mesh on client
1974 if(m_map->mapType() == MAPTYPE_CLIENT)
1976 v3s16 p_blocks = getNodeBlockPos(bottompos);
1977 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1978 //b->updateMesh(getDayNightRatio());
1979 b->setMeshExpired(true);
1983 catch(InvalidPositionException &e)
1990 Step active objects and update lighting of them
1993 for(core::map<u16, ClientActiveObject*>::Iterator
1994 i = m_active_objects.getIterator();
1995 i.atEnd()==false; i++)
1997 ClientActiveObject* obj = i.getNode()->getValue();
1999 obj->step(dtime, this);
2001 if(m_active_object_light_update_interval.step(dtime, 0.21))
2004 //u8 light = LIGHT_MAX;
2008 v3s16 p = obj->getLightPosition();
2009 MapNode n = m_map->getNode(p);
2010 light = n.getLightBlend(getDayNightRatio());
2012 catch(InvalidPositionException &e) {}
2013 obj->updateLight(light);
2018 void ClientEnvironment::updateMeshes(v3s16 blockpos)
2020 m_map->updateMeshes(blockpos, getDayNightRatio());
2023 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
2025 m_map->expireMeshes(only_daynight_diffed);
2028 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2030 core::map<u16, ClientActiveObject*>::Node *n;
2031 n = m_active_objects.find(id);
2034 return n->getValue();
2037 bool isFreeClientActiveObjectId(u16 id,
2038 core::map<u16, ClientActiveObject*> &objects)
2043 for(core::map<u16, ClientActiveObject*>::Iterator
2044 i = objects.getIterator();
2045 i.atEnd()==false; i++)
2047 if(i.getNode()->getKey() == id)
2053 u16 getFreeClientActiveObjectId(
2054 core::map<u16, ClientActiveObject*> &objects)
2059 if(isFreeClientActiveObjectId(new_id, objects))
2069 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2072 if(object->getId() == 0)
2074 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2077 infostream<<"ClientEnvironment::addActiveObject(): "
2078 <<"no free ids available"<<std::endl;
2082 object->setId(new_id);
2084 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2086 infostream<<"ClientEnvironment::addActiveObject(): "
2087 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2091 infostream<<"ClientEnvironment::addActiveObject(): "
2092 <<"added (id="<<object->getId()<<")"<<std::endl;
2093 m_active_objects.insert(object->getId(), object);
2094 object->addToScene(m_smgr);
2095 return object->getId();
2098 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2099 const std::string &init_data)
2101 ClientActiveObject* obj = ClientActiveObject::create(type);
2104 infostream<<"ClientEnvironment::addActiveObject(): "
2105 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2112 obj->initialize(init_data);
2114 addActiveObject(obj);
2117 void ClientEnvironment::removeActiveObject(u16 id)
2119 infostream<<"ClientEnvironment::removeActiveObject(): "
2120 <<"id="<<id<<std::endl;
2121 ClientActiveObject* obj = getActiveObject(id);
2124 infostream<<"ClientEnvironment::removeActiveObject(): "
2125 <<"id="<<id<<" not found"<<std::endl;
2128 obj->removeFromScene();
2130 m_active_objects.remove(id);
2133 void ClientEnvironment::processActiveObjectMessage(u16 id,
2134 const std::string &data)
2136 ClientActiveObject* obj = getActiveObject(id);
2139 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2140 <<" got message for id="<<id<<", which doesn't exist."
2144 obj->processMessage(data);
2148 Callbacks for activeobjects
2151 void ClientEnvironment::damageLocalPlayer(u8 damage)
2153 LocalPlayer *lplayer = getLocalPlayer();
2156 if(lplayer->hp > damage)
2157 lplayer->hp -= damage;
2161 ClientEnvEvent event;
2162 event.type = CEE_PLAYER_DAMAGE;
2163 event.player_damage.amount = damage;
2164 m_client_event_queue.push_back(event);
2168 Client likes to call these
2171 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2172 core::array<DistanceSortedActiveObject> &dest)
2174 for(core::map<u16, ClientActiveObject*>::Iterator
2175 i = m_active_objects.getIterator();
2176 i.atEnd()==false; i++)
2178 ClientActiveObject* obj = i.getNode()->getValue();
2180 f32 d = (obj->getPosition() - origin).getLength();
2185 DistanceSortedActiveObject dso(obj, d);
2187 dest.push_back(dso);
2191 ClientEnvEvent ClientEnvironment::getClientEvent()
2193 if(m_client_event_queue.size() == 0)
2195 ClientEnvEvent event;
2196 event.type = CEE_NONE;
2199 return m_client_event_queue.pop_front();
2202 #endif // #ifndef SERVER