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"
29 Environment::Environment():
34 Environment::~Environment()
37 for(core::list<Player*>::Iterator i = m_players.begin();
38 i != m_players.end(); i++)
44 void Environment::addPlayer(Player *player)
46 DSTACK(__FUNCTION_NAME);
48 Check that peer_ids are unique.
49 Also check that names are unique.
50 Exception: there can be multiple players with peer_id=0
52 // If peer id is non-zero, it has to be unique.
53 if(player->peer_id != 0)
54 assert(getPlayer(player->peer_id) == NULL);
55 // Name has to be unique.
56 assert(getPlayer(player->getName()) == NULL);
58 m_players.push_back(player);
61 void Environment::removePlayer(u16 peer_id)
63 DSTACK(__FUNCTION_NAME);
65 for(core::list<Player*>::Iterator i = m_players.begin();
66 i != m_players.end(); i++)
69 if(player->peer_id != peer_id)
74 // See if there is an another one
75 // (shouldn't be, but just to be sure)
80 Player * Environment::getPlayer(u16 peer_id)
82 for(core::list<Player*>::Iterator i = m_players.begin();
83 i != m_players.end(); i++)
86 if(player->peer_id == peer_id)
92 Player * Environment::getPlayer(const char *name)
94 for(core::list<Player*>::Iterator i = m_players.begin();
95 i != m_players.end(); i++)
98 if(strcmp(player->getName(), name) == 0)
104 Player * Environment::getRandomConnectedPlayer()
106 core::list<Player*> connected_players = getPlayers(true);
107 u32 chosen_one = myrand() % connected_players.size();
109 for(core::list<Player*>::Iterator
110 i = connected_players.begin();
111 i != connected_players.end(); i++)
123 Player * Environment::getNearestConnectedPlayer(v3f pos)
125 core::list<Player*> connected_players = getPlayers(true);
127 Player *nearest_player = NULL;
128 for(core::list<Player*>::Iterator
129 i = connected_players.begin();
130 i != connected_players.end(); i++)
133 f32 d = player->getPosition().getDistanceFrom(pos);
134 if(d < nearest_d || nearest_player == NULL)
137 nearest_player = player;
140 return nearest_player;
143 core::list<Player*> Environment::getPlayers()
148 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
150 core::list<Player*> newlist;
151 for(core::list<Player*>::Iterator
152 i = m_players.begin();
153 i != m_players.end(); i++)
157 if(ignore_disconnected)
159 // Ignore disconnected players
160 if(player->peer_id == 0)
164 newlist.push_back(player);
169 void Environment::printPlayers(std::ostream &o)
171 o<<"Players in environment:"<<std::endl;
172 for(core::list<Player*>::Iterator i = m_players.begin();
173 i != m_players.end(); i++)
176 o<<"Player peer_id="<<player->peer_id<<std::endl;
180 /*void Environment::setDayNightRatio(u32 r)
182 getDayNightRatio() = r;
185 u32 Environment::getDayNightRatio()
187 //return getDayNightRatio();
188 return time_to_daynight_ratio(m_time_of_day);
195 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
198 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
199 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
200 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
207 void ActiveBlockList::update(core::list<v3s16> &active_positions,
209 core::map<v3s16, bool> &blocks_removed,
210 core::map<v3s16, bool> &blocks_added)
215 core::map<v3s16, bool> newlist;
216 for(core::list<v3s16>::Iterator i = active_positions.begin();
217 i != active_positions.end(); i++)
219 fillRadiusBlock(*i, radius, newlist);
223 Find out which blocks on the old list are not on the new list
225 // Go through old list
226 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
227 i.atEnd()==false; i++)
229 v3s16 p = i.getNode()->getKey();
230 // If not on new list, it's been removed
231 if(newlist.find(p) == NULL)
232 blocks_removed.insert(p, true);
236 Find out which blocks on the new list are not on the old list
238 // Go through new list
239 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
240 i.atEnd()==false; i++)
242 v3s16 p = i.getNode()->getKey();
243 // If not on old list, it's been added
244 if(m_list.find(p) == NULL)
245 blocks_added.insert(p, true);
252 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
253 i.atEnd()==false; i++)
255 v3s16 p = i.getNode()->getKey();
256 m_list.insert(p, true);
264 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
267 m_random_spawn_timer(3),
268 m_send_recommended_timer(0),
270 m_game_time_fraction_counter(0)
274 ServerEnvironment::~ServerEnvironment()
276 // Clear active block list.
277 // This makes the next one delete all active objects.
278 m_active_blocks.clear();
280 // Convert all objects to static and delete the active objects
281 deactivateFarObjects(true);
287 void ServerEnvironment::serializePlayers(const std::string &savedir)
289 std::string players_path = savedir + "/players";
290 fs::CreateDir(players_path);
292 core::map<Player*, bool> saved_players;
294 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
295 for(u32 i=0; i<player_files.size(); i++)
297 if(player_files[i].dir)
300 // Full path to this file
301 std::string path = players_path + "/" + player_files[i].name;
303 //dstream<<"Checking player file "<<path<<std::endl;
305 // Load player to see what is its name
306 ServerRemotePlayer testplayer;
308 // Open file and deserialize
309 std::ifstream is(path.c_str(), std::ios_base::binary);
310 if(is.good() == false)
312 dstream<<"Failed to read "<<path<<std::endl;
315 testplayer.deSerialize(is);
318 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
320 // Search for the player
321 std::string playername = testplayer.getName();
322 Player *player = getPlayer(playername.c_str());
325 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
329 //dstream<<"Found matching player, overwriting."<<std::endl;
331 // OK, found. Save player there.
333 // Open file and serialize
334 std::ofstream os(path.c_str(), std::ios_base::binary);
335 if(os.good() == false)
337 dstream<<"Failed to overwrite "<<path<<std::endl;
340 player->serialize(os);
341 saved_players.insert(player, true);
345 for(core::list<Player*>::Iterator i = m_players.begin();
346 i != m_players.end(); i++)
349 if(saved_players.find(player) != NULL)
351 /*dstream<<"Player "<<player->getName()
352 <<" was already saved."<<std::endl;*/
355 std::string playername = player->getName();
356 // Don't save unnamed player
359 //dstream<<"Not saving unnamed player."<<std::endl;
365 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
366 playername = "player";
367 std::string path = players_path + "/" + playername;
369 for(u32 i=0; i<1000; i++)
371 if(fs::PathExists(path) == false)
376 path = players_path + "/" + playername + itos(i);
380 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
385 /*dstream<<"Saving player "<<player->getName()<<" to "
387 // Open file and serialize
388 std::ofstream os(path.c_str(), std::ios_base::binary);
389 if(os.good() == false)
391 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
394 player->serialize(os);
395 saved_players.insert(player, true);
399 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
402 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
404 std::string players_path = savedir + "/players";
406 core::map<Player*, bool> saved_players;
408 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
409 for(u32 i=0; i<player_files.size(); i++)
411 if(player_files[i].dir)
414 // Full path to this file
415 std::string path = players_path + "/" + player_files[i].name;
417 dstream<<"Checking player file "<<path<<std::endl;
419 // Load player to see what is its name
420 ServerRemotePlayer testplayer;
422 // Open file and deserialize
423 std::ifstream is(path.c_str(), std::ios_base::binary);
424 if(is.good() == false)
426 dstream<<"Failed to read "<<path<<std::endl;
429 testplayer.deSerialize(is);
432 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
434 dstream<<"Not loading player with invalid name: "
435 <<testplayer.getName()<<std::endl;
438 dstream<<"Loaded test player with name "<<testplayer.getName()
441 // Search for the player
442 std::string playername = testplayer.getName();
443 Player *player = getPlayer(playername.c_str());
444 bool newplayer = false;
447 dstream<<"Is a new player"<<std::endl;
448 player = new ServerRemotePlayer();
454 dstream<<"Reading player "<<testplayer.getName()<<" from "
456 // Open file and deserialize
457 std::ifstream is(path.c_str(), std::ios_base::binary);
458 if(is.good() == false)
460 dstream<<"Failed to read "<<path<<std::endl;
463 player->deSerialize(is);
471 void ServerEnvironment::saveMeta(const std::string &savedir)
473 std::string path = savedir + "/env_meta.txt";
475 // Open file and serialize
476 std::ofstream os(path.c_str(), std::ios_base::binary);
477 if(os.good() == false)
479 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
481 throw SerializationError("Couldn't save env meta");
485 args.setU64("game_time", m_game_time);
486 args.setU64("time_of_day", getTimeOfDay());
491 void ServerEnvironment::loadMeta(const std::string &savedir)
493 std::string path = savedir + "/env_meta.txt";
495 // Open file and deserialize
496 std::ifstream is(path.c_str(), std::ios_base::binary);
497 if(is.good() == false)
499 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
501 throw SerializationError("Couldn't load env meta");
509 throw SerializationError
510 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
512 std::getline(is, line);
513 std::string trimmedline = trim(line);
514 if(trimmedline == "EnvArgsEnd")
516 args.parseConfigLine(line);
520 m_game_time = args.getU64("game_time");
521 }catch(SettingNotFoundException &e){
522 // Getting this is crucial, otherwise timestamps are useless
523 throw SerializationError("Couldn't load env meta game_time");
527 m_time_of_day = args.getU64("time_of_day");
528 }catch(SettingNotFoundException &e){
529 // This is not as important
530 m_time_of_day = 9000;
535 // This is probably very useless
536 void spawnRandomObjects(MapBlock *block)
538 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
539 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
541 bool last_node_walkable = false;
542 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
545 MapNode n = block->getNodeNoEx(p);
546 if(n.d == CONTENT_IGNORE)
548 if(content_features(n.d).liquid_type != LIQUID_NONE)
550 if(content_features(n.d).walkable)
552 last_node_walkable = true;
555 if(last_node_walkable)
557 // If block contains light information
558 if(content_features(n.d).param_type == CPT_LIGHT)
560 if(n.getLight(LIGHTBANK_DAY) <= 5)
562 if(myrand() % 1000 == 0)
564 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
566 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
567 std::string data = obj->getStaticData();
568 StaticObject s_obj(obj->getType(),
569 obj->getBasePosition(), data);
571 block->m_static_objects.insert(0, s_obj);
573 block->setChangedFlag();
578 last_node_walkable = false;
584 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
586 // Get time difference
588 u32 stamp = block->getTimestamp();
589 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
590 dtime_s = m_game_time - block->getTimestamp();
591 dtime_s += additional_dtime;
593 // Set current time as timestamp (and let it set ChangedFlag)
594 block->setTimestamp(m_game_time);
596 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
598 // Activate stored objects
599 activateObjects(block);
602 bool changed = block->m_node_metadata.step((float)dtime_s);
606 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
607 event.p = block->getPos();
608 m_map->dispatchEvent(&event);
610 block->setChangedFlag();
613 // TODO: Do something
614 // TODO: Implement usage of ActiveBlockModifier
616 // Here's a quick demonstration
618 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
619 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
620 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
622 v3s16 p = p0 + block->getPosRelative();
623 MapNode n = block->getNodeNoEx(p0);
626 // Convert all mud under proper day lighting to grass
627 if(n.d == CONTENT_MUD)
631 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
632 if(content_features(n_top.d).air_equivalent &&
633 n_top.getLight(LIGHTBANK_DAY) >= 13)
636 m_map->addNodeWithEvent(p, n);
644 void ServerEnvironment::step(float dtime)
646 DSTACK(__FUNCTION_NAME);
648 //TimeTaker timer("ServerEnv step");
651 bool footprints = g_settings.getBool("footprints");
657 m_game_time_fraction_counter += dtime;
658 u32 inc_i = (u32)m_game_time_fraction_counter;
659 m_game_time += inc_i;
660 m_game_time_fraction_counter -= (float)inc_i;
664 Let map update it's timers
667 //TimeTaker timer("Server m_map->timerUpdate()");
668 m_map->timerUpdate(dtime);
674 for(core::list<Player*>::Iterator i = m_players.begin();
675 i != m_players.end(); i++)
679 // Ignore disconnected players
680 if(player->peer_id == 0)
683 v3f playerpos = player->getPosition();
686 player->move(dtime, *m_map, 100*BS);
689 Add footsteps to grass
693 // Get node that is at BS/4 under player
694 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
696 MapNode n = m_map->getNode(bottompos);
697 if(n.d == CONTENT_GRASS)
699 n.d = CONTENT_GRASS_FOOTSTEPS;
700 m_map->setNode(bottompos, n);
703 catch(InvalidPositionException &e)
710 Manage active block list
712 if(m_active_blocks_management_interval.step(dtime, 2.0))
715 Get player block positions
717 core::list<v3s16> players_blockpos;
718 for(core::list<Player*>::Iterator
719 i = m_players.begin();
720 i != m_players.end(); i++)
723 // Ignore disconnected players
724 if(player->peer_id == 0)
726 v3s16 blockpos = getNodeBlockPos(
727 floatToInt(player->getPosition(), BS));
728 players_blockpos.push_back(blockpos);
732 Update list of active blocks, collecting changes
734 const s16 active_block_range = 5;
735 core::map<v3s16, bool> blocks_removed;
736 core::map<v3s16, bool> blocks_added;
737 m_active_blocks.update(players_blockpos, active_block_range,
738 blocks_removed, blocks_added);
741 Handle removed blocks
744 // Convert active objects that are no more in active blocks to static
745 deactivateFarObjects(false);
747 for(core::map<v3s16, bool>::Iterator
748 i = blocks_removed.getIterator();
749 i.atEnd()==false; i++)
751 v3s16 p = i.getNode()->getKey();
753 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
754 <<") became inactive"<<std::endl;*/
756 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
760 // Set current time as timestamp (and let it set ChangedFlag)
761 block->setTimestamp(m_game_time);
768 for(core::map<v3s16, bool>::Iterator
769 i = blocks_added.getIterator();
770 i.atEnd()==false; i++)
772 v3s16 p = i.getNode()->getKey();
774 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
775 <<") became active"<<std::endl;*/
777 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
781 activateBlock(block);
786 Mess around in active blocks
788 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
792 for(core::map<v3s16, bool>::Iterator
793 i = m_active_blocks.m_list.getIterator();
794 i.atEnd()==false; i++)
796 v3s16 p = i.getNode()->getKey();
798 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
799 <<") being handled"<<std::endl;*/
801 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
805 // Reset block usage timer
806 block->resetUsageTimer();
808 // Set current time as timestamp
809 block->setTimestampNoChangedFlag(m_game_time);
812 bool changed = block->m_node_metadata.step(dtime);
816 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
818 m_map->dispatchEvent(&event);
820 block->setChangedFlag();
824 if(m_active_blocks_test_interval.step(dtime, 10.0))
826 //float dtime = 10.0;
828 for(core::map<v3s16, bool>::Iterator
829 i = m_active_blocks.m_list.getIterator();
830 i.atEnd()==false; i++)
832 v3s16 p = i.getNode()->getKey();
834 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
835 <<") being handled"<<std::endl;*/
837 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
841 // Set current time as timestamp
842 block->setTimestampNoChangedFlag(m_game_time);
847 Note that map modifications should be done using the event-
848 making map methods so that the server gets information
851 Reading can be done quickly directly from the block.
853 Everything should bind to inside this single content
854 searching loop to keep things fast.
856 // TODO: Implement usage of ActiveBlockModifier
859 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
860 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
861 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
863 v3s16 p = p0 + block->getPosRelative();
864 MapNode n = block->getNodeNoEx(p0);
868 Convert mud under proper lighting to grass
870 if(n.d == CONTENT_MUD)
874 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
875 if(content_features(n_top.d).air_equivalent &&
876 n_top.getLightBlend(getDayNightRatio()) >= 13)
879 m_map->addNodeWithEvent(p, n);
884 Convert grass into mud if under something else than air
886 else if(n.d == CONTENT_GRASS)
888 //if(myrand()%20 == 0)
890 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
891 if(n_top.d != CONTENT_AIR
892 && n_top.d != CONTENT_IGNORE)
895 m_map->addNodeWithEvent(p, n);
907 //TimeTaker timer("Step active objects");
909 // This helps the objects to send data at the same time
910 bool send_recommended = false;
911 m_send_recommended_timer += dtime;
912 if(m_send_recommended_timer > 0.15)
914 m_send_recommended_timer = 0;
915 send_recommended = true;
918 for(core::map<u16, ServerActiveObject*>::Iterator
919 i = m_active_objects.getIterator();
920 i.atEnd()==false; i++)
922 ServerActiveObject* obj = i.getNode()->getValue();
923 // Don't step if is to be removed or stored statically
924 if(obj->m_removed || obj->m_pending_deactivation)
927 obj->step(dtime, send_recommended);
928 // Read messages from object
929 while(obj->m_messages_out.size() > 0)
931 m_active_object_messages.push_back(
932 obj->m_messages_out.pop_front());
938 Manage active objects
940 if(m_object_management_interval.step(dtime, 0.5))
943 Remove objects that satisfy (m_removed && m_known_by_count==0)
945 removeRemovedObjects();
948 if(g_settings.getBool("enable_experimental"))
955 m_random_spawn_timer -= dtime;
956 if(m_random_spawn_timer < 0)
958 //m_random_spawn_timer += myrand_range(2.0, 20.0);
959 //m_random_spawn_timer += 2.0;
960 m_random_spawn_timer += 200.0;
966 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
967 s16 y = 1 + getServerMap().findGroundLevel(p2d);
968 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
970 Player *player = getRandomConnectedPlayer();
973 pos = player->getPosition();
975 myrand_range(-3,3)*BS,
977 myrand_range(-3,3)*BS
981 Create a ServerActiveObject
984 //TestSAO *obj = new TestSAO(this, 0, pos);
985 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
986 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
987 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
988 addActiveObject(obj);
992 } // enable_experimental
995 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
997 core::map<u16, ServerActiveObject*>::Node *n;
998 n = m_active_objects.find(id);
1001 return n->getValue();
1004 bool isFreeServerActiveObjectId(u16 id,
1005 core::map<u16, ServerActiveObject*> &objects)
1010 for(core::map<u16, ServerActiveObject*>::Iterator
1011 i = objects.getIterator();
1012 i.atEnd()==false; i++)
1014 if(i.getNode()->getKey() == id)
1020 u16 getFreeServerActiveObjectId(
1021 core::map<u16, ServerActiveObject*> &objects)
1026 if(isFreeServerActiveObjectId(new_id, objects))
1036 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1039 u16 id = addActiveObjectRaw(object, true);
1044 Finds out what new objects have been added to
1045 inside a radius around a position
1047 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1048 core::map<u16, bool> ¤t_objects,
1049 core::map<u16, bool> &added_objects)
1051 v3f pos_f = intToFloat(pos, BS);
1052 f32 radius_f = radius * BS;
1054 Go through the object list,
1055 - discard m_removed objects,
1056 - discard objects that are too far away,
1057 - discard objects that are found in current_objects.
1058 - add remaining objects to added_objects
1060 for(core::map<u16, ServerActiveObject*>::Iterator
1061 i = m_active_objects.getIterator();
1062 i.atEnd()==false; i++)
1064 u16 id = i.getNode()->getKey();
1066 ServerActiveObject *object = i.getNode()->getValue();
1069 // Discard if removed
1070 if(object->m_removed)
1072 // Discard if too far
1073 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1074 if(distance_f > radius_f)
1076 // Discard if already on current_objects
1077 core::map<u16, bool>::Node *n;
1078 n = current_objects.find(id);
1081 // Add to added_objects
1082 added_objects.insert(id, false);
1087 Finds out what objects have been removed from
1088 inside a radius around a position
1090 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1091 core::map<u16, bool> ¤t_objects,
1092 core::map<u16, bool> &removed_objects)
1094 v3f pos_f = intToFloat(pos, BS);
1095 f32 radius_f = radius * BS;
1097 Go through current_objects; object is removed if:
1098 - object is not found in m_active_objects (this is actually an
1099 error condition; objects should be set m_removed=true and removed
1100 only after all clients have been informed about removal), or
1101 - object has m_removed=true, or
1102 - object is too far away
1104 for(core::map<u16, bool>::Iterator
1105 i = current_objects.getIterator();
1106 i.atEnd()==false; i++)
1108 u16 id = i.getNode()->getKey();
1109 ServerActiveObject *object = getActiveObject(id);
1112 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1113 <<" object in current_objects is NULL"<<std::endl;
1115 else if(object->m_removed == false)
1117 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1118 /*dstream<<"removed == false"
1119 <<"distance_f = "<<distance_f
1120 <<", radius_f = "<<radius_f<<std::endl;*/
1121 if(distance_f < radius_f)
1127 removed_objects.insert(id, false);
1131 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1133 if(m_active_object_messages.size() == 0)
1134 return ActiveObjectMessage(0);
1136 return m_active_object_messages.pop_front();
1140 ************ Private methods *************
1143 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1147 if(object->getId() == 0)
1149 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1152 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1153 <<"no free ids available"<<std::endl;
1157 object->setId(new_id);
1159 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1161 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1162 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1166 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1167 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1169 m_active_objects.insert(object->getId(), object);
1171 // Add static object to active static list of the block
1172 v3f objectpos = object->getBasePosition();
1173 std::string staticdata = object->getStaticData();
1174 StaticObject s_obj(object->getType(), objectpos, staticdata);
1175 // Add to the block where the object is located in
1176 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1177 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1180 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1181 object->m_static_exists = true;
1182 object->m_static_block = blockpos;
1185 block->setChangedFlag();
1188 dstream<<"WARNING: Server: Could not find a block for "
1189 <<"storing newly added static active object"<<std::endl;
1192 return object->getId();
1196 Remove objects that satisfy (m_removed && m_known_by_count==0)
1198 void ServerEnvironment::removeRemovedObjects()
1200 core::list<u16> objects_to_remove;
1201 for(core::map<u16, ServerActiveObject*>::Iterator
1202 i = m_active_objects.getIterator();
1203 i.atEnd()==false; i++)
1205 u16 id = i.getNode()->getKey();
1206 ServerActiveObject* obj = i.getNode()->getValue();
1207 // This shouldn't happen but check it
1210 dstream<<"WARNING: NULL object found in ServerEnvironment"
1211 <<" while finding removed objects. id="<<id<<std::endl;
1212 // Id to be removed from m_active_objects
1213 objects_to_remove.push_back(id);
1218 We will delete objects that are marked as removed or thatare
1219 waiting for deletion after deactivation
1221 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1225 Delete static data from block if is marked as removed
1227 if(obj->m_static_exists && obj->m_removed)
1229 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1232 block->m_static_objects.remove(id);
1233 block->setChangedFlag();
1237 // If m_known_by_count > 0, don't actually remove.
1238 if(obj->m_known_by_count > 0)
1243 // Id to be removed from m_active_objects
1244 objects_to_remove.push_back(id);
1246 // Remove references from m_active_objects
1247 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1248 i != objects_to_remove.end(); i++)
1250 m_active_objects.remove(*i);
1255 Convert stored objects from blocks near the players to active.
1257 void ServerEnvironment::activateObjects(MapBlock *block)
1261 // Ignore if no stored objects (to not set changed flag)
1262 if(block->m_static_objects.m_stored.size() == 0)
1264 // A list for objects that couldn't be converted to static for some
1265 // reason. They will be stored back.
1266 core::list<StaticObject> new_stored;
1267 // Loop through stored static objects
1268 for(core::list<StaticObject>::Iterator
1269 i = block->m_static_objects.m_stored.begin();
1270 i != block->m_static_objects.m_stored.end(); i++)
1272 /*dstream<<"INFO: Server: Creating an active object from "
1273 <<"static data"<<std::endl;*/
1274 StaticObject &s_obj = *i;
1275 // Create an active object from the data
1276 ServerActiveObject *obj = ServerActiveObject::create
1277 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1278 // If couldn't create object, store static data back.
1281 new_stored.push_back(s_obj);
1284 // This will also add the object to the active static list
1285 addActiveObjectRaw(obj, false);
1286 //u16 id = addActiveObjectRaw(obj, false);
1288 // Clear stored list
1289 block->m_static_objects.m_stored.clear();
1290 // Add leftover failed stuff to stored list
1291 for(core::list<StaticObject>::Iterator
1292 i = new_stored.begin();
1293 i != new_stored.end(); i++)
1295 StaticObject &s_obj = *i;
1296 block->m_static_objects.m_stored.push_back(s_obj);
1298 // Block has been modified
1299 // NOTE: No it has not really. Save I/O here.
1300 //block->setChangedFlag();
1304 Convert objects that are not in active blocks to static.
1306 If m_known_by_count != 0, active object is not deleted, but static
1307 data is still updated.
1309 If force_delete is set, active object is deleted nevertheless. It
1310 shall only be set so in the destructor of the environment.
1312 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1314 core::list<u16> objects_to_remove;
1315 for(core::map<u16, ServerActiveObject*>::Iterator
1316 i = m_active_objects.getIterator();
1317 i.atEnd()==false; i++)
1319 ServerActiveObject* obj = i.getNode()->getValue();
1320 u16 id = i.getNode()->getKey();
1321 v3f objectpos = obj->getBasePosition();
1323 // This shouldn't happen but check it
1326 dstream<<"WARNING: NULL object found in ServerEnvironment"
1332 // The block in which the object resides in
1333 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1335 // If block is active, don't remove
1336 if(m_active_blocks.contains(blockpos_o))
1340 Update the static data
1343 // Delete old static object
1344 MapBlock *oldblock = NULL;
1345 if(obj->m_static_exists)
1347 MapBlock *block = m_map->getBlockNoCreateNoEx
1348 (obj->m_static_block);
1351 block->m_static_objects.remove(id);
1355 // Create new static object
1356 std::string staticdata = obj->getStaticData();
1357 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1358 // Add to the block where the object is located in
1359 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1360 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1363 block->m_static_objects.insert(0, s_obj);
1364 block->setChangedFlag();
1365 obj->m_static_exists = true;
1366 obj->m_static_block = block->getPos();
1368 // If not possible, add back to previous block
1371 oldblock->m_static_objects.insert(0, s_obj);
1372 oldblock->setChangedFlag();
1373 obj->m_static_exists = true;
1374 obj->m_static_block = oldblock->getPos();
1377 dstream<<"WARNING: Server: Could not find a block for "
1378 <<"storing static object"<<std::endl;
1379 obj->m_static_exists = false;
1384 Delete active object if not known by some client,
1385 else set pending deactivation
1388 // If known by some client, don't delete.
1389 if(obj->m_known_by_count > 0 && force_delete == false)
1391 obj->m_pending_deactivation = true;
1395 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1397 // Delete active object
1399 // Id to be removed from m_active_objects
1400 objects_to_remove.push_back(id);
1403 // Remove references from m_active_objects
1404 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1405 i != objects_to_remove.end(); i++)
1407 m_active_objects.remove(*i);
1418 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1426 ClientEnvironment::~ClientEnvironment()
1428 // delete active objects
1429 for(core::map<u16, ClientActiveObject*>::Iterator
1430 i = m_active_objects.getIterator();
1431 i.atEnd()==false; i++)
1433 delete i.getNode()->getValue();
1440 void ClientEnvironment::addPlayer(Player *player)
1442 DSTACK(__FUNCTION_NAME);
1444 It is a failure if player is local and there already is a local
1447 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1449 Environment::addPlayer(player);
1452 LocalPlayer * ClientEnvironment::getLocalPlayer()
1454 for(core::list<Player*>::Iterator i = m_players.begin();
1455 i != m_players.end(); i++)
1457 Player *player = *i;
1458 if(player->isLocal())
1459 return (LocalPlayer*)player;
1464 void ClientEnvironment::step(float dtime)
1466 DSTACK(__FUNCTION_NAME);
1468 // Get some settings
1469 bool free_move = g_settings.getBool("free_move");
1470 bool footprints = g_settings.getBool("footprints");
1473 //TimeTaker timer("Client m_map->timerUpdate()");
1474 m_map->timerUpdate(dtime);
1478 LocalPlayer *lplayer = getLocalPlayer();
1480 // collision info queue
1481 core::list<CollisionInfo> player_collisions;
1484 Get the speed the player is going
1486 f32 player_speed = 0.001; // just some small value
1487 player_speed = lplayer->getSpeed().getLength();
1490 Maximum position increment
1492 //f32 position_max_increment = 0.05*BS;
1493 f32 position_max_increment = 0.1*BS;
1495 // Maximum time increment (for collision detection etc)
1496 // time = distance / speed
1497 f32 dtime_max_increment = position_max_increment / player_speed;
1499 // Maximum time increment is 10ms or lower
1500 if(dtime_max_increment > 0.01)
1501 dtime_max_increment = 0.01;
1503 // Don't allow overly huge dtime
1507 f32 dtime_downcount = dtime;
1510 Stuff that has a maximum time increment
1519 if(dtime_downcount > dtime_max_increment)
1521 dtime_part = dtime_max_increment;
1522 dtime_downcount -= dtime_part;
1526 dtime_part = dtime_downcount;
1528 Setting this to 0 (no -=dtime_part) disables an infinite loop
1529 when dtime_part is so small that dtime_downcount -= dtime_part
1532 dtime_downcount = 0;
1540 v3f lplayerpos = lplayer->getPosition();
1543 if(free_move == false)
1546 v3f speed = lplayer->getSpeed();
1547 if(lplayer->swimming_up == false)
1548 speed.Y -= 9.81 * BS * dtime_part * 2;
1551 if(lplayer->in_water_stable || lplayer->in_water)
1553 f32 max_down = 2.0*BS;
1554 if(speed.Y < -max_down) speed.Y = -max_down;
1557 if(speed.getLength() > max)
1559 speed = speed / speed.getLength() * max;
1563 lplayer->setSpeed(speed);
1568 This also does collision detection.
1570 lplayer->move(dtime_part, *m_map, position_max_increment,
1571 &player_collisions);
1574 while(dtime_downcount > 0.001);
1576 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1578 for(core::list<CollisionInfo>::Iterator
1579 i = player_collisions.begin();
1580 i != player_collisions.end(); i++)
1582 CollisionInfo &info = *i;
1583 if(info.t == COLLISION_FALL)
1585 //f32 tolerance = BS*10; // 2 without damage
1586 f32 tolerance = BS*12; // 3 without damage
1588 if(info.speed > tolerance)
1590 f32 damage_f = (info.speed - tolerance)/BS*factor;
1591 u16 damage = (u16)(damage_f+0.5);
1592 if(lplayer->hp > damage)
1593 lplayer->hp -= damage;
1597 ClientEnvEvent event;
1598 event.type = CEE_PLAYER_DAMAGE;
1599 event.player_damage.amount = damage;
1600 m_client_event_queue.push_back(event);
1606 Stuff that can be done in an arbitarily large dtime
1608 for(core::list<Player*>::Iterator i = m_players.begin();
1609 i != m_players.end(); i++)
1611 Player *player = *i;
1612 v3f playerpos = player->getPosition();
1615 Handle non-local players
1617 if(player->isLocal() == false)
1620 player->move(dtime, *m_map, 100*BS);
1622 // Update lighting on remote players on client
1623 u8 light = LIGHT_MAX;
1626 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1627 MapNode n = m_map->getNode(p);
1628 light = n.getLightBlend(getDayNightRatio());
1630 catch(InvalidPositionException &e) {}
1631 player->updateLight(light);
1635 Add footsteps to grass
1639 // Get node that is at BS/4 under player
1640 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1642 MapNode n = m_map->getNode(bottompos);
1643 if(n.d == CONTENT_GRASS)
1645 n.d = CONTENT_GRASS_FOOTSTEPS;
1646 m_map->setNode(bottompos, n);
1647 // Update mesh on client
1648 if(m_map->mapType() == MAPTYPE_CLIENT)
1650 v3s16 p_blocks = getNodeBlockPos(bottompos);
1651 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1652 //b->updateMesh(getDayNightRatio());
1653 b->setMeshExpired(true);
1657 catch(InvalidPositionException &e)
1664 Step active objects and update lighting of them
1667 for(core::map<u16, ClientActiveObject*>::Iterator
1668 i = m_active_objects.getIterator();
1669 i.atEnd()==false; i++)
1671 ClientActiveObject* obj = i.getNode()->getValue();
1673 obj->step(dtime, this);
1675 if(m_active_object_light_update_interval.step(dtime, 0.5))
1678 //u8 light = LIGHT_MAX;
1682 v3s16 p = obj->getLightPosition();
1683 MapNode n = m_map->getNode(p);
1684 light = n.getLightBlend(getDayNightRatio());
1686 catch(InvalidPositionException &e) {}
1687 obj->updateLight(light);
1692 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1694 m_map->updateMeshes(blockpos, getDayNightRatio());
1697 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1699 m_map->expireMeshes(only_daynight_diffed);
1702 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1704 core::map<u16, ClientActiveObject*>::Node *n;
1705 n = m_active_objects.find(id);
1708 return n->getValue();
1711 bool isFreeClientActiveObjectId(u16 id,
1712 core::map<u16, ClientActiveObject*> &objects)
1717 for(core::map<u16, ClientActiveObject*>::Iterator
1718 i = objects.getIterator();
1719 i.atEnd()==false; i++)
1721 if(i.getNode()->getKey() == id)
1727 u16 getFreeClientActiveObjectId(
1728 core::map<u16, ClientActiveObject*> &objects)
1733 if(isFreeClientActiveObjectId(new_id, objects))
1743 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1746 if(object->getId() == 0)
1748 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1751 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1752 <<"no free ids available"<<std::endl;
1756 object->setId(new_id);
1758 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1760 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1761 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1765 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1766 <<"added (id="<<object->getId()<<")"<<std::endl;
1767 m_active_objects.insert(object->getId(), object);
1768 object->addToScene(m_smgr);
1769 return object->getId();
1772 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1773 const std::string &init_data)
1775 ClientActiveObject* obj = ClientActiveObject::create(type);
1778 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1779 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1786 addActiveObject(obj);
1788 obj->initialize(init_data);
1791 void ClientEnvironment::removeActiveObject(u16 id)
1793 dstream<<"ClientEnvironment::removeActiveObject(): "
1794 <<"id="<<id<<std::endl;
1795 ClientActiveObject* obj = getActiveObject(id);
1798 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1799 <<"id="<<id<<" not found"<<std::endl;
1802 obj->removeFromScene();
1804 m_active_objects.remove(id);
1807 void ClientEnvironment::processActiveObjectMessage(u16 id,
1808 const std::string &data)
1810 ClientActiveObject* obj = getActiveObject(id);
1813 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1814 <<" got message for id="<<id<<", which doesn't exist."
1818 obj->processMessage(data);
1822 Callbacks for activeobjects
1825 void ClientEnvironment::damageLocalPlayer(u8 damage)
1827 LocalPlayer *lplayer = getLocalPlayer();
1830 if(lplayer->hp > damage)
1831 lplayer->hp -= damage;
1835 ClientEnvEvent event;
1836 event.type = CEE_PLAYER_DAMAGE;
1837 event.player_damage.amount = damage;
1838 m_client_event_queue.push_back(event);
1842 Client likes to call these
1845 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1846 core::array<DistanceSortedActiveObject> &dest)
1848 for(core::map<u16, ClientActiveObject*>::Iterator
1849 i = m_active_objects.getIterator();
1850 i.atEnd()==false; i++)
1852 ClientActiveObject* obj = i.getNode()->getValue();
1854 f32 d = (obj->getPosition() - origin).getLength();
1859 DistanceSortedActiveObject dso(obj, d);
1861 dest.push_back(dso);
1865 ClientEnvEvent ClientEnvironment::getClientEvent()
1867 if(m_client_event_queue.size() == 0)
1869 ClientEnvEvent event;
1870 event.type = CEE_NONE;
1873 return m_client_event_queue.pop_front();
1876 #endif // #ifndef SERVER