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;
1675 Check if the player is frozen (don't apply physics)
1677 bool is_frozen = lplayer->is_frozen;
1679 f32 player_speed = 0.001; // just some small value
1680 player_speed = lplayer->getSpeed().getLength();
1683 Maximum position increment
1685 //f32 position_max_increment = 0.05*BS;
1686 f32 position_max_increment = 0.1*BS;
1688 // Maximum time increment (for collision detection etc)
1689 // time = distance / speed
1690 f32 dtime_max_increment = position_max_increment / player_speed;
1692 // Maximum time increment is 10ms or lower
1693 if(dtime_max_increment > 0.01)
1694 dtime_max_increment = 0.01;
1696 // Don't allow overly huge dtime
1700 f32 dtime_downcount = dtime;
1703 Stuff that has a maximum time increment
1712 if(dtime_downcount > dtime_max_increment)
1714 dtime_part = dtime_max_increment;
1715 dtime_downcount -= dtime_part;
1719 dtime_part = dtime_downcount;
1721 Setting this to 0 (no -=dtime_part) disables an infinite loop
1722 when dtime_part is so small that dtime_downcount -= dtime_part
1725 dtime_downcount = 0;
1733 v3f lplayerpos = lplayer->getPosition();
1736 if(free_move == false && is_climbing == false && is_frozen == false)
1739 v3f speed = lplayer->getSpeed();
1740 if(lplayer->swimming_up == false)
1741 speed.Y -= 9.81 * BS * dtime_part * 2;
1744 if(lplayer->in_water_stable || lplayer->in_water)
1746 f32 max_down = 2.0*BS;
1747 if(speed.Y < -max_down) speed.Y = -max_down;
1750 if(speed.getLength() > max)
1752 speed = speed / speed.getLength() * max;
1756 lplayer->setSpeed(speed);
1761 This also does collision detection.
1763 lplayer->move(dtime_part, *m_map, position_max_increment,
1764 &player_collisions);
1767 while(dtime_downcount > 0.001);
1769 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1771 for(core::list<CollisionInfo>::Iterator
1772 i = player_collisions.begin();
1773 i != player_collisions.end(); i++)
1775 CollisionInfo &info = *i;
1776 if(info.t == COLLISION_FALL)
1778 //f32 tolerance = BS*10; // 2 without damage
1779 f32 tolerance = BS*12; // 3 without damage
1781 if(info.speed > tolerance)
1783 f32 damage_f = (info.speed - tolerance)/BS*factor;
1784 u16 damage = (u16)(damage_f+0.5);
1785 if(lplayer->hp > damage)
1786 lplayer->hp -= damage;
1790 ClientEnvEvent event;
1791 event.type = CEE_PLAYER_DAMAGE;
1792 event.player_damage.amount = damage;
1793 m_client_event_queue.push_back(event);
1799 A quick draft of lava damage
1801 if(m_lava_hurt_interval.step(dtime, 1.0))
1803 v3f pf = lplayer->getPosition();
1805 // Feet, middle and head
1806 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1807 MapNode n1 = m_map->getNodeNoEx(p1);
1808 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1809 MapNode n2 = m_map->getNodeNoEx(p2);
1810 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1811 MapNode n3 = m_map->getNodeNoEx(p2);
1813 u32 damage_per_second = 0;
1814 damage_per_second = MYMAX(damage_per_second,
1815 content_features(n1).damage_per_second);
1816 damage_per_second = MYMAX(damage_per_second,
1817 content_features(n2).damage_per_second);
1818 damage_per_second = MYMAX(damage_per_second,
1819 content_features(n3).damage_per_second);
1821 if(damage_per_second != 0)
1823 ClientEnvEvent event;
1824 event.type = CEE_PLAYER_DAMAGE;
1825 event.player_damage.amount = damage_per_second;
1826 m_client_event_queue.push_back(event);
1831 Stuff that can be done in an arbitarily large dtime
1833 for(core::list<Player*>::Iterator i = m_players.begin();
1834 i != m_players.end(); i++)
1836 Player *player = *i;
1837 v3f playerpos = player->getPosition();
1840 Handle non-local players
1842 if(player->isLocal() == false)
1845 player->move(dtime, *m_map, 100*BS);
1849 // Update lighting on all players on client
1850 u8 light = LIGHT_MAX;
1853 v3s16 p = player->getLightPosition();
1854 MapNode n = m_map->getNode(p);
1855 light = n.getLightBlend(getDayNightRatio());
1857 catch(InvalidPositionException &e) {}
1858 player->updateLight(light);
1861 Add footsteps to grass
1865 // Get node that is at BS/4 under player
1866 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1868 MapNode n = m_map->getNode(bottompos);
1869 if(n.getContent() == CONTENT_GRASS)
1871 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1872 m_map->setNode(bottompos, n);
1873 // Update mesh on client
1874 if(m_map->mapType() == MAPTYPE_CLIENT)
1876 v3s16 p_blocks = getNodeBlockPos(bottompos);
1877 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1878 //b->updateMesh(getDayNightRatio());
1879 b->setMeshExpired(true);
1883 catch(InvalidPositionException &e)
1890 Step active objects and update lighting of them
1893 for(core::map<u16, ClientActiveObject*>::Iterator
1894 i = m_active_objects.getIterator();
1895 i.atEnd()==false; i++)
1897 ClientActiveObject* obj = i.getNode()->getValue();
1899 obj->step(dtime, this);
1901 if(m_active_object_light_update_interval.step(dtime, 0.21))
1904 //u8 light = LIGHT_MAX;
1908 v3s16 p = obj->getLightPosition();
1909 MapNode n = m_map->getNode(p);
1910 light = n.getLightBlend(getDayNightRatio());
1912 catch(InvalidPositionException &e) {}
1913 obj->updateLight(light);
1918 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1920 m_map->updateMeshes(blockpos, getDayNightRatio());
1923 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1925 m_map->expireMeshes(only_daynight_diffed);
1928 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1930 core::map<u16, ClientActiveObject*>::Node *n;
1931 n = m_active_objects.find(id);
1934 return n->getValue();
1937 bool isFreeClientActiveObjectId(u16 id,
1938 core::map<u16, ClientActiveObject*> &objects)
1943 for(core::map<u16, ClientActiveObject*>::Iterator
1944 i = objects.getIterator();
1945 i.atEnd()==false; i++)
1947 if(i.getNode()->getKey() == id)
1953 u16 getFreeClientActiveObjectId(
1954 core::map<u16, ClientActiveObject*> &objects)
1959 if(isFreeClientActiveObjectId(new_id, objects))
1969 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1972 if(object->getId() == 0)
1974 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1977 infostream<<"ClientEnvironment::addActiveObject(): "
1978 <<"no free ids available"<<std::endl;
1982 object->setId(new_id);
1984 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1986 infostream<<"ClientEnvironment::addActiveObject(): "
1987 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1991 infostream<<"ClientEnvironment::addActiveObject(): "
1992 <<"added (id="<<object->getId()<<")"<<std::endl;
1993 m_active_objects.insert(object->getId(), object);
1994 object->addToScene(m_smgr);
1995 return object->getId();
1998 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1999 const std::string &init_data)
2001 ClientActiveObject* obj = ClientActiveObject::create(type);
2004 infostream<<"ClientEnvironment::addActiveObject(): "
2005 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2012 obj->initialize(init_data);
2014 addActiveObject(obj);
2017 void ClientEnvironment::removeActiveObject(u16 id)
2019 infostream<<"ClientEnvironment::removeActiveObject(): "
2020 <<"id="<<id<<std::endl;
2021 ClientActiveObject* obj = getActiveObject(id);
2024 infostream<<"ClientEnvironment::removeActiveObject(): "
2025 <<"id="<<id<<" not found"<<std::endl;
2028 obj->removeFromScene();
2030 m_active_objects.remove(id);
2033 void ClientEnvironment::processActiveObjectMessage(u16 id,
2034 const std::string &data)
2036 ClientActiveObject* obj = getActiveObject(id);
2039 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2040 <<" got message for id="<<id<<", which doesn't exist."
2044 obj->processMessage(data);
2048 Callbacks for activeobjects
2051 void ClientEnvironment::damageLocalPlayer(u8 damage)
2053 LocalPlayer *lplayer = getLocalPlayer();
2056 if(lplayer->hp > damage)
2057 lplayer->hp -= damage;
2061 ClientEnvEvent event;
2062 event.type = CEE_PLAYER_DAMAGE;
2063 event.player_damage.amount = damage;
2064 m_client_event_queue.push_back(event);
2068 Client likes to call these
2071 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2072 core::array<DistanceSortedActiveObject> &dest)
2074 for(core::map<u16, ClientActiveObject*>::Iterator
2075 i = m_active_objects.getIterator();
2076 i.atEnd()==false; i++)
2078 ClientActiveObject* obj = i.getNode()->getValue();
2080 f32 d = (obj->getPosition() - origin).getLength();
2085 DistanceSortedActiveObject dso(obj, d);
2087 dest.push_back(dso);
2091 ClientEnvEvent ClientEnvironment::getClientEvent()
2093 if(m_client_event_queue.size() == 0)
2095 ClientEnvEvent event;
2096 event.type = CEE_NONE;
2099 return m_client_event_queue.pop_front();
2102 #endif // #ifndef SERVER