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;
666 for(core::list<Player*>::Iterator i = m_players.begin();
667 i != m_players.end(); i++)
671 // Ignore disconnected players
672 if(player->peer_id == 0)
675 v3f playerpos = player->getPosition();
678 player->move(dtime, *m_map, 100*BS);
681 Add footsteps to grass
685 // Get node that is at BS/4 under player
686 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
688 MapNode n = m_map->getNode(bottompos);
689 if(n.d == CONTENT_GRASS)
691 n.d = CONTENT_GRASS_FOOTSTEPS;
692 m_map->setNode(bottompos, n);
695 catch(InvalidPositionException &e)
702 Manage active block list
704 if(m_active_blocks_management_interval.step(dtime, 2.0))
707 Get player block positions
709 core::list<v3s16> players_blockpos;
710 for(core::list<Player*>::Iterator
711 i = m_players.begin();
712 i != m_players.end(); i++)
715 // Ignore disconnected players
716 if(player->peer_id == 0)
718 v3s16 blockpos = getNodeBlockPos(
719 floatToInt(player->getPosition(), BS));
720 players_blockpos.push_back(blockpos);
724 Update list of active blocks, collecting changes
726 const s16 active_block_range = 5;
727 core::map<v3s16, bool> blocks_removed;
728 core::map<v3s16, bool> blocks_added;
729 m_active_blocks.update(players_blockpos, active_block_range,
730 blocks_removed, blocks_added);
733 Handle removed blocks
736 // Convert active objects that are no more in active blocks to static
737 deactivateFarObjects(false);
739 for(core::map<v3s16, bool>::Iterator
740 i = blocks_removed.getIterator();
741 i.atEnd()==false; i++)
743 v3s16 p = i.getNode()->getKey();
745 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
746 <<") became inactive"<<std::endl;*/
748 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
752 // Set current time as timestamp (and let it set ChangedFlag)
753 block->setTimestamp(m_game_time);
760 for(core::map<v3s16, bool>::Iterator
761 i = blocks_added.getIterator();
762 i.atEnd()==false; i++)
764 v3s16 p = i.getNode()->getKey();
766 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
767 <<") became active"<<std::endl;*/
769 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
773 activateBlock(block);
778 Mess around in active blocks
780 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
784 for(core::map<v3s16, bool>::Iterator
785 i = m_active_blocks.m_list.getIterator();
786 i.atEnd()==false; i++)
788 v3s16 p = i.getNode()->getKey();
790 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
791 <<") being handled"<<std::endl;*/
793 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
797 // Reset block usage timer
798 block->resetUsageTimer();
800 // Set current time as timestamp
801 block->setTimestampNoChangedFlag(m_game_time);
804 bool changed = block->m_node_metadata.step(dtime);
808 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
810 m_map->dispatchEvent(&event);
812 block->setChangedFlag();
816 if(m_active_blocks_test_interval.step(dtime, 10.0))
818 //float dtime = 10.0;
820 for(core::map<v3s16, bool>::Iterator
821 i = m_active_blocks.m_list.getIterator();
822 i.atEnd()==false; i++)
824 v3s16 p = i.getNode()->getKey();
826 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
827 <<") being handled"<<std::endl;*/
829 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
833 // Set current time as timestamp
834 block->setTimestampNoChangedFlag(m_game_time);
839 Note that map modifications should be done using the event-
840 making map methods so that the server gets information
843 Reading can be done quickly directly from the block.
845 Everything should bind to inside this single content
846 searching loop to keep things fast.
848 // TODO: Implement usage of ActiveBlockModifier
851 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
852 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
853 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
855 v3s16 p = p0 + block->getPosRelative();
856 MapNode n = block->getNodeNoEx(p0);
860 Convert mud under proper lighting to grass
862 if(n.d == CONTENT_MUD)
866 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
867 if(content_features(n_top.d).air_equivalent &&
868 n_top.getLightBlend(getDayNightRatio()) >= 13)
871 m_map->addNodeWithEvent(p, n);
876 Convert grass into mud if under something else than air
878 else if(n.d == CONTENT_GRASS)
880 //if(myrand()%20 == 0)
882 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
883 if(n_top.d != CONTENT_AIR
884 && n_top.d != CONTENT_IGNORE)
887 m_map->addNodeWithEvent(p, n);
899 //TimeTaker timer("Step active objects");
901 // This helps the objects to send data at the same time
902 bool send_recommended = false;
903 m_send_recommended_timer += dtime;
904 if(m_send_recommended_timer > 0.15)
906 m_send_recommended_timer = 0;
907 send_recommended = true;
910 for(core::map<u16, ServerActiveObject*>::Iterator
911 i = m_active_objects.getIterator();
912 i.atEnd()==false; i++)
914 ServerActiveObject* obj = i.getNode()->getValue();
915 // Don't step if is to be removed or stored statically
916 if(obj->m_removed || obj->m_pending_deactivation)
919 obj->step(dtime, send_recommended);
920 // Read messages from object
921 while(obj->m_messages_out.size() > 0)
923 m_active_object_messages.push_back(
924 obj->m_messages_out.pop_front());
930 Manage active objects
932 if(m_object_management_interval.step(dtime, 0.5))
935 Remove objects that satisfy (m_removed && m_known_by_count==0)
937 removeRemovedObjects();
940 if(g_settings.getBool("enable_experimental"))
947 m_random_spawn_timer -= dtime;
948 if(m_random_spawn_timer < 0)
950 //m_random_spawn_timer += myrand_range(2.0, 20.0);
951 //m_random_spawn_timer += 2.0;
952 m_random_spawn_timer += 200.0;
958 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
959 s16 y = 1 + getServerMap().findGroundLevel(p2d);
960 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
962 Player *player = getRandomConnectedPlayer();
965 pos = player->getPosition();
967 myrand_range(-3,3)*BS,
969 myrand_range(-3,3)*BS
973 Create a ServerActiveObject
976 //TestSAO *obj = new TestSAO(this, 0, pos);
977 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
978 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
979 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
980 ServerActiveObject *obj = new FireflySAO(this, 0, pos);
981 addActiveObject(obj);
985 } // enable_experimental
988 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
990 core::map<u16, ServerActiveObject*>::Node *n;
991 n = m_active_objects.find(id);
994 return n->getValue();
997 bool isFreeServerActiveObjectId(u16 id,
998 core::map<u16, ServerActiveObject*> &objects)
1003 for(core::map<u16, ServerActiveObject*>::Iterator
1004 i = objects.getIterator();
1005 i.atEnd()==false; i++)
1007 if(i.getNode()->getKey() == id)
1013 u16 getFreeServerActiveObjectId(
1014 core::map<u16, ServerActiveObject*> &objects)
1019 if(isFreeServerActiveObjectId(new_id, objects))
1029 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1032 u16 id = addActiveObjectRaw(object, true);
1037 Finds out what new objects have been added to
1038 inside a radius around a position
1040 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1041 core::map<u16, bool> ¤t_objects,
1042 core::map<u16, bool> &added_objects)
1044 v3f pos_f = intToFloat(pos, BS);
1045 f32 radius_f = radius * BS;
1047 Go through the object list,
1048 - discard m_removed objects,
1049 - discard objects that are too far away,
1050 - discard objects that are found in current_objects.
1051 - add remaining objects to added_objects
1053 for(core::map<u16, ServerActiveObject*>::Iterator
1054 i = m_active_objects.getIterator();
1055 i.atEnd()==false; i++)
1057 u16 id = i.getNode()->getKey();
1059 ServerActiveObject *object = i.getNode()->getValue();
1062 // Discard if removed
1063 if(object->m_removed)
1065 // Discard if too far
1066 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1067 if(distance_f > radius_f)
1069 // Discard if already on current_objects
1070 core::map<u16, bool>::Node *n;
1071 n = current_objects.find(id);
1074 // Add to added_objects
1075 added_objects.insert(id, false);
1080 Finds out what objects have been removed from
1081 inside a radius around a position
1083 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1084 core::map<u16, bool> ¤t_objects,
1085 core::map<u16, bool> &removed_objects)
1087 v3f pos_f = intToFloat(pos, BS);
1088 f32 radius_f = radius * BS;
1090 Go through current_objects; object is removed if:
1091 - object is not found in m_active_objects (this is actually an
1092 error condition; objects should be set m_removed=true and removed
1093 only after all clients have been informed about removal), or
1094 - object has m_removed=true, or
1095 - object is too far away
1097 for(core::map<u16, bool>::Iterator
1098 i = current_objects.getIterator();
1099 i.atEnd()==false; i++)
1101 u16 id = i.getNode()->getKey();
1102 ServerActiveObject *object = getActiveObject(id);
1105 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1106 <<" object in current_objects is NULL"<<std::endl;
1108 else if(object->m_removed == false)
1110 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1111 /*dstream<<"removed == false"
1112 <<"distance_f = "<<distance_f
1113 <<", radius_f = "<<radius_f<<std::endl;*/
1114 if(distance_f < radius_f)
1120 removed_objects.insert(id, false);
1124 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1126 if(m_active_object_messages.size() == 0)
1127 return ActiveObjectMessage(0);
1129 return m_active_object_messages.pop_front();
1133 ************ Private methods *************
1136 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1140 if(object->getId() == 0)
1142 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1145 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1146 <<"no free ids available"<<std::endl;
1150 object->setId(new_id);
1152 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1154 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1155 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1159 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1160 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1162 m_active_objects.insert(object->getId(), object);
1164 // Add static object to active static list of the block
1165 v3f objectpos = object->getBasePosition();
1166 std::string staticdata = object->getStaticData();
1167 StaticObject s_obj(object->getType(), objectpos, staticdata);
1168 // Add to the block where the object is located in
1169 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1170 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1173 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1174 object->m_static_exists = true;
1175 object->m_static_block = blockpos;
1178 block->setChangedFlag();
1181 dstream<<"WARNING: ServerEnv: Could not find a block for "
1182 <<"storing newly added static active object"<<std::endl;
1185 return object->getId();
1189 Remove objects that satisfy (m_removed && m_known_by_count==0)
1191 void ServerEnvironment::removeRemovedObjects()
1193 core::list<u16> objects_to_remove;
1194 for(core::map<u16, ServerActiveObject*>::Iterator
1195 i = m_active_objects.getIterator();
1196 i.atEnd()==false; i++)
1198 u16 id = i.getNode()->getKey();
1199 ServerActiveObject* obj = i.getNode()->getValue();
1200 // This shouldn't happen but check it
1203 dstream<<"WARNING: NULL object found in ServerEnvironment"
1204 <<" while finding removed objects. id="<<id<<std::endl;
1205 // Id to be removed from m_active_objects
1206 objects_to_remove.push_back(id);
1211 We will delete objects that are marked as removed or thatare
1212 waiting for deletion after deactivation
1214 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1218 Delete static data from block if is marked as removed
1220 if(obj->m_static_exists && obj->m_removed)
1222 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1225 block->m_static_objects.remove(id);
1226 block->setChangedFlag();
1230 // If m_known_by_count > 0, don't actually remove.
1231 if(obj->m_known_by_count > 0)
1236 // Id to be removed from m_active_objects
1237 objects_to_remove.push_back(id);
1239 // Remove references from m_active_objects
1240 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1241 i != objects_to_remove.end(); i++)
1243 m_active_objects.remove(*i);
1248 Convert stored objects from blocks near the players to active.
1250 void ServerEnvironment::activateObjects(MapBlock *block)
1254 // Ignore if no stored objects (to not set changed flag)
1255 if(block->m_static_objects.m_stored.size() == 0)
1257 // A list for objects that couldn't be converted to static for some
1258 // reason. They will be stored back.
1259 core::list<StaticObject> new_stored;
1260 // Loop through stored static objects
1261 for(core::list<StaticObject>::Iterator
1262 i = block->m_static_objects.m_stored.begin();
1263 i != block->m_static_objects.m_stored.end(); i++)
1265 /*dstream<<"INFO: Server: Creating an active object from "
1266 <<"static data"<<std::endl;*/
1267 StaticObject &s_obj = *i;
1268 // Create an active object from the data
1269 ServerActiveObject *obj = ServerActiveObject::create
1270 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1271 // If couldn't create object, store static data back.
1274 new_stored.push_back(s_obj);
1277 // This will also add the object to the active static list
1278 addActiveObjectRaw(obj, false);
1279 //u16 id = addActiveObjectRaw(obj, false);
1281 // Clear stored list
1282 block->m_static_objects.m_stored.clear();
1283 // Add leftover failed stuff to stored list
1284 for(core::list<StaticObject>::Iterator
1285 i = new_stored.begin();
1286 i != new_stored.end(); i++)
1288 StaticObject &s_obj = *i;
1289 block->m_static_objects.m_stored.push_back(s_obj);
1291 // Block has been modified
1292 // NOTE: No it has not really. Save I/O here.
1293 //block->setChangedFlag();
1297 Convert objects that are not in active blocks to static.
1299 If m_known_by_count != 0, active object is not deleted, but static
1300 data is still updated.
1302 If force_delete is set, active object is deleted nevertheless. It
1303 shall only be set so in the destructor of the environment.
1305 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1307 core::list<u16> objects_to_remove;
1308 for(core::map<u16, ServerActiveObject*>::Iterator
1309 i = m_active_objects.getIterator();
1310 i.atEnd()==false; i++)
1312 ServerActiveObject* obj = i.getNode()->getValue();
1313 u16 id = i.getNode()->getKey();
1314 v3f objectpos = obj->getBasePosition();
1316 // This shouldn't happen but check it
1319 dstream<<"WARNING: NULL object found in ServerEnvironment"
1325 // The block in which the object resides in
1326 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1328 // If block is active, don't remove
1329 if(m_active_blocks.contains(blockpos_o))
1333 Update the static data
1336 // Delete old static object
1337 MapBlock *oldblock = NULL;
1338 if(obj->m_static_exists)
1340 MapBlock *block = m_map->getBlockNoCreateNoEx
1341 (obj->m_static_block);
1344 block->m_static_objects.remove(id);
1348 // Create new static object
1349 std::string staticdata = obj->getStaticData();
1350 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1351 // Add to the block where the object is located in
1352 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1353 // Get or generate the block
1354 MapBlock *block = m_map->emergeBlock(blockpos);
1356 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1359 // Block not found. Is the old block still ok?
1362 // Load from disk or generate
1364 block = m_map->emergeBlock(blockpos);
1369 block->m_static_objects.insert(0, s_obj);
1370 block->setChangedFlag();
1371 obj->m_static_exists = true;
1372 obj->m_static_block = block->getPos();
1375 dstream<<"WARNING: ServerEnv: Could not find or generate "
1376 <<"a block for storing static object"<<std::endl;
1377 obj->m_static_exists = false;
1382 Delete active object if not known by some client,
1383 else set pending deactivation
1386 // If known by some client, don't delete.
1387 if(obj->m_known_by_count > 0 && force_delete == false)
1389 obj->m_pending_deactivation = true;
1393 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1395 // Delete active object
1397 // Id to be removed from m_active_objects
1398 objects_to_remove.push_back(id);
1401 // Remove references from m_active_objects
1402 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1403 i != objects_to_remove.end(); i++)
1405 m_active_objects.remove(*i);
1416 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1424 ClientEnvironment::~ClientEnvironment()
1426 // delete active objects
1427 for(core::map<u16, ClientActiveObject*>::Iterator
1428 i = m_active_objects.getIterator();
1429 i.atEnd()==false; i++)
1431 delete i.getNode()->getValue();
1438 void ClientEnvironment::addPlayer(Player *player)
1440 DSTACK(__FUNCTION_NAME);
1442 It is a failure if player is local and there already is a local
1445 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1447 Environment::addPlayer(player);
1450 LocalPlayer * ClientEnvironment::getLocalPlayer()
1452 for(core::list<Player*>::Iterator i = m_players.begin();
1453 i != m_players.end(); i++)
1455 Player *player = *i;
1456 if(player->isLocal())
1457 return (LocalPlayer*)player;
1462 void ClientEnvironment::step(float dtime)
1464 DSTACK(__FUNCTION_NAME);
1466 // Get some settings
1467 bool free_move = g_settings.getBool("free_move");
1468 bool footprints = g_settings.getBool("footprints");
1471 LocalPlayer *lplayer = getLocalPlayer();
1473 // collision info queue
1474 core::list<CollisionInfo> player_collisions;
1477 Get the speed the player is going
1479 f32 player_speed = 0.001; // just some small value
1480 player_speed = lplayer->getSpeed().getLength();
1483 Maximum position increment
1485 //f32 position_max_increment = 0.05*BS;
1486 f32 position_max_increment = 0.1*BS;
1488 // Maximum time increment (for collision detection etc)
1489 // time = distance / speed
1490 f32 dtime_max_increment = position_max_increment / player_speed;
1492 // Maximum time increment is 10ms or lower
1493 if(dtime_max_increment > 0.01)
1494 dtime_max_increment = 0.01;
1496 // Don't allow overly huge dtime
1500 f32 dtime_downcount = dtime;
1503 Stuff that has a maximum time increment
1512 if(dtime_downcount > dtime_max_increment)
1514 dtime_part = dtime_max_increment;
1515 dtime_downcount -= dtime_part;
1519 dtime_part = dtime_downcount;
1521 Setting this to 0 (no -=dtime_part) disables an infinite loop
1522 when dtime_part is so small that dtime_downcount -= dtime_part
1525 dtime_downcount = 0;
1533 v3f lplayerpos = lplayer->getPosition();
1536 if(free_move == false)
1539 v3f speed = lplayer->getSpeed();
1540 if(lplayer->swimming_up == false)
1541 speed.Y -= 9.81 * BS * dtime_part * 2;
1544 if(lplayer->in_water_stable || lplayer->in_water)
1546 f32 max_down = 2.0*BS;
1547 if(speed.Y < -max_down) speed.Y = -max_down;
1550 if(speed.getLength() > max)
1552 speed = speed / speed.getLength() * max;
1556 lplayer->setSpeed(speed);
1561 This also does collision detection.
1563 lplayer->move(dtime_part, *m_map, position_max_increment,
1564 &player_collisions);
1567 while(dtime_downcount > 0.001);
1569 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1571 for(core::list<CollisionInfo>::Iterator
1572 i = player_collisions.begin();
1573 i != player_collisions.end(); i++)
1575 CollisionInfo &info = *i;
1576 if(info.t == COLLISION_FALL)
1578 //f32 tolerance = BS*10; // 2 without damage
1579 f32 tolerance = BS*12; // 3 without damage
1581 if(info.speed > tolerance)
1583 f32 damage_f = (info.speed - tolerance)/BS*factor;
1584 u16 damage = (u16)(damage_f+0.5);
1585 if(lplayer->hp > damage)
1586 lplayer->hp -= damage;
1590 ClientEnvEvent event;
1591 event.type = CEE_PLAYER_DAMAGE;
1592 event.player_damage.amount = damage;
1593 m_client_event_queue.push_back(event);
1599 Stuff that can be done in an arbitarily large dtime
1601 for(core::list<Player*>::Iterator i = m_players.begin();
1602 i != m_players.end(); i++)
1604 Player *player = *i;
1605 v3f playerpos = player->getPosition();
1608 Handle non-local players
1610 if(player->isLocal() == false)
1613 player->move(dtime, *m_map, 100*BS);
1615 // Update lighting on remote players on client
1616 u8 light = LIGHT_MAX;
1619 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1620 MapNode n = m_map->getNode(p);
1621 light = n.getLightBlend(getDayNightRatio());
1623 catch(InvalidPositionException &e) {}
1624 player->updateLight(light);
1628 Add footsteps to grass
1632 // Get node that is at BS/4 under player
1633 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1635 MapNode n = m_map->getNode(bottompos);
1636 if(n.d == CONTENT_GRASS)
1638 n.d = CONTENT_GRASS_FOOTSTEPS;
1639 m_map->setNode(bottompos, n);
1640 // Update mesh on client
1641 if(m_map->mapType() == MAPTYPE_CLIENT)
1643 v3s16 p_blocks = getNodeBlockPos(bottompos);
1644 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1645 //b->updateMesh(getDayNightRatio());
1646 b->setMeshExpired(true);
1650 catch(InvalidPositionException &e)
1657 Step active objects and update lighting of them
1660 for(core::map<u16, ClientActiveObject*>::Iterator
1661 i = m_active_objects.getIterator();
1662 i.atEnd()==false; i++)
1664 ClientActiveObject* obj = i.getNode()->getValue();
1666 obj->step(dtime, this);
1668 if(m_active_object_light_update_interval.step(dtime, 0.21))
1671 //u8 light = LIGHT_MAX;
1675 v3s16 p = obj->getLightPosition();
1676 MapNode n = m_map->getNode(p);
1677 light = n.getLightBlend(getDayNightRatio());
1679 catch(InvalidPositionException &e) {}
1680 obj->updateLight(light);
1685 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1687 m_map->updateMeshes(blockpos, getDayNightRatio());
1690 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1692 m_map->expireMeshes(only_daynight_diffed);
1695 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1697 core::map<u16, ClientActiveObject*>::Node *n;
1698 n = m_active_objects.find(id);
1701 return n->getValue();
1704 bool isFreeClientActiveObjectId(u16 id,
1705 core::map<u16, ClientActiveObject*> &objects)
1710 for(core::map<u16, ClientActiveObject*>::Iterator
1711 i = objects.getIterator();
1712 i.atEnd()==false; i++)
1714 if(i.getNode()->getKey() == id)
1720 u16 getFreeClientActiveObjectId(
1721 core::map<u16, ClientActiveObject*> &objects)
1726 if(isFreeClientActiveObjectId(new_id, objects))
1736 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1739 if(object->getId() == 0)
1741 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1744 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1745 <<"no free ids available"<<std::endl;
1749 object->setId(new_id);
1751 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1753 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1754 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1758 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1759 <<"added (id="<<object->getId()<<")"<<std::endl;
1760 m_active_objects.insert(object->getId(), object);
1761 object->addToScene(m_smgr);
1762 return object->getId();
1765 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1766 const std::string &init_data)
1768 ClientActiveObject* obj = ClientActiveObject::create(type);
1771 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1772 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1779 addActiveObject(obj);
1781 obj->initialize(init_data);
1784 void ClientEnvironment::removeActiveObject(u16 id)
1786 dstream<<"ClientEnvironment::removeActiveObject(): "
1787 <<"id="<<id<<std::endl;
1788 ClientActiveObject* obj = getActiveObject(id);
1791 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1792 <<"id="<<id<<" not found"<<std::endl;
1795 obj->removeFromScene();
1797 m_active_objects.remove(id);
1800 void ClientEnvironment::processActiveObjectMessage(u16 id,
1801 const std::string &data)
1803 ClientActiveObject* obj = getActiveObject(id);
1806 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1807 <<" got message for id="<<id<<", which doesn't exist."
1811 obj->processMessage(data);
1815 Callbacks for activeobjects
1818 void ClientEnvironment::damageLocalPlayer(u8 damage)
1820 LocalPlayer *lplayer = getLocalPlayer();
1823 if(lplayer->hp > damage)
1824 lplayer->hp -= damage;
1828 ClientEnvEvent event;
1829 event.type = CEE_PLAYER_DAMAGE;
1830 event.player_damage.amount = damage;
1831 m_client_event_queue.push_back(event);
1835 Client likes to call these
1838 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1839 core::array<DistanceSortedActiveObject> &dest)
1841 for(core::map<u16, ClientActiveObject*>::Iterator
1842 i = m_active_objects.getIterator();
1843 i.atEnd()==false; i++)
1845 ClientActiveObject* obj = i.getNode()->getValue();
1847 f32 d = (obj->getPosition() - origin).getLength();
1852 DistanceSortedActiveObject dso(obj, d);
1854 dest.push_back(dso);
1858 ClientEnvEvent ClientEnvironment::getClientEvent()
1860 if(m_client_event_queue.size() == 0)
1862 ClientEnvEvent event;
1863 event.type = CEE_NONE;
1866 return m_client_event_queue.pop_front();
1869 void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
1871 /*LocalPlayer *player = getLocalPlayer();
1873 v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
1874 v3f pos_f = camera_pos;
1875 v3s16 p_nodes = floatToInt(pos_f, BS);
1876 MapNode n = m_map->getNodeNoEx(p_nodes);
1877 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1879 v2u32 ss = driver->getScreenSize();
1880 core::rect<s32> rect(0,0, ss.X, ss.Y);
1881 driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
1885 #endif // #ifndef SERVER