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"
32 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
34 Environment::Environment():
39 Environment::~Environment()
42 for(core::list<Player*>::Iterator i = m_players.begin();
43 i != m_players.end(); i++)
49 void Environment::addPlayer(Player *player)
51 DSTACK(__FUNCTION_NAME);
53 Check that peer_ids are unique.
54 Also check that names are unique.
55 Exception: there can be multiple players with peer_id=0
57 // If peer id is non-zero, it has to be unique.
58 if(player->peer_id != 0)
59 assert(getPlayer(player->peer_id) == NULL);
60 // Name has to be unique.
61 assert(getPlayer(player->getName()) == NULL);
63 m_players.push_back(player);
66 void Environment::removePlayer(u16 peer_id)
68 DSTACK(__FUNCTION_NAME);
70 for(core::list<Player*>::Iterator i = m_players.begin();
71 i != m_players.end(); i++)
74 if(player->peer_id != peer_id)
79 // See if there is an another one
80 // (shouldn't be, but just to be sure)
85 Player * Environment::getPlayer(u16 peer_id)
87 for(core::list<Player*>::Iterator i = m_players.begin();
88 i != m_players.end(); i++)
91 if(player->peer_id == peer_id)
97 Player * Environment::getPlayer(const char *name)
99 for(core::list<Player*>::Iterator i = m_players.begin();
100 i != m_players.end(); i++)
103 if(strcmp(player->getName(), name) == 0)
109 Player * Environment::getRandomConnectedPlayer()
111 core::list<Player*> connected_players = getPlayers(true);
112 u32 chosen_one = myrand() % connected_players.size();
114 for(core::list<Player*>::Iterator
115 i = connected_players.begin();
116 i != connected_players.end(); i++)
128 Player * Environment::getNearestConnectedPlayer(v3f pos)
130 core::list<Player*> connected_players = getPlayers(true);
132 Player *nearest_player = NULL;
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
138 f32 d = player->getPosition().getDistanceFrom(pos);
139 if(d < nearest_d || nearest_player == NULL)
142 nearest_player = player;
145 return nearest_player;
148 core::list<Player*> Environment::getPlayers()
153 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
155 core::list<Player*> newlist;
156 for(core::list<Player*>::Iterator
157 i = m_players.begin();
158 i != m_players.end(); i++)
162 if(ignore_disconnected)
164 // Ignore disconnected players
165 if(player->peer_id == 0)
169 newlist.push_back(player);
174 void Environment::printPlayers(std::ostream &o)
176 o<<"Players in environment:"<<std::endl;
177 for(core::list<Player*>::Iterator i = m_players.begin();
178 i != m_players.end(); i++)
181 o<<"Player peer_id="<<player->peer_id<<std::endl;
185 /*void Environment::setDayNightRatio(u32 r)
187 getDayNightRatio() = r;
190 u32 Environment::getDayNightRatio()
192 //return getDayNightRatio();
193 return time_to_daynight_ratio(m_time_of_day);
200 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
203 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
204 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
205 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
212 void ActiveBlockList::update(core::list<v3s16> &active_positions,
214 core::map<v3s16, bool> &blocks_removed,
215 core::map<v3s16, bool> &blocks_added)
220 core::map<v3s16, bool> newlist;
221 for(core::list<v3s16>::Iterator i = active_positions.begin();
222 i != active_positions.end(); i++)
224 fillRadiusBlock(*i, radius, newlist);
228 Find out which blocks on the old list are not on the new list
230 // Go through old list
231 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
232 i.atEnd()==false; i++)
234 v3s16 p = i.getNode()->getKey();
235 // If not on new list, it's been removed
236 if(newlist.find(p) == NULL)
237 blocks_removed.insert(p, true);
241 Find out which blocks on the new list are not on the old list
243 // Go through new list
244 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
245 i.atEnd()==false; i++)
247 v3s16 p = i.getNode()->getKey();
248 // If not on old list, it's been added
249 if(m_list.find(p) == NULL)
250 blocks_added.insert(p, true);
257 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
258 i.atEnd()==false; i++)
260 v3s16 p = i.getNode()->getKey();
261 m_list.insert(p, true);
269 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
272 m_random_spawn_timer(3),
273 m_send_recommended_timer(0),
275 m_game_time_fraction_counter(0)
279 ServerEnvironment::~ServerEnvironment()
281 // Clear active block list.
282 // This makes the next one delete all active objects.
283 m_active_blocks.clear();
285 // Convert all objects to static and delete the active objects
286 deactivateFarObjects(true);
292 void ServerEnvironment::serializePlayers(const std::string &savedir)
294 std::string players_path = savedir + "/players";
295 fs::CreateDir(players_path);
297 core::map<Player*, bool> saved_players;
299 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
300 for(u32 i=0; i<player_files.size(); i++)
302 if(player_files[i].dir)
305 // Full path to this file
306 std::string path = players_path + "/" + player_files[i].name;
308 //infostream<<"Checking player file "<<path<<std::endl;
310 // Load player to see what is its name
311 ServerRemotePlayer testplayer;
313 // Open file and deserialize
314 std::ifstream is(path.c_str(), std::ios_base::binary);
315 if(is.good() == false)
317 infostream<<"Failed to read "<<path<<std::endl;
320 testplayer.deSerialize(is);
323 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
325 // Search for the player
326 std::string playername = testplayer.getName();
327 Player *player = getPlayer(playername.c_str());
330 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
334 //infostream<<"Found matching player, overwriting."<<std::endl;
336 // OK, found. Save player there.
338 // Open file and serialize
339 std::ofstream os(path.c_str(), std::ios_base::binary);
340 if(os.good() == false)
342 infostream<<"Failed to overwrite "<<path<<std::endl;
345 player->serialize(os);
346 saved_players.insert(player, true);
350 for(core::list<Player*>::Iterator i = m_players.begin();
351 i != m_players.end(); i++)
354 if(saved_players.find(player) != NULL)
356 /*infostream<<"Player "<<player->getName()
357 <<" was already saved."<<std::endl;*/
360 std::string playername = player->getName();
361 // Don't save unnamed player
364 //infostream<<"Not saving unnamed player."<<std::endl;
370 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
371 playername = "player";
372 std::string path = players_path + "/" + playername;
374 for(u32 i=0; i<1000; i++)
376 if(fs::PathExists(path) == false)
381 path = players_path + "/" + playername + itos(i);
385 infostream<<"Didn't find free file for player"<<std::endl;
390 /*infostream<<"Saving player "<<player->getName()<<" to "
392 // Open file and serialize
393 std::ofstream os(path.c_str(), std::ios_base::binary);
394 if(os.good() == false)
396 infostream<<"Failed to overwrite "<<path<<std::endl;
399 player->serialize(os);
400 saved_players.insert(player, true);
404 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
407 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
409 std::string players_path = savedir + "/players";
411 core::map<Player*, bool> saved_players;
413 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
414 for(u32 i=0; i<player_files.size(); i++)
416 if(player_files[i].dir)
419 // Full path to this file
420 std::string path = players_path + "/" + player_files[i].name;
422 infostream<<"Checking player file "<<path<<std::endl;
424 // Load player to see what is its name
425 ServerRemotePlayer testplayer;
427 // Open file and deserialize
428 std::ifstream is(path.c_str(), std::ios_base::binary);
429 if(is.good() == false)
431 infostream<<"Failed to read "<<path<<std::endl;
434 testplayer.deSerialize(is);
437 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
439 infostream<<"Not loading player with invalid name: "
440 <<testplayer.getName()<<std::endl;
443 infostream<<"Loaded test player with name "<<testplayer.getName()
446 // Search for the player
447 std::string playername = testplayer.getName();
448 Player *player = getPlayer(playername.c_str());
449 bool newplayer = false;
452 infostream<<"Is a new player"<<std::endl;
453 player = new ServerRemotePlayer();
459 infostream<<"Reading player "<<testplayer.getName()<<" from "
461 // Open file and deserialize
462 std::ifstream is(path.c_str(), std::ios_base::binary);
463 if(is.good() == false)
465 infostream<<"Failed to read "<<path<<std::endl;
468 player->deSerialize(is);
476 void ServerEnvironment::saveMeta(const std::string &savedir)
478 std::string path = savedir + "/env_meta.txt";
480 // Open file and serialize
481 std::ofstream os(path.c_str(), std::ios_base::binary);
482 if(os.good() == false)
484 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
486 throw SerializationError("Couldn't save env meta");
490 args.setU64("game_time", m_game_time);
491 args.setU64("time_of_day", getTimeOfDay());
496 void ServerEnvironment::loadMeta(const std::string &savedir)
498 std::string path = savedir + "/env_meta.txt";
500 // Open file and deserialize
501 std::ifstream is(path.c_str(), std::ios_base::binary);
502 if(is.good() == false)
504 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
506 throw SerializationError("Couldn't load env meta");
514 throw SerializationError
515 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
517 std::getline(is, line);
518 std::string trimmedline = trim(line);
519 if(trimmedline == "EnvArgsEnd")
521 args.parseConfigLine(line);
525 m_game_time = args.getU64("game_time");
526 }catch(SettingNotFoundException &e){
527 // Getting this is crucial, otherwise timestamps are useless
528 throw SerializationError("Couldn't load env meta game_time");
532 m_time_of_day = args.getU64("time_of_day");
533 }catch(SettingNotFoundException &e){
534 // This is not as important
535 m_time_of_day = 9000;
540 // This is probably very useless
541 void spawnRandomObjects(MapBlock *block)
543 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
544 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
546 bool last_node_walkable = false;
547 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
550 MapNode n = block->getNodeNoEx(p);
551 if(n.getContent() == CONTENT_IGNORE)
553 if(content_features(n).liquid_type != LIQUID_NONE)
555 if(content_features(n).walkable)
557 last_node_walkable = true;
560 if(last_node_walkable)
562 // If block contains light information
563 if(content_features(n).param_type == CPT_LIGHT)
565 if(n.getLight(LIGHTBANK_DAY) <= 5)
567 if(myrand() % 1000 == 0)
569 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
571 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
572 std::string data = obj->getStaticData();
573 StaticObject s_obj(obj->getType(),
574 obj->getBasePosition(), data);
576 block->m_static_objects.insert(0, s_obj);
578 block->setChangedFlag();
583 last_node_walkable = false;
589 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
591 // Get time difference
593 u32 stamp = block->getTimestamp();
594 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
595 dtime_s = m_game_time - block->getTimestamp();
596 dtime_s += additional_dtime;
598 // Set current time as timestamp (and let it set ChangedFlag)
599 block->setTimestamp(m_game_time);
601 //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
603 // Activate stored objects
604 activateObjects(block);
607 bool changed = block->m_node_metadata.step((float)dtime_s);
611 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
612 event.p = block->getPos();
613 m_map->dispatchEvent(&event);
615 block->setChangedFlag();
618 // TODO: Do something
619 // TODO: Implement usage of ActiveBlockModifier
621 // Here's a quick demonstration
623 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
624 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
625 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
627 v3s16 p = p0 + block->getPosRelative();
628 MapNode n = block->getNodeNoEx(p0);
631 // Convert all mud under proper day lighting to grass
632 if(n.getContent() == CONTENT_MUD)
636 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
637 if(content_features(n_top).air_equivalent &&
638 n_top.getLight(LIGHTBANK_DAY) >= 13)
640 n.setContent(CONTENT_GRASS);
641 m_map->addNodeWithEvent(p, n);
649 static void getMob_dungeon_master(Settings &properties)
651 properties.set("looks", "dungeon_master");
652 properties.setFloat("yaw", 1.57);
653 properties.setFloat("hp", 30);
654 properties.setBool("bright_shooting", true);
655 properties.set("shoot_type", "fireball");
656 properties.set("shoot_y", "0.7");
657 properties.set("player_hit_damage", "1");
658 properties.set("player_hit_distance", "1.0");
659 properties.set("player_hit_interval", "0.5");
662 void ServerEnvironment::step(float dtime)
664 DSTACK(__FUNCTION_NAME);
666 //TimeTaker timer("ServerEnv step");
669 bool footprints = g_settings->getBool("footprints");
675 m_game_time_fraction_counter += dtime;
676 u32 inc_i = (u32)m_game_time_fraction_counter;
677 m_game_time += inc_i;
678 m_game_time_fraction_counter -= (float)inc_i;
684 for(core::list<Player*>::Iterator i = m_players.begin();
685 i != m_players.end(); i++)
689 // Ignore disconnected players
690 if(player->peer_id == 0)
693 v3f playerpos = player->getPosition();
696 player->move(dtime, *m_map, 100*BS);
699 Add footsteps to grass
703 // Get node that is at BS/4 under player
704 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
706 MapNode n = m_map->getNode(bottompos);
707 if(n.getContent() == CONTENT_GRASS)
709 n.setContent(CONTENT_GRASS_FOOTSTEPS);
710 m_map->setNode(bottompos, n);
713 catch(InvalidPositionException &e)
720 Manage active block list
722 if(m_active_blocks_management_interval.step(dtime, 2.0))
725 Get player block positions
727 core::list<v3s16> players_blockpos;
728 for(core::list<Player*>::Iterator
729 i = m_players.begin();
730 i != m_players.end(); i++)
733 // Ignore disconnected players
734 if(player->peer_id == 0)
736 v3s16 blockpos = getNodeBlockPos(
737 floatToInt(player->getPosition(), BS));
738 players_blockpos.push_back(blockpos);
742 Update list of active blocks, collecting changes
744 const s16 active_block_range = g_settings->getS16("active_block_range");
745 core::map<v3s16, bool> blocks_removed;
746 core::map<v3s16, bool> blocks_added;
747 m_active_blocks.update(players_blockpos, active_block_range,
748 blocks_removed, blocks_added);
751 Handle removed blocks
754 // Convert active objects that are no more in active blocks to static
755 deactivateFarObjects(false);
757 for(core::map<v3s16, bool>::Iterator
758 i = blocks_removed.getIterator();
759 i.atEnd()==false; i++)
761 v3s16 p = i.getNode()->getKey();
763 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
764 <<") became inactive"<<std::endl;*/
766 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
770 // Set current time as timestamp (and let it set ChangedFlag)
771 block->setTimestamp(m_game_time);
778 for(core::map<v3s16, bool>::Iterator
779 i = blocks_added.getIterator();
780 i.atEnd()==false; i++)
782 v3s16 p = i.getNode()->getKey();
784 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
785 <<") became active"<<std::endl;*/
787 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
791 activateBlock(block);
796 Mess around in active blocks
798 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
802 for(core::map<v3s16, bool>::Iterator
803 i = m_active_blocks.m_list.getIterator();
804 i.atEnd()==false; i++)
806 v3s16 p = i.getNode()->getKey();
808 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
809 <<") being handled"<<std::endl;*/
811 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
815 // Reset block usage timer
816 block->resetUsageTimer();
818 // Set current time as timestamp
819 block->setTimestampNoChangedFlag(m_game_time);
822 bool changed = block->m_node_metadata.step(dtime);
826 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
828 m_map->dispatchEvent(&event);
830 block->setChangedFlag();
834 if(m_active_blocks_test_interval.step(dtime, 10.0))
836 //float dtime = 10.0;
838 for(core::map<v3s16, bool>::Iterator
839 i = m_active_blocks.m_list.getIterator();
840 i.atEnd()==false; i++)
842 v3s16 p = i.getNode()->getKey();
844 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
845 <<") being handled"<<std::endl;*/
847 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
851 // Set current time as timestamp
852 block->setTimestampNoChangedFlag(m_game_time);
857 Note that map modifications should be done using the event-
858 making map methods so that the server gets information
861 Reading can be done quickly directly from the block.
863 Everything should bind to inside this single content
864 searching loop to keep things fast.
866 // TODO: Implement usage of ActiveBlockModifier
868 // Find out how many objects the block contains
869 u32 active_object_count = block->m_static_objects.m_active.size();
870 // Find out how many objects this and all the neighbors contain
871 u32 active_object_count_wider = 0;
872 for(s16 x=-1; x<=1; x++)
873 for(s16 y=-1; y<=1; y++)
874 for(s16 z=-1; z<=1; z++)
876 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
879 active_object_count_wider +=
880 block->m_static_objects.m_active.size();
884 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
885 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
886 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
888 v3s16 p = p0 + block->getPosRelative();
889 MapNode n = block->getNodeNoEx(p0);
893 Convert mud under proper lighting to grass
895 if(n.getContent() == CONTENT_MUD)
899 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
900 if(content_features(n_top).air_equivalent &&
901 n_top.getLightBlend(getDayNightRatio()) >= 13)
903 n.setContent(CONTENT_GRASS);
904 m_map->addNodeWithEvent(p, n);
909 Convert grass into mud if under something else than air
911 if(n.getContent() == CONTENT_GRASS)
913 //if(myrand()%20 == 0)
915 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
916 if(content_features(n_top).air_equivalent == false)
918 n.setContent(CONTENT_MUD);
919 m_map->addNodeWithEvent(p, n);
924 Rats spawn around regular trees
926 if(n.getContent() == CONTENT_TREE ||
927 n.getContent() == CONTENT_JUNGLETREE)
929 if(myrand()%200 == 0 && active_object_count_wider == 0)
931 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
932 0, myrand_range(-2, 2));
933 MapNode n1 = m_map->getNodeNoEx(p1);
934 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
935 if(n1b.getContent() == CONTENT_GRASS &&
936 n1.getContent() == CONTENT_AIR)
938 v3f pos = intToFloat(p1, BS);
939 ServerActiveObject *obj = new RatSAO(this, 0, pos);
940 addActiveObject(obj);
945 Fun things spawn in caves and dungeons
947 if(n.getContent() == CONTENT_STONE ||
948 n.getContent() == CONTENT_MOSSYCOBBLE)
950 if(myrand()%200 == 0 && active_object_count_wider == 0)
952 v3s16 p1 = p + v3s16(0,1,0);
953 MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
954 if(n1a.getLightBlend(getDayNightRatio()) <= 3){
955 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
956 if(n1a.getContent() == CONTENT_AIR &&
957 n1b.getContent() == CONTENT_AIR)
959 v3f pos = intToFloat(p1, BS);
961 if(i == 0 || i == 1){
962 actionstream<<"A dungeon master spawns at "
965 getMob_dungeon_master(properties);
966 ServerActiveObject *obj = new MobV2SAO(
967 this, 0, pos, &properties);
968 addActiveObject(obj);
969 } else if(i == 2 || i == 3){
970 actionstream<<"Rats spawn at "
972 for(int j=0; j<3; j++){
973 ServerActiveObject *obj = new RatSAO(
975 addActiveObject(obj);
978 actionstream<<"An oerkki spawns at "
980 ServerActiveObject *obj = new Oerkki1SAO(
982 addActiveObject(obj);
989 Make trees from saplings!
991 if(n.getContent() == CONTENT_SAPLING)
995 actionstream<<"A sapling grows into a tree at "
998 core::map<v3s16, MapBlock*> modified_blocks;
1000 ManualMapVoxelManipulator vmanip(m_map);
1001 v3s16 tree_blockp = getNodeBlockPos(tree_p);
1002 vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
1003 bool is_apple_tree = myrand()%4 == 0;
1004 mapgen::make_tree(vmanip, tree_p, is_apple_tree);
1005 vmanip.blitBackAll(&modified_blocks);
1008 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1009 for(core::map<v3s16, MapBlock*>::Iterator
1010 i = modified_blocks.getIterator();
1011 i.atEnd() == false; i++)
1013 lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
1015 m_map->updateLighting(lighting_modified_blocks, modified_blocks);
1017 // Send a MEET_OTHER event
1019 event.type = MEET_OTHER;
1020 for(core::map<v3s16, MapBlock*>::Iterator
1021 i = modified_blocks.getIterator();
1022 i.atEnd() == false; i++)
1024 v3s16 p = i.getNode()->getKey();
1025 event.modified_blocks.insert(p, true);
1027 m_map->dispatchEvent(&event);
1038 //TimeTaker timer("Step active objects");
1040 // This helps the objects to send data at the same time
1041 bool send_recommended = false;
1042 m_send_recommended_timer += dtime;
1043 if(m_send_recommended_timer > 0.10)
1045 m_send_recommended_timer = 0;
1046 send_recommended = true;
1049 for(core::map<u16, ServerActiveObject*>::Iterator
1050 i = m_active_objects.getIterator();
1051 i.atEnd()==false; i++)
1053 ServerActiveObject* obj = i.getNode()->getValue();
1054 // Remove non-peaceful mobs on peaceful mode
1055 if(g_settings->getBool("only_peaceful_mobs")){
1056 if(!obj->isPeaceful())
1057 obj->m_removed = true;
1059 // Don't step if is to be removed or stored statically
1060 if(obj->m_removed || obj->m_pending_deactivation)
1063 obj->step(dtime, send_recommended);
1064 // Read messages from object
1065 while(obj->m_messages_out.size() > 0)
1067 m_active_object_messages.push_back(
1068 obj->m_messages_out.pop_front());
1074 Manage active objects
1076 if(m_object_management_interval.step(dtime, 0.5))
1079 Remove objects that satisfy (m_removed && m_known_by_count==0)
1081 removeRemovedObjects();
1084 if(g_settings->getBool("enable_experimental"))
1091 m_random_spawn_timer -= dtime;
1092 if(m_random_spawn_timer < 0)
1094 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1095 //m_random_spawn_timer += 2.0;
1096 m_random_spawn_timer += 200.0;
1102 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1103 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1104 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1106 Player *player = getRandomConnectedPlayer();
1109 pos = player->getPosition();
1111 myrand_range(-3,3)*BS,
1113 myrand_range(-3,3)*BS
1117 Create a ServerActiveObject
1120 //TestSAO *obj = new TestSAO(this, 0, pos);
1121 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1122 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1123 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1124 //ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1126 infostream<<"Server: Spawning MobV2SAO at "
1127 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
1129 Settings properties;
1130 getMob_dungeon_master(properties);
1131 ServerActiveObject *obj = new MobV2SAO(this, 0, pos, &properties);
1132 addActiveObject(obj);
1136 } // enable_experimental
1139 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1141 core::map<u16, ServerActiveObject*>::Node *n;
1142 n = m_active_objects.find(id);
1145 return n->getValue();
1148 bool isFreeServerActiveObjectId(u16 id,
1149 core::map<u16, ServerActiveObject*> &objects)
1154 for(core::map<u16, ServerActiveObject*>::Iterator
1155 i = objects.getIterator();
1156 i.atEnd()==false; i++)
1158 if(i.getNode()->getKey() == id)
1164 u16 getFreeServerActiveObjectId(
1165 core::map<u16, ServerActiveObject*> &objects)
1170 if(isFreeServerActiveObjectId(new_id, objects))
1180 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1183 u16 id = addActiveObjectRaw(object, true);
1187 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1191 v3f objectpos = obj->getBasePosition();
1193 // The block in which the object resides in
1194 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1197 Update the static data
1200 // Create new static object
1201 std::string staticdata = obj->getStaticData();
1202 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1203 // Add to the block where the object is located in
1204 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1205 // Get or generate the block
1206 MapBlock *block = m_map->emergeBlock(blockpos);
1208 bool succeeded = false;
1212 block->m_static_objects.insert(0, s_obj);
1213 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
1217 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1218 <<"Could not find or generate "
1219 <<"a block for storing static object"<<std::endl;
1229 Finds out what new objects have been added to
1230 inside a radius around a position
1232 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1233 core::map<u16, bool> ¤t_objects,
1234 core::map<u16, bool> &added_objects)
1236 v3f pos_f = intToFloat(pos, BS);
1237 f32 radius_f = radius * BS;
1239 Go through the object list,
1240 - discard m_removed objects,
1241 - discard objects that are too far away,
1242 - discard objects that are found in current_objects.
1243 - add remaining objects to added_objects
1245 for(core::map<u16, ServerActiveObject*>::Iterator
1246 i = m_active_objects.getIterator();
1247 i.atEnd()==false; i++)
1249 u16 id = i.getNode()->getKey();
1251 ServerActiveObject *object = i.getNode()->getValue();
1254 // Discard if removed
1255 if(object->m_removed)
1257 // Discard if too far
1258 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1259 if(distance_f > radius_f)
1261 // Discard if already on current_objects
1262 core::map<u16, bool>::Node *n;
1263 n = current_objects.find(id);
1266 // Add to added_objects
1267 added_objects.insert(id, false);
1272 Finds out what objects have been removed from
1273 inside a radius around a position
1275 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1276 core::map<u16, bool> ¤t_objects,
1277 core::map<u16, bool> &removed_objects)
1279 v3f pos_f = intToFloat(pos, BS);
1280 f32 radius_f = radius * BS;
1282 Go through current_objects; object is removed if:
1283 - object is not found in m_active_objects (this is actually an
1284 error condition; objects should be set m_removed=true and removed
1285 only after all clients have been informed about removal), or
1286 - object has m_removed=true, or
1287 - object is too far away
1289 for(core::map<u16, bool>::Iterator
1290 i = current_objects.getIterator();
1291 i.atEnd()==false; i++)
1293 u16 id = i.getNode()->getKey();
1294 ServerActiveObject *object = getActiveObject(id);
1297 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1298 <<" object in current_objects is NULL"<<std::endl;
1300 else if(object->m_removed == false)
1302 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1303 /*infostream<<"removed == false"
1304 <<"distance_f = "<<distance_f
1305 <<", radius_f = "<<radius_f<<std::endl;*/
1306 if(distance_f < radius_f)
1312 removed_objects.insert(id, false);
1316 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1318 if(m_active_object_messages.size() == 0)
1319 return ActiveObjectMessage(0);
1321 return m_active_object_messages.pop_front();
1325 ************ Private methods *************
1328 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1332 if(object->getId() == 0)
1334 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1337 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1338 <<"no free ids available"<<std::endl;
1342 object->setId(new_id);
1344 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1346 infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1347 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1351 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1352 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1354 m_active_objects.insert(object->getId(), object);
1356 // Add static object to active static list of the block
1357 v3f objectpos = object->getBasePosition();
1358 std::string staticdata = object->getStaticData();
1359 StaticObject s_obj(object->getType(), objectpos, staticdata);
1360 // Add to the block where the object is located in
1361 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1362 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1365 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1366 object->m_static_exists = true;
1367 object->m_static_block = blockpos;
1370 block->setChangedFlag();
1373 infostream<<"ServerEnv: Could not find a block for "
1374 <<"storing newly added static active object"<<std::endl;
1377 return object->getId();
1381 Remove objects that satisfy (m_removed && m_known_by_count==0)
1383 void ServerEnvironment::removeRemovedObjects()
1385 core::list<u16> objects_to_remove;
1386 for(core::map<u16, ServerActiveObject*>::Iterator
1387 i = m_active_objects.getIterator();
1388 i.atEnd()==false; i++)
1390 u16 id = i.getNode()->getKey();
1391 ServerActiveObject* obj = i.getNode()->getValue();
1392 // This shouldn't happen but check it
1395 infostream<<"NULL object found in ServerEnvironment"
1396 <<" while finding removed objects. id="<<id<<std::endl;
1397 // Id to be removed from m_active_objects
1398 objects_to_remove.push_back(id);
1403 We will delete objects that are marked as removed or thatare
1404 waiting for deletion after deactivation
1406 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1410 Delete static data from block if is marked as removed
1412 if(obj->m_static_exists && obj->m_removed)
1414 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1417 block->m_static_objects.remove(id);
1418 block->setChangedFlag();
1422 // If m_known_by_count > 0, don't actually remove.
1423 if(obj->m_known_by_count > 0)
1428 // Id to be removed from m_active_objects
1429 objects_to_remove.push_back(id);
1431 // Remove references from m_active_objects
1432 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1433 i != objects_to_remove.end(); i++)
1435 m_active_objects.remove(*i);
1440 Convert stored objects from blocks near the players to active.
1442 void ServerEnvironment::activateObjects(MapBlock *block)
1446 // Ignore if no stored objects (to not set changed flag)
1447 if(block->m_static_objects.m_stored.size() == 0)
1449 // A list for objects that couldn't be converted to static for some
1450 // reason. They will be stored back.
1451 core::list<StaticObject> new_stored;
1452 // Loop through stored static objects
1453 for(core::list<StaticObject>::Iterator
1454 i = block->m_static_objects.m_stored.begin();
1455 i != block->m_static_objects.m_stored.end(); i++)
1457 /*infostream<<"Server: Creating an active object from "
1458 <<"static data"<<std::endl;*/
1459 StaticObject &s_obj = *i;
1460 // Create an active object from the data
1461 ServerActiveObject *obj = ServerActiveObject::create
1462 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1463 // If couldn't create object, store static data back.
1466 new_stored.push_back(s_obj);
1469 // This will also add the object to the active static list
1470 addActiveObjectRaw(obj, false);
1471 //u16 id = addActiveObjectRaw(obj, false);
1473 // Clear stored list
1474 block->m_static_objects.m_stored.clear();
1475 // Add leftover failed stuff to stored list
1476 for(core::list<StaticObject>::Iterator
1477 i = new_stored.begin();
1478 i != new_stored.end(); i++)
1480 StaticObject &s_obj = *i;
1481 block->m_static_objects.m_stored.push_back(s_obj);
1483 // Block has been modified
1484 // NOTE: No it has not really. Save I/O here.
1485 //block->setChangedFlag();
1489 Convert objects that are not in active blocks to static.
1491 If m_known_by_count != 0, active object is not deleted, but static
1492 data is still updated.
1494 If force_delete is set, active object is deleted nevertheless. It
1495 shall only be set so in the destructor of the environment.
1497 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1499 core::list<u16> objects_to_remove;
1500 for(core::map<u16, ServerActiveObject*>::Iterator
1501 i = m_active_objects.getIterator();
1502 i.atEnd()==false; i++)
1504 ServerActiveObject* obj = i.getNode()->getValue();
1506 // This shouldn't happen but check it
1509 infostream<<"NULL object found in ServerEnvironment"
1515 u16 id = i.getNode()->getKey();
1516 v3f objectpos = obj->getBasePosition();
1518 // The block in which the object resides in
1519 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1521 // If block is active, don't remove
1522 if(m_active_blocks.contains(blockpos_o))
1526 Update the static data
1529 // Delete old static object
1530 MapBlock *oldblock = NULL;
1531 if(obj->m_static_exists)
1533 MapBlock *block = m_map->getBlockNoCreateNoEx
1534 (obj->m_static_block);
1537 block->m_static_objects.remove(id);
1541 // Create new static object
1542 std::string staticdata = obj->getStaticData();
1543 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1544 // Add to the block where the object is located in
1545 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1546 // Get or generate the block
1547 MapBlock *block = m_map->emergeBlock(blockpos);
1549 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1552 // Block not found. Is the old block still ok?
1555 // Load from disk or generate
1557 block = m_map->emergeBlock(blockpos);
1562 block->m_static_objects.insert(0, s_obj);
1563 block->setChangedFlag();
1564 obj->m_static_exists = true;
1565 obj->m_static_block = block->getPos();
1568 infostream<<"ServerEnv: Could not find or generate "
1569 <<"a block for storing static object"<<std::endl;
1570 obj->m_static_exists = false;
1575 Delete active object if not known by some client,
1576 else set pending deactivation
1579 // If known by some client, don't delete.
1580 if(obj->m_known_by_count > 0 && force_delete == false)
1582 obj->m_pending_deactivation = true;
1586 /*infostream<<"Server: Stored static data. Deleting object."
1588 // Delete active object
1590 // Id to be removed from m_active_objects
1591 objects_to_remove.push_back(id);
1594 // Remove references from m_active_objects
1595 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1596 i != objects_to_remove.end(); i++)
1598 m_active_objects.remove(*i);
1609 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1617 ClientEnvironment::~ClientEnvironment()
1619 // delete active objects
1620 for(core::map<u16, ClientActiveObject*>::Iterator
1621 i = m_active_objects.getIterator();
1622 i.atEnd()==false; i++)
1624 delete i.getNode()->getValue();
1631 void ClientEnvironment::addPlayer(Player *player)
1633 DSTACK(__FUNCTION_NAME);
1635 It is a failure if player is local and there already is a local
1638 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1640 Environment::addPlayer(player);
1643 LocalPlayer * ClientEnvironment::getLocalPlayer()
1645 for(core::list<Player*>::Iterator i = m_players.begin();
1646 i != m_players.end(); i++)
1648 Player *player = *i;
1649 if(player->isLocal())
1650 return (LocalPlayer*)player;
1655 void ClientEnvironment::step(float dtime)
1657 DSTACK(__FUNCTION_NAME);
1659 // Get some settings
1660 bool free_move = g_settings->getBool("free_move");
1661 bool footprints = g_settings->getBool("footprints");
1664 LocalPlayer *lplayer = getLocalPlayer();
1666 // collision info queue
1667 core::list<CollisionInfo> player_collisions;
1670 Get the speed the player is going
1672 bool is_climbing = lplayer->is_climbing;
1674 f32 player_speed = 0.001; // just some small value
1675 player_speed = lplayer->getSpeed().getLength();
1678 Maximum position increment
1680 //f32 position_max_increment = 0.05*BS;
1681 f32 position_max_increment = 0.1*BS;
1683 // Maximum time increment (for collision detection etc)
1684 // time = distance / speed
1685 f32 dtime_max_increment = position_max_increment / player_speed;
1687 // Maximum time increment is 10ms or lower
1688 if(dtime_max_increment > 0.01)
1689 dtime_max_increment = 0.01;
1691 // Don't allow overly huge dtime
1695 f32 dtime_downcount = dtime;
1698 Stuff that has a maximum time increment
1707 if(dtime_downcount > dtime_max_increment)
1709 dtime_part = dtime_max_increment;
1710 dtime_downcount -= dtime_part;
1714 dtime_part = dtime_downcount;
1716 Setting this to 0 (no -=dtime_part) disables an infinite loop
1717 when dtime_part is so small that dtime_downcount -= dtime_part
1720 dtime_downcount = 0;
1728 v3f lplayerpos = lplayer->getPosition();
1731 if(free_move == false && is_climbing == false)
1734 v3f speed = lplayer->getSpeed();
1735 if(lplayer->swimming_up == false)
1736 speed.Y -= 9.81 * BS * dtime_part * 2;
1739 if(lplayer->in_water_stable || lplayer->in_water)
1741 f32 max_down = 2.0*BS;
1742 if(speed.Y < -max_down) speed.Y = -max_down;
1745 if(speed.getLength() > max)
1747 speed = speed / speed.getLength() * max;
1751 lplayer->setSpeed(speed);
1756 This also does collision detection.
1758 lplayer->move(dtime_part, *m_map, position_max_increment,
1759 &player_collisions);
1762 while(dtime_downcount > 0.001);
1764 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1766 for(core::list<CollisionInfo>::Iterator
1767 i = player_collisions.begin();
1768 i != player_collisions.end(); i++)
1770 CollisionInfo &info = *i;
1771 if(info.t == COLLISION_FALL)
1773 //f32 tolerance = BS*10; // 2 without damage
1774 f32 tolerance = BS*12; // 3 without damage
1776 if(info.speed > tolerance)
1778 f32 damage_f = (info.speed - tolerance)/BS*factor;
1779 u16 damage = (u16)(damage_f+0.5);
1780 if(lplayer->hp > damage)
1781 lplayer->hp -= damage;
1785 ClientEnvEvent event;
1786 event.type = CEE_PLAYER_DAMAGE;
1787 event.player_damage.amount = damage;
1788 m_client_event_queue.push_back(event);
1794 A quick draft of lava damage
1796 if(m_lava_hurt_interval.step(dtime, 1.0))
1798 v3f pf = lplayer->getPosition();
1800 // Feet, middle and head
1801 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1802 MapNode n1 = m_map->getNodeNoEx(p1);
1803 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1804 MapNode n2 = m_map->getNodeNoEx(p2);
1805 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1806 MapNode n3 = m_map->getNodeNoEx(p2);
1808 u32 damage_per_second = 0;
1809 damage_per_second = MYMAX(damage_per_second,
1810 content_features(n1).damage_per_second);
1811 damage_per_second = MYMAX(damage_per_second,
1812 content_features(n2).damage_per_second);
1813 damage_per_second = MYMAX(damage_per_second,
1814 content_features(n3).damage_per_second);
1816 if(damage_per_second != 0)
1818 ClientEnvEvent event;
1819 event.type = CEE_PLAYER_DAMAGE;
1820 event.player_damage.amount = damage_per_second;
1821 m_client_event_queue.push_back(event);
1826 Stuff that can be done in an arbitarily large dtime
1828 for(core::list<Player*>::Iterator i = m_players.begin();
1829 i != m_players.end(); i++)
1831 Player *player = *i;
1832 v3f playerpos = player->getPosition();
1835 Handle non-local players
1837 if(player->isLocal() == false)
1840 player->move(dtime, *m_map, 100*BS);
1844 // Update lighting on all players on client
1845 u8 light = LIGHT_MAX;
1848 v3s16 p = player->getLightPosition();
1849 MapNode n = m_map->getNode(p);
1850 light = n.getLightBlend(getDayNightRatio());
1852 catch(InvalidPositionException &e) {}
1853 player->updateLight(light);
1856 Add footsteps to grass
1860 // Get node that is at BS/4 under player
1861 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1863 MapNode n = m_map->getNode(bottompos);
1864 if(n.getContent() == CONTENT_GRASS)
1866 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1867 m_map->setNode(bottompos, n);
1868 // Update mesh on client
1869 if(m_map->mapType() == MAPTYPE_CLIENT)
1871 v3s16 p_blocks = getNodeBlockPos(bottompos);
1872 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1873 //b->updateMesh(getDayNightRatio());
1874 b->setMeshExpired(true);
1878 catch(InvalidPositionException &e)
1885 Step active objects and update lighting of them
1888 for(core::map<u16, ClientActiveObject*>::Iterator
1889 i = m_active_objects.getIterator();
1890 i.atEnd()==false; i++)
1892 ClientActiveObject* obj = i.getNode()->getValue();
1894 obj->step(dtime, this);
1896 if(m_active_object_light_update_interval.step(dtime, 0.21))
1899 //u8 light = LIGHT_MAX;
1903 v3s16 p = obj->getLightPosition();
1904 MapNode n = m_map->getNode(p);
1905 light = n.getLightBlend(getDayNightRatio());
1907 catch(InvalidPositionException &e) {}
1908 obj->updateLight(light);
1913 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1915 m_map->updateMeshes(blockpos, getDayNightRatio());
1918 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1920 m_map->expireMeshes(only_daynight_diffed);
1923 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1925 core::map<u16, ClientActiveObject*>::Node *n;
1926 n = m_active_objects.find(id);
1929 return n->getValue();
1932 bool isFreeClientActiveObjectId(u16 id,
1933 core::map<u16, ClientActiveObject*> &objects)
1938 for(core::map<u16, ClientActiveObject*>::Iterator
1939 i = objects.getIterator();
1940 i.atEnd()==false; i++)
1942 if(i.getNode()->getKey() == id)
1948 u16 getFreeClientActiveObjectId(
1949 core::map<u16, ClientActiveObject*> &objects)
1954 if(isFreeClientActiveObjectId(new_id, objects))
1964 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1967 if(object->getId() == 0)
1969 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1972 infostream<<"ClientEnvironment::addActiveObject(): "
1973 <<"no free ids available"<<std::endl;
1977 object->setId(new_id);
1979 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1981 infostream<<"ClientEnvironment::addActiveObject(): "
1982 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1986 infostream<<"ClientEnvironment::addActiveObject(): "
1987 <<"added (id="<<object->getId()<<")"<<std::endl;
1988 m_active_objects.insert(object->getId(), object);
1989 object->addToScene(m_smgr);
1990 return object->getId();
1993 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1994 const std::string &init_data)
1996 ClientActiveObject* obj = ClientActiveObject::create(type);
1999 infostream<<"ClientEnvironment::addActiveObject(): "
2000 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2007 obj->initialize(init_data);
2009 addActiveObject(obj);
2012 void ClientEnvironment::removeActiveObject(u16 id)
2014 infostream<<"ClientEnvironment::removeActiveObject(): "
2015 <<"id="<<id<<std::endl;
2016 ClientActiveObject* obj = getActiveObject(id);
2019 infostream<<"ClientEnvironment::removeActiveObject(): "
2020 <<"id="<<id<<" not found"<<std::endl;
2023 obj->removeFromScene();
2025 m_active_objects.remove(id);
2028 void ClientEnvironment::processActiveObjectMessage(u16 id,
2029 const std::string &data)
2031 ClientActiveObject* obj = getActiveObject(id);
2034 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2035 <<" got message for id="<<id<<", which doesn't exist."
2039 obj->processMessage(data);
2043 Callbacks for activeobjects
2046 void ClientEnvironment::damageLocalPlayer(u8 damage)
2048 LocalPlayer *lplayer = getLocalPlayer();
2051 if(lplayer->hp > damage)
2052 lplayer->hp -= damage;
2056 ClientEnvEvent event;
2057 event.type = CEE_PLAYER_DAMAGE;
2058 event.player_damage.amount = damage;
2059 m_client_event_queue.push_back(event);
2063 Client likes to call these
2066 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2067 core::array<DistanceSortedActiveObject> &dest)
2069 for(core::map<u16, ClientActiveObject*>::Iterator
2070 i = m_active_objects.getIterator();
2071 i.atEnd()==false; i++)
2073 ClientActiveObject* obj = i.getNode()->getValue();
2075 f32 d = (obj->getPosition() - origin).getLength();
2080 DistanceSortedActiveObject dso(obj, d);
2082 dest.push_back(dso);
2086 ClientEnvEvent ClientEnvironment::getClientEvent()
2088 if(m_client_event_queue.size() == 0)
2090 ClientEnvEvent event;
2091 event.type = CEE_NONE;
2094 return m_client_event_queue.pop_front();
2097 #endif // #ifndef SERVER