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"
27 Environment::Environment():
32 Environment::~Environment()
35 for(core::list<Player*>::Iterator i = m_players.begin();
36 i != m_players.end(); i++)
42 void Environment::addPlayer(Player *player)
44 DSTACK(__FUNCTION_NAME);
46 Check that peer_ids are unique.
47 Also check that names are unique.
48 Exception: there can be multiple players with peer_id=0
50 // If peer id is non-zero, it has to be unique.
51 if(player->peer_id != 0)
52 assert(getPlayer(player->peer_id) == NULL);
53 // Name has to be unique.
54 assert(getPlayer(player->getName()) == NULL);
56 m_players.push_back(player);
59 void Environment::removePlayer(u16 peer_id)
61 DSTACK(__FUNCTION_NAME);
63 for(core::list<Player*>::Iterator i = m_players.begin();
64 i != m_players.end(); i++)
67 if(player->peer_id != peer_id)
72 // See if there is an another one
73 // (shouldn't be, but just to be sure)
78 Player * Environment::getPlayer(u16 peer_id)
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id == peer_id)
90 Player * Environment::getPlayer(const char *name)
92 for(core::list<Player*>::Iterator i = m_players.begin();
93 i != m_players.end(); i++)
96 if(strcmp(player->getName(), name) == 0)
102 Player * Environment::getRandomConnectedPlayer()
104 core::list<Player*> connected_players = getPlayers(true);
105 u32 chosen_one = myrand() % connected_players.size();
107 for(core::list<Player*>::Iterator
108 i = connected_players.begin();
109 i != connected_players.end(); i++)
121 Player * Environment::getNearestConnectedPlayer(v3f pos)
123 core::list<Player*> connected_players = getPlayers(true);
125 Player *nearest_player = NULL;
126 for(core::list<Player*>::Iterator
127 i = connected_players.begin();
128 i != connected_players.end(); i++)
131 f32 d = player->getPosition().getDistanceFrom(pos);
132 if(d < nearest_d || nearest_player == NULL)
135 nearest_player = player;
138 return nearest_player;
141 core::list<Player*> Environment::getPlayers()
146 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
148 core::list<Player*> newlist;
149 for(core::list<Player*>::Iterator
150 i = m_players.begin();
151 i != m_players.end(); i++)
155 if(ignore_disconnected)
157 // Ignore disconnected players
158 if(player->peer_id == 0)
162 newlist.push_back(player);
167 void Environment::printPlayers(std::ostream &o)
169 o<<"Players in environment:"<<std::endl;
170 for(core::list<Player*>::Iterator i = m_players.begin();
171 i != m_players.end(); i++)
174 o<<"Player peer_id="<<player->peer_id<<std::endl;
178 /*void Environment::setDayNightRatio(u32 r)
180 getDayNightRatio() = r;
183 u32 Environment::getDayNightRatio()
185 //return getDayNightRatio();
186 return time_to_daynight_ratio(m_time_of_day);
193 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
196 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
197 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
198 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
205 void ActiveBlockList::update(core::list<v3s16> &active_positions,
207 core::map<v3s16, bool> &blocks_removed,
208 core::map<v3s16, bool> &blocks_added)
213 core::map<v3s16, bool> newlist;
214 for(core::list<v3s16>::Iterator i = active_positions.begin();
215 i != active_positions.end(); i++)
217 fillRadiusBlock(*i, radius, newlist);
221 Find out which blocks on the old list are not on the new list
223 // Go through old list
224 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
225 i.atEnd()==false; i++)
227 v3s16 p = i.getNode()->getKey();
228 // If not on new list, it's been removed
229 if(newlist.find(p) == NULL)
230 blocks_removed.insert(p, true);
234 Find out which blocks on the new list are not on the old list
236 // Go through new list
237 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
238 i.atEnd()==false; i++)
240 v3s16 p = i.getNode()->getKey();
241 // If not on old list, it's been added
242 if(m_list.find(p) == NULL)
243 blocks_added.insert(p, true);
250 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251 i.atEnd()==false; i++)
253 v3s16 p = i.getNode()->getKey();
254 m_list.insert(p, true);
262 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
265 m_random_spawn_timer(3),
266 m_send_recommended_timer(0),
268 m_game_time_fraction_counter(0)
272 ServerEnvironment::~ServerEnvironment()
274 // Clear active block list.
275 // This makes the next one delete all active objects.
276 m_active_blocks.clear();
278 // Convert all objects to static and delete the active objects
279 deactivateFarObjects(true);
285 void ServerEnvironment::serializePlayers(const std::string &savedir)
287 std::string players_path = savedir + "/players";
288 fs::CreateDir(players_path);
290 core::map<Player*, bool> saved_players;
292 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
293 for(u32 i=0; i<player_files.size(); i++)
295 if(player_files[i].dir)
298 // Full path to this file
299 std::string path = players_path + "/" + player_files[i].name;
301 //dstream<<"Checking player file "<<path<<std::endl;
303 // Load player to see what is its name
304 ServerRemotePlayer testplayer;
306 // Open file and deserialize
307 std::ifstream is(path.c_str(), std::ios_base::binary);
308 if(is.good() == false)
310 dstream<<"Failed to read "<<path<<std::endl;
313 testplayer.deSerialize(is);
316 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
318 // Search for the player
319 std::string playername = testplayer.getName();
320 Player *player = getPlayer(playername.c_str());
323 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
327 //dstream<<"Found matching player, overwriting."<<std::endl;
329 // OK, found. Save player there.
331 // Open file and serialize
332 std::ofstream os(path.c_str(), std::ios_base::binary);
333 if(os.good() == false)
335 dstream<<"Failed to overwrite "<<path<<std::endl;
338 player->serialize(os);
339 saved_players.insert(player, true);
343 for(core::list<Player*>::Iterator i = m_players.begin();
344 i != m_players.end(); i++)
347 if(saved_players.find(player) != NULL)
349 /*dstream<<"Player "<<player->getName()
350 <<" was already saved."<<std::endl;*/
353 std::string playername = player->getName();
354 // Don't save unnamed player
357 //dstream<<"Not saving unnamed player."<<std::endl;
363 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
364 playername = "player";
365 std::string path = players_path + "/" + playername;
367 for(u32 i=0; i<1000; i++)
369 if(fs::PathExists(path) == false)
374 path = players_path + "/" + playername + itos(i);
378 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
383 /*dstream<<"Saving player "<<player->getName()<<" to "
385 // Open file and serialize
386 std::ofstream os(path.c_str(), std::ios_base::binary);
387 if(os.good() == false)
389 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
392 player->serialize(os);
393 saved_players.insert(player, true);
397 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
400 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
402 std::string players_path = savedir + "/players";
404 core::map<Player*, bool> saved_players;
406 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
407 for(u32 i=0; i<player_files.size(); i++)
409 if(player_files[i].dir)
412 // Full path to this file
413 std::string path = players_path + "/" + player_files[i].name;
415 dstream<<"Checking player file "<<path<<std::endl;
417 // Load player to see what is its name
418 ServerRemotePlayer testplayer;
420 // Open file and deserialize
421 std::ifstream is(path.c_str(), std::ios_base::binary);
422 if(is.good() == false)
424 dstream<<"Failed to read "<<path<<std::endl;
427 testplayer.deSerialize(is);
430 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
432 dstream<<"Not loading player with invalid name: "
433 <<testplayer.getName()<<std::endl;
436 dstream<<"Loaded test player with name "<<testplayer.getName()
439 // Search for the player
440 std::string playername = testplayer.getName();
441 Player *player = getPlayer(playername.c_str());
442 bool newplayer = false;
445 dstream<<"Is a new player"<<std::endl;
446 player = new ServerRemotePlayer();
452 dstream<<"Reading player "<<testplayer.getName()<<" from "
454 // Open file and deserialize
455 std::ifstream is(path.c_str(), std::ios_base::binary);
456 if(is.good() == false)
458 dstream<<"Failed to read "<<path<<std::endl;
461 player->deSerialize(is);
469 void ServerEnvironment::saveMeta(const std::string &savedir)
471 std::string path = savedir + "/env_meta.txt";
473 // Open file and serialize
474 std::ofstream os(path.c_str(), std::ios_base::binary);
475 if(os.good() == false)
477 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
479 throw SerializationError("Couldn't save env meta");
483 args.setU64("game_time", m_game_time);
484 args.setU64("time_of_day", getTimeOfDay());
489 void ServerEnvironment::loadMeta(const std::string &savedir)
491 std::string path = savedir + "/env_meta.txt";
493 // Open file and deserialize
494 std::ifstream is(path.c_str(), std::ios_base::binary);
495 if(is.good() == false)
497 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
499 throw SerializationError("Couldn't load env meta");
507 throw SerializationError
508 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
510 std::getline(is, line);
511 std::string trimmedline = trim(line);
512 if(trimmedline == "EnvArgsEnd")
514 args.parseConfigLine(line);
518 m_game_time = args.getU64("game_time");
519 }catch(SettingNotFoundException &e){
520 // Getting this is crucial, otherwise timestamps are useless
521 throw SerializationError("Couldn't load env meta game_time");
525 m_time_of_day = args.getU64("time_of_day");
526 }catch(SettingNotFoundException &e){
527 // This is not as important
528 m_time_of_day = 9000;
533 // This is probably very useless
534 void spawnRandomObjects(MapBlock *block)
536 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
537 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
539 bool last_node_walkable = false;
540 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
543 MapNode n = block->getNodeNoEx(p);
544 if(n.d == CONTENT_IGNORE)
546 if(content_features(n.d).liquid_type != LIQUID_NONE)
548 if(content_features(n.d).walkable)
550 last_node_walkable = true;
553 if(last_node_walkable)
555 // If block contains light information
556 if(content_features(n.d).param_type == CPT_LIGHT)
558 if(n.getLight(LIGHTBANK_DAY) <= 5)
560 if(myrand() % 1000 == 0)
562 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
564 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
565 std::string data = obj->getStaticData();
566 StaticObject s_obj(obj->getType(),
567 obj->getBasePosition(), data);
569 block->m_static_objects.insert(0, s_obj);
571 block->setChangedFlag();
576 last_node_walkable = false;
582 void ServerEnvironment::step(float dtime)
584 DSTACK(__FUNCTION_NAME);
586 //TimeTaker timer("ServerEnv step");
589 bool footprints = g_settings.getBool("footprints");
595 m_game_time_fraction_counter += dtime;
596 u32 inc_i = (u32)m_game_time_fraction_counter;
597 m_game_time += inc_i;
598 m_game_time_fraction_counter -= (float)inc_i;
602 Let map update it's timers
605 //TimeTaker timer("Server m_map->timerUpdate()");
606 m_map->timerUpdate(dtime);
612 for(core::list<Player*>::Iterator i = m_players.begin();
613 i != m_players.end(); i++)
617 // Ignore disconnected players
618 if(player->peer_id == 0)
621 v3f playerpos = player->getPosition();
624 player->move(dtime, *m_map, 100*BS);
627 Add footsteps to grass
631 // Get node that is at BS/4 under player
632 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
634 MapNode n = m_map->getNode(bottompos);
635 if(n.d == CONTENT_GRASS)
637 n.d = CONTENT_GRASS_FOOTSTEPS;
638 m_map->setNode(bottompos, n);
641 catch(InvalidPositionException &e)
648 Manage active block list
650 if(m_active_blocks_management_interval.step(dtime, 2.0))
653 Get player block positions
655 core::list<v3s16> players_blockpos;
656 for(core::list<Player*>::Iterator
657 i = m_players.begin();
658 i != m_players.end(); i++)
661 // Ignore disconnected players
662 if(player->peer_id == 0)
664 v3s16 blockpos = getNodeBlockPos(
665 floatToInt(player->getPosition(), BS));
666 players_blockpos.push_back(blockpos);
670 Update list of active blocks, collecting changes
672 const s16 active_block_range = 5;
673 core::map<v3s16, bool> blocks_removed;
674 core::map<v3s16, bool> blocks_added;
675 m_active_blocks.update(players_blockpos, active_block_range,
676 blocks_removed, blocks_added);
679 Handle removed blocks
682 // Convert active objects that are no more in active blocks to static
683 deactivateFarObjects(false);
685 for(core::map<v3s16, bool>::Iterator
686 i = blocks_removed.getIterator();
687 i.atEnd()==false; i++)
689 v3s16 p = i.getNode()->getKey();
691 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
692 <<") became inactive"<<std::endl;*/
694 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
698 // Set current time as timestamp (and let it set ChangedFlag)
699 block->setTimestamp(m_game_time);
706 for(core::map<v3s16, bool>::Iterator
707 i = blocks_added.getIterator();
708 i.atEnd()==false; i++)
710 v3s16 p = i.getNode()->getKey();
712 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
713 <<") became active"<<std::endl;*/
715 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
719 // Get time difference
721 u32 stamp = block->getTimestamp();
722 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
723 dtime_s = m_game_time - block->getTimestamp();
725 // Set current time as timestamp (and let it set ChangedFlag)
726 block->setTimestamp(m_game_time);
728 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
730 // Activate stored objects
731 activateObjects(block);
734 bool changed = block->m_node_metadata.step((float)dtime_s);
738 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
740 m_map->dispatchEvent(&event);
742 block->setChangedFlag();
745 // TODO: Do something
746 // TODO: Implement usage of ActiveBlockModifier
748 // Here's a quick demonstration
750 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
751 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
752 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
754 v3s16 p = p0 + block->getPosRelative();
755 MapNode n = block->getNodeNoEx(p0);
757 // Convert all mud under proper day lighting to grass
758 if(n.d == CONTENT_MUD)
762 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
763 if(content_features(n_top.d).air_equivalent &&
764 n_top.getLight(LIGHTBANK_DAY) >= 13)
767 m_map->addNodeWithEvent(p, n);
776 Mess around in active blocks
778 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
782 for(core::map<v3s16, bool>::Iterator
783 i = m_active_blocks.m_list.getIterator();
784 i.atEnd()==false; i++)
786 v3s16 p = i.getNode()->getKey();
788 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
789 <<") being handled"<<std::endl;*/
791 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
795 // Set current time as timestamp
796 block->setTimestampNoChangedFlag(m_game_time);
799 bool changed = block->m_node_metadata.step(dtime);
803 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
805 m_map->dispatchEvent(&event);
807 block->setChangedFlag();
811 if(m_active_blocks_test_interval.step(dtime, 10.0))
813 //float dtime = 10.0;
815 for(core::map<v3s16, bool>::Iterator
816 i = m_active_blocks.m_list.getIterator();
817 i.atEnd()==false; i++)
819 v3s16 p = i.getNode()->getKey();
821 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
822 <<") being handled"<<std::endl;*/
824 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
828 // Set current time as timestamp
829 block->setTimestampNoChangedFlag(m_game_time);
834 Note that map modifications should be done using the event-
835 making map methods so that the server gets information
838 Reading can be done quickly directly from the block.
840 Everything should bind to inside this single content
841 searching loop to keep things fast.
843 // TODO: Implement usage of ActiveBlockModifier
846 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
847 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
848 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
850 v3s16 p = p0 + block->getPosRelative();
851 MapNode n = block->getNodeNoEx(p0);
855 Convert mud under proper lighting to grass
857 if(n.d == CONTENT_MUD)
861 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
862 if(content_features(n_top.d).air_equivalent &&
863 n_top.getLightBlend(getDayNightRatio()) >= 13)
866 m_map->addNodeWithEvent(p, n);
878 //TimeTaker timer("Step active objects");
880 // This helps the objects to send data at the same time
881 bool send_recommended = false;
882 m_send_recommended_timer += dtime;
883 if(m_send_recommended_timer > 0.15)
885 m_send_recommended_timer = 0;
886 send_recommended = true;
889 for(core::map<u16, ServerActiveObject*>::Iterator
890 i = m_active_objects.getIterator();
891 i.atEnd()==false; i++)
893 ServerActiveObject* obj = i.getNode()->getValue();
894 // Don't step if is to be removed or stored statically
895 if(obj->m_removed || obj->m_pending_deactivation)
897 // Step object, putting messages directly to the queue
898 obj->step(dtime, m_active_object_messages, send_recommended);
903 Manage active objects
905 if(m_object_management_interval.step(dtime, 0.5))
908 Remove objects that satisfy (m_removed && m_known_by_count==0)
910 removeRemovedObjects();
913 if(g_settings.getBool("enable_experimental"))
920 m_random_spawn_timer -= dtime;
921 if(m_random_spawn_timer < 0)
923 //m_random_spawn_timer += myrand_range(2.0, 20.0);
924 //m_random_spawn_timer += 2.0;
925 m_random_spawn_timer += 200.0;
931 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
932 s16 y = 1 + getServerMap().findGroundLevel(p2d);
933 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
935 Player *player = getRandomConnectedPlayer();
938 pos = player->getPosition();
940 myrand_range(-3,3)*BS,
942 myrand_range(-3,3)*BS
946 Create a ServerActiveObject
949 //TestSAO *obj = new TestSAO(this, 0, pos);
950 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
951 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
952 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
953 addActiveObject(obj);
957 } // enable_experimental
960 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
962 core::map<u16, ServerActiveObject*>::Node *n;
963 n = m_active_objects.find(id);
966 return n->getValue();
969 bool isFreeServerActiveObjectId(u16 id,
970 core::map<u16, ServerActiveObject*> &objects)
975 for(core::map<u16, ServerActiveObject*>::Iterator
976 i = objects.getIterator();
977 i.atEnd()==false; i++)
979 if(i.getNode()->getKey() == id)
985 u16 getFreeServerActiveObjectId(
986 core::map<u16, ServerActiveObject*> &objects)
991 if(isFreeServerActiveObjectId(new_id, objects))
1001 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1004 u16 id = addActiveObjectRaw(object, true);
1009 Finds out what new objects have been added to
1010 inside a radius around a position
1012 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1013 core::map<u16, bool> ¤t_objects,
1014 core::map<u16, bool> &added_objects)
1016 v3f pos_f = intToFloat(pos, BS);
1017 f32 radius_f = radius * BS;
1019 Go through the object list,
1020 - discard m_removed objects,
1021 - discard objects that are too far away,
1022 - discard objects that are found in current_objects.
1023 - add remaining objects to added_objects
1025 for(core::map<u16, ServerActiveObject*>::Iterator
1026 i = m_active_objects.getIterator();
1027 i.atEnd()==false; i++)
1029 u16 id = i.getNode()->getKey();
1031 ServerActiveObject *object = i.getNode()->getValue();
1034 // Discard if removed
1035 if(object->m_removed)
1037 // Discard if too far
1038 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1039 if(distance_f > radius_f)
1041 // Discard if already on current_objects
1042 core::map<u16, bool>::Node *n;
1043 n = current_objects.find(id);
1046 // Add to added_objects
1047 added_objects.insert(id, false);
1052 Finds out what objects have been removed from
1053 inside a radius around a position
1055 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1056 core::map<u16, bool> ¤t_objects,
1057 core::map<u16, bool> &removed_objects)
1059 v3f pos_f = intToFloat(pos, BS);
1060 f32 radius_f = radius * BS;
1062 Go through current_objects; object is removed if:
1063 - object is not found in m_active_objects (this is actually an
1064 error condition; objects should be set m_removed=true and removed
1065 only after all clients have been informed about removal), or
1066 - object has m_removed=true, or
1067 - object is too far away
1069 for(core::map<u16, bool>::Iterator
1070 i = current_objects.getIterator();
1071 i.atEnd()==false; i++)
1073 u16 id = i.getNode()->getKey();
1074 ServerActiveObject *object = getActiveObject(id);
1077 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1078 <<" object in current_objects is NULL"<<std::endl;
1080 else if(object->m_removed == false)
1082 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1083 /*dstream<<"removed == false"
1084 <<"distance_f = "<<distance_f
1085 <<", radius_f = "<<radius_f<<std::endl;*/
1086 if(distance_f < radius_f)
1092 removed_objects.insert(id, false);
1096 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1098 if(m_active_object_messages.size() == 0)
1099 return ActiveObjectMessage(0);
1101 return m_active_object_messages.pop_front();
1105 ************ Private methods *************
1108 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1112 if(object->getId() == 0)
1114 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1117 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1118 <<"no free ids available"<<std::endl;
1122 object->setId(new_id);
1124 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1126 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1127 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1131 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1132 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1134 m_active_objects.insert(object->getId(), object);
1136 // Add static object to active static list of the block
1137 v3f objectpos = object->getBasePosition();
1138 std::string staticdata = object->getStaticData();
1139 StaticObject s_obj(object->getType(), objectpos, staticdata);
1140 // Add to the block where the object is located in
1141 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1142 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1145 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1146 object->m_static_exists = true;
1147 object->m_static_block = blockpos;
1150 block->setChangedFlag();
1153 dstream<<"WARNING: Server: Could not find a block for "
1154 <<"storing newly added static active object"<<std::endl;
1157 return object->getId();
1161 Remove objects that satisfy (m_removed && m_known_by_count==0)
1163 void ServerEnvironment::removeRemovedObjects()
1165 core::list<u16> objects_to_remove;
1166 for(core::map<u16, ServerActiveObject*>::Iterator
1167 i = m_active_objects.getIterator();
1168 i.atEnd()==false; i++)
1170 u16 id = i.getNode()->getKey();
1171 ServerActiveObject* obj = i.getNode()->getValue();
1172 // This shouldn't happen but check it
1175 dstream<<"WARNING: NULL object found in ServerEnvironment"
1176 <<" while finding removed objects. id="<<id<<std::endl;
1177 // Id to be removed from m_active_objects
1178 objects_to_remove.push_back(id);
1183 We will delete objects that are marked as removed or thatare
1184 waiting for deletion after deactivation
1186 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1190 Delete static data from block if is marked as removed
1192 if(obj->m_static_exists && obj->m_removed)
1194 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1197 block->m_static_objects.remove(id);
1198 block->setChangedFlag();
1202 // If m_known_by_count > 0, don't actually remove.
1203 if(obj->m_known_by_count > 0)
1208 // Id to be removed from m_active_objects
1209 objects_to_remove.push_back(id);
1211 // Remove references from m_active_objects
1212 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1213 i != objects_to_remove.end(); i++)
1215 m_active_objects.remove(*i);
1220 Convert stored objects from blocks near the players to active.
1222 void ServerEnvironment::activateObjects(MapBlock *block)
1226 // Ignore if no stored objects (to not set changed flag)
1227 if(block->m_static_objects.m_stored.size() == 0)
1229 // A list for objects that couldn't be converted to static for some
1230 // reason. They will be stored back.
1231 core::list<StaticObject> new_stored;
1232 // Loop through stored static objects
1233 for(core::list<StaticObject>::Iterator
1234 i = block->m_static_objects.m_stored.begin();
1235 i != block->m_static_objects.m_stored.end(); i++)
1237 /*dstream<<"INFO: Server: Creating an active object from "
1238 <<"static data"<<std::endl;*/
1239 StaticObject &s_obj = *i;
1240 // Create an active object from the data
1241 ServerActiveObject *obj = ServerActiveObject::create
1242 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1243 // If couldn't create object, store static data back.
1246 new_stored.push_back(s_obj);
1249 // This will also add the object to the active static list
1250 addActiveObjectRaw(obj, false);
1251 //u16 id = addActiveObjectRaw(obj, false);
1253 // Clear stored list
1254 block->m_static_objects.m_stored.clear();
1255 // Add leftover failed stuff to stored list
1256 for(core::list<StaticObject>::Iterator
1257 i = new_stored.begin();
1258 i != new_stored.end(); i++)
1260 StaticObject &s_obj = *i;
1261 block->m_static_objects.m_stored.push_back(s_obj);
1263 // Block has been modified
1264 // NOTE: No it has not really. Save I/O here.
1265 //block->setChangedFlag();
1269 Convert objects that are not in active blocks to static.
1271 If m_known_by_count != 0, active object is not deleted, but static
1272 data is still updated.
1274 If force_delete is set, active object is deleted nevertheless. It
1275 shall only be set so in the destructor of the environment.
1277 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1279 core::list<u16> objects_to_remove;
1280 for(core::map<u16, ServerActiveObject*>::Iterator
1281 i = m_active_objects.getIterator();
1282 i.atEnd()==false; i++)
1284 ServerActiveObject* obj = i.getNode()->getValue();
1285 u16 id = i.getNode()->getKey();
1286 v3f objectpos = obj->getBasePosition();
1288 // This shouldn't happen but check it
1291 dstream<<"WARNING: NULL object found in ServerEnvironment"
1297 // The block in which the object resides in
1298 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1300 // If block is active, don't remove
1301 if(m_active_blocks.contains(blockpos_o))
1305 Update the static data
1308 // Delete old static object
1309 MapBlock *oldblock = NULL;
1310 if(obj->m_static_exists)
1312 MapBlock *block = m_map->getBlockNoCreateNoEx
1313 (obj->m_static_block);
1316 block->m_static_objects.remove(id);
1320 // Create new static object
1321 std::string staticdata = obj->getStaticData();
1322 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1323 // Add to the block where the object is located in
1324 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1325 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1328 block->m_static_objects.insert(0, s_obj);
1329 block->setChangedFlag();
1330 obj->m_static_exists = true;
1331 obj->m_static_block = block->getPos();
1333 // If not possible, add back to previous block
1336 oldblock->m_static_objects.insert(0, s_obj);
1337 oldblock->setChangedFlag();
1338 obj->m_static_exists = true;
1339 obj->m_static_block = oldblock->getPos();
1342 dstream<<"WARNING: Server: Could not find a block for "
1343 <<"storing static object"<<std::endl;
1344 obj->m_static_exists = false;
1349 Delete active object if not known by some client,
1350 else set pending deactivation
1353 // If known by some client, don't delete.
1354 if(obj->m_known_by_count > 0 && force_delete == false)
1356 obj->m_pending_deactivation = true;
1360 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1362 // Delete active object
1364 // Id to be removed from m_active_objects
1365 objects_to_remove.push_back(id);
1368 // Remove references from m_active_objects
1369 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1370 i != objects_to_remove.end(); i++)
1372 m_active_objects.remove(*i);
1383 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1391 ClientEnvironment::~ClientEnvironment()
1393 // delete active objects
1394 for(core::map<u16, ClientActiveObject*>::Iterator
1395 i = m_active_objects.getIterator();
1396 i.atEnd()==false; i++)
1398 delete i.getNode()->getValue();
1405 void ClientEnvironment::addPlayer(Player *player)
1407 DSTACK(__FUNCTION_NAME);
1409 It is a failure if player is local and there already is a local
1412 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1414 Environment::addPlayer(player);
1417 LocalPlayer * ClientEnvironment::getLocalPlayer()
1419 for(core::list<Player*>::Iterator i = m_players.begin();
1420 i != m_players.end(); i++)
1422 Player *player = *i;
1423 if(player->isLocal())
1424 return (LocalPlayer*)player;
1429 void ClientEnvironment::step(float dtime)
1431 DSTACK(__FUNCTION_NAME);
1433 // Get some settings
1434 bool free_move = g_settings.getBool("free_move");
1435 bool footprints = g_settings.getBool("footprints");
1438 //TimeTaker timer("Client m_map->timerUpdate()");
1439 m_map->timerUpdate(dtime);
1443 LocalPlayer *lplayer = getLocalPlayer();
1445 // collision info queue
1446 core::list<CollisionInfo> player_collisions;
1449 Get the speed the player is going
1451 f32 player_speed = 0.001; // just some small value
1452 player_speed = lplayer->getSpeed().getLength();
1455 Maximum position increment
1457 //f32 position_max_increment = 0.05*BS;
1458 f32 position_max_increment = 0.1*BS;
1460 // Maximum time increment (for collision detection etc)
1461 // time = distance / speed
1462 f32 dtime_max_increment = position_max_increment / player_speed;
1464 // Maximum time increment is 10ms or lower
1465 if(dtime_max_increment > 0.01)
1466 dtime_max_increment = 0.01;
1468 // Don't allow overly huge dtime
1472 f32 dtime_downcount = dtime;
1475 Stuff that has a maximum time increment
1484 if(dtime_downcount > dtime_max_increment)
1486 dtime_part = dtime_max_increment;
1487 dtime_downcount -= dtime_part;
1491 dtime_part = dtime_downcount;
1493 Setting this to 0 (no -=dtime_part) disables an infinite loop
1494 when dtime_part is so small that dtime_downcount -= dtime_part
1497 dtime_downcount = 0;
1505 v3f lplayerpos = lplayer->getPosition();
1508 if(free_move == false)
1511 v3f speed = lplayer->getSpeed();
1512 if(lplayer->swimming_up == false)
1513 speed.Y -= 9.81 * BS * dtime_part * 2;
1516 if(lplayer->in_water_stable || lplayer->in_water)
1518 f32 max_down = 2.0*BS;
1519 if(speed.Y < -max_down) speed.Y = -max_down;
1522 if(speed.getLength() > max)
1524 speed = speed / speed.getLength() * max;
1528 lplayer->setSpeed(speed);
1533 This also does collision detection.
1535 lplayer->move(dtime_part, *m_map, position_max_increment,
1536 &player_collisions);
1539 while(dtime_downcount > 0.001);
1541 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1543 for(core::list<CollisionInfo>::Iterator
1544 i = player_collisions.begin();
1545 i != player_collisions.end(); i++)
1547 CollisionInfo &info = *i;
1548 if(info.t == COLLISION_FALL)
1550 //f32 tolerance = BS*10; // 2 without damage
1551 f32 tolerance = BS*12; // 3 without damage
1553 if(info.speed > tolerance)
1555 f32 damage_f = (info.speed - tolerance)/BS*factor;
1556 u16 damage = (u16)(damage_f+0.5);
1557 if(lplayer->hp > damage)
1558 lplayer->hp -= damage;
1562 ClientEnvEvent event;
1563 event.type = CEE_PLAYER_DAMAGE;
1564 event.player_damage.amount = damage;
1565 m_client_event_queue.push_back(event);
1571 Stuff that can be done in an arbitarily large dtime
1573 for(core::list<Player*>::Iterator i = m_players.begin();
1574 i != m_players.end(); i++)
1576 Player *player = *i;
1577 v3f playerpos = player->getPosition();
1580 Handle non-local players
1582 if(player->isLocal() == false)
1585 player->move(dtime, *m_map, 100*BS);
1587 // Update lighting on remote players on client
1588 u8 light = LIGHT_MAX;
1591 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1592 MapNode n = m_map->getNode(p);
1593 light = n.getLightBlend(getDayNightRatio());
1595 catch(InvalidPositionException &e) {}
1596 player->updateLight(light);
1600 Add footsteps to grass
1604 // Get node that is at BS/4 under player
1605 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1607 MapNode n = m_map->getNode(bottompos);
1608 if(n.d == CONTENT_GRASS)
1610 n.d = CONTENT_GRASS_FOOTSTEPS;
1611 m_map->setNode(bottompos, n);
1612 // Update mesh on client
1613 if(m_map->mapType() == MAPTYPE_CLIENT)
1615 v3s16 p_blocks = getNodeBlockPos(bottompos);
1616 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1617 //b->updateMesh(getDayNightRatio());
1618 b->setMeshExpired(true);
1622 catch(InvalidPositionException &e)
1629 Step active objects and update lighting of them
1632 for(core::map<u16, ClientActiveObject*>::Iterator
1633 i = m_active_objects.getIterator();
1634 i.atEnd()==false; i++)
1636 ClientActiveObject* obj = i.getNode()->getValue();
1638 obj->step(dtime, this);
1640 //u8 light = LIGHT_MAX;
1644 v3s16 p = obj->getLightPosition();
1645 MapNode n = m_map->getNode(p);
1646 light = n.getLightBlend(getDayNightRatio());
1648 catch(InvalidPositionException &e) {}
1649 obj->updateLight(light);
1653 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1655 m_map->updateMeshes(blockpos, getDayNightRatio());
1658 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1660 m_map->expireMeshes(only_daynight_diffed);
1663 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1665 core::map<u16, ClientActiveObject*>::Node *n;
1666 n = m_active_objects.find(id);
1669 return n->getValue();
1672 bool isFreeClientActiveObjectId(u16 id,
1673 core::map<u16, ClientActiveObject*> &objects)
1678 for(core::map<u16, ClientActiveObject*>::Iterator
1679 i = objects.getIterator();
1680 i.atEnd()==false; i++)
1682 if(i.getNode()->getKey() == id)
1688 u16 getFreeClientActiveObjectId(
1689 core::map<u16, ClientActiveObject*> &objects)
1694 if(isFreeClientActiveObjectId(new_id, objects))
1704 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1707 if(object->getId() == 0)
1709 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1712 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1713 <<"no free ids available"<<std::endl;
1717 object->setId(new_id);
1719 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1721 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1722 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1726 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1727 <<"added (id="<<object->getId()<<")"<<std::endl;
1728 m_active_objects.insert(object->getId(), object);
1729 object->addToScene(m_smgr);
1730 return object->getId();
1733 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1734 const std::string &init_data)
1736 ClientActiveObject* obj = ClientActiveObject::create(type);
1739 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1740 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1747 addActiveObject(obj);
1749 obj->initialize(init_data);
1752 void ClientEnvironment::removeActiveObject(u16 id)
1754 dstream<<"ClientEnvironment::removeActiveObject(): "
1755 <<"id="<<id<<std::endl;
1756 ClientActiveObject* obj = getActiveObject(id);
1759 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1760 <<"id="<<id<<" not found"<<std::endl;
1763 obj->removeFromScene();
1765 m_active_objects.remove(id);
1768 void ClientEnvironment::processActiveObjectMessage(u16 id,
1769 const std::string &data)
1771 ClientActiveObject* obj = getActiveObject(id);
1774 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1775 <<" got message for id="<<id<<", which doesn't exist."
1779 obj->processMessage(data);
1783 Callbacks for activeobjects
1786 void ClientEnvironment::damageLocalPlayer(u8 damage)
1788 LocalPlayer *lplayer = getLocalPlayer();
1791 if(lplayer->hp > damage)
1792 lplayer->hp -= damage;
1796 ClientEnvEvent event;
1797 event.type = CEE_PLAYER_DAMAGE;
1798 event.player_damage.amount = damage;
1799 m_client_event_queue.push_back(event);
1803 Client likes to call these
1806 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1807 core::array<DistanceSortedActiveObject> &dest)
1809 for(core::map<u16, ClientActiveObject*>::Iterator
1810 i = m_active_objects.getIterator();
1811 i.atEnd()==false; i++)
1813 ClientActiveObject* obj = i.getNode()->getValue();
1815 f32 d = (obj->getPosition() - origin).getLength();
1820 DistanceSortedActiveObject dso(obj, d);
1822 dest.push_back(dso);
1826 ClientEnvEvent ClientEnvironment::getClientEvent()
1828 if(m_client_event_queue.size() == 0)
1830 ClientEnvEvent event;
1831 event.type = CEE_NONE;
1834 return m_client_event_queue.pop_front();
1837 #endif // #ifndef SERVER