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);
751 // 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 // Get time difference
775 u32 stamp = block->getTimestamp();
776 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
777 dtime_s = m_game_time - block->getTimestamp();
779 // Set current time as timestamp (and let it set ChangedFlag)
780 block->setTimestamp(m_game_time);
782 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
784 // Activate stored objects
785 activateObjects(block);
788 bool changed = block->m_node_metadata.step((float)dtime_s);
792 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
794 m_map->dispatchEvent(&event);
796 block->setChangedFlag();
799 // TODO: Do something
800 // TODO: Implement usage of ActiveBlockModifier
802 // Here's a quick demonstration
804 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
805 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
806 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
808 v3s16 p = p0 + block->getPosRelative();
809 MapNode n = block->getNodeNoEx(p0);
811 // Convert all mud under proper day lighting to grass
812 if(n.d == CONTENT_MUD)
816 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
817 if(content_features(n_top.d).air_equivalent &&
818 n_top.getLight(LIGHTBANK_DAY) >= 13)
821 m_map->addNodeWithEvent(p, n);
826 Convert grass into mud if under something else than air
828 else if(n.d == CONTENT_GRASS)
830 //if(myrand()%20 == 0)
832 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
833 if(n_top.d != CONTENT_AIR
834 && n_top.d != CONTENT_IGNORE)
837 m_map->addNodeWithEvent(p, n);
846 Mess around in active blocks
848 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
852 for(core::map<v3s16, bool>::Iterator
853 i = m_active_blocks.m_list.getIterator();
854 i.atEnd()==false; i++)
856 v3s16 p = i.getNode()->getKey();
858 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
859 <<") being handled"<<std::endl;*/
861 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
865 // Reset block usage timer
866 block->resetUsageTimer();
868 // Set current time as timestamp
869 block->setTimestampNoChangedFlag(m_game_time);
872 bool changed = block->m_node_metadata.step(dtime);
876 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
878 m_map->dispatchEvent(&event);
880 block->setChangedFlag();
884 if(m_active_blocks_test_interval.step(dtime, 10.0))
886 //float dtime = 10.0;
888 for(core::map<v3s16, bool>::Iterator
889 i = m_active_blocks.m_list.getIterator();
890 i.atEnd()==false; i++)
892 v3s16 p = i.getNode()->getKey();
894 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
895 <<") being handled"<<std::endl;*/
897 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
901 // Set current time as timestamp
902 block->setTimestampNoChangedFlag(m_game_time);
907 Note that map modifications should be done using the event-
908 making map methods so that the server gets information
911 Reading can be done quickly directly from the block.
913 Everything should bind to inside this single content
914 searching loop to keep things fast.
916 // TODO: Implement usage of ActiveBlockModifier
919 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
920 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
921 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
923 v3s16 p = p0 + block->getPosRelative();
924 MapNode n = block->getNodeNoEx(p0);
928 Convert mud under proper lighting to grass
930 if(n.d == CONTENT_MUD)
934 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
935 if(content_features(n_top.d).air_equivalent &&
936 n_top.getLightBlend(getDayNightRatio()) >= 13)
939 m_map->addNodeWithEvent(p, n);
944 Convert grass into mud if under something else than air
946 else if(n.d == CONTENT_GRASS)
948 //if(myrand()%20 == 0)
950 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
951 if(n_top.d != CONTENT_AIR
952 && n_top.d != CONTENT_IGNORE)
955 m_map->addNodeWithEvent(p, n);
967 //TimeTaker timer("Step active objects");
969 // This helps the objects to send data at the same time
970 bool send_recommended = false;
971 m_send_recommended_timer += dtime;
972 if(m_send_recommended_timer > 0.15)
974 m_send_recommended_timer = 0;
975 send_recommended = true;
978 for(core::map<u16, ServerActiveObject*>::Iterator
979 i = m_active_objects.getIterator();
980 i.atEnd()==false; i++)
982 ServerActiveObject* obj = i.getNode()->getValue();
983 // Don't step if is to be removed or stored statically
984 if(obj->m_removed || obj->m_pending_deactivation)
987 obj->step(dtime, send_recommended);
988 // Read messages from object
989 while(obj->m_messages_out.size() > 0)
991 m_active_object_messages.push_back(
992 obj->m_messages_out.pop_front());
998 Manage active objects
1000 if(m_object_management_interval.step(dtime, 0.5))
1003 Remove objects that satisfy (m_removed && m_known_by_count==0)
1005 removeRemovedObjects();
1008 if(g_settings.getBool("enable_experimental"))
1015 m_random_spawn_timer -= dtime;
1016 if(m_random_spawn_timer < 0)
1018 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1019 //m_random_spawn_timer += 2.0;
1020 m_random_spawn_timer += 200.0;
1026 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1027 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1028 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1030 Player *player = getRandomConnectedPlayer();
1033 pos = player->getPosition();
1035 myrand_range(-3,3)*BS,
1037 myrand_range(-3,3)*BS
1041 Create a ServerActiveObject
1044 //TestSAO *obj = new TestSAO(this, 0, pos);
1045 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1046 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1047 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1048 addActiveObject(obj);
1052 } // enable_experimental
1055 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1057 core::map<u16, ServerActiveObject*>::Node *n;
1058 n = m_active_objects.find(id);
1061 return n->getValue();
1064 bool isFreeServerActiveObjectId(u16 id,
1065 core::map<u16, ServerActiveObject*> &objects)
1070 for(core::map<u16, ServerActiveObject*>::Iterator
1071 i = objects.getIterator();
1072 i.atEnd()==false; i++)
1074 if(i.getNode()->getKey() == id)
1080 u16 getFreeServerActiveObjectId(
1081 core::map<u16, ServerActiveObject*> &objects)
1086 if(isFreeServerActiveObjectId(new_id, objects))
1096 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1099 u16 id = addActiveObjectRaw(object, true);
1104 Finds out what new objects have been added to
1105 inside a radius around a position
1107 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1108 core::map<u16, bool> ¤t_objects,
1109 core::map<u16, bool> &added_objects)
1111 v3f pos_f = intToFloat(pos, BS);
1112 f32 radius_f = radius * BS;
1114 Go through the object list,
1115 - discard m_removed objects,
1116 - discard objects that are too far away,
1117 - discard objects that are found in current_objects.
1118 - add remaining objects to added_objects
1120 for(core::map<u16, ServerActiveObject*>::Iterator
1121 i = m_active_objects.getIterator();
1122 i.atEnd()==false; i++)
1124 u16 id = i.getNode()->getKey();
1126 ServerActiveObject *object = i.getNode()->getValue();
1129 // Discard if removed
1130 if(object->m_removed)
1132 // Discard if too far
1133 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1134 if(distance_f > radius_f)
1136 // Discard if already on current_objects
1137 core::map<u16, bool>::Node *n;
1138 n = current_objects.find(id);
1141 // Add to added_objects
1142 added_objects.insert(id, false);
1147 Finds out what objects have been removed from
1148 inside a radius around a position
1150 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1151 core::map<u16, bool> ¤t_objects,
1152 core::map<u16, bool> &removed_objects)
1154 v3f pos_f = intToFloat(pos, BS);
1155 f32 radius_f = radius * BS;
1157 Go through current_objects; object is removed if:
1158 - object is not found in m_active_objects (this is actually an
1159 error condition; objects should be set m_removed=true and removed
1160 only after all clients have been informed about removal), or
1161 - object has m_removed=true, or
1162 - object is too far away
1164 for(core::map<u16, bool>::Iterator
1165 i = current_objects.getIterator();
1166 i.atEnd()==false; i++)
1168 u16 id = i.getNode()->getKey();
1169 ServerActiveObject *object = getActiveObject(id);
1172 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1173 <<" object in current_objects is NULL"<<std::endl;
1175 else if(object->m_removed == false)
1177 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1178 /*dstream<<"removed == false"
1179 <<"distance_f = "<<distance_f
1180 <<", radius_f = "<<radius_f<<std::endl;*/
1181 if(distance_f < radius_f)
1187 removed_objects.insert(id, false);
1191 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1193 if(m_active_object_messages.size() == 0)
1194 return ActiveObjectMessage(0);
1196 return m_active_object_messages.pop_front();
1200 ************ Private methods *************
1203 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1207 if(object->getId() == 0)
1209 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1212 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1213 <<"no free ids available"<<std::endl;
1217 object->setId(new_id);
1219 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1221 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1222 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1226 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1227 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1229 m_active_objects.insert(object->getId(), object);
1231 // Add static object to active static list of the block
1232 v3f objectpos = object->getBasePosition();
1233 std::string staticdata = object->getStaticData();
1234 StaticObject s_obj(object->getType(), objectpos, staticdata);
1235 // Add to the block where the object is located in
1236 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1237 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1240 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1241 object->m_static_exists = true;
1242 object->m_static_block = blockpos;
1245 block->setChangedFlag();
1248 dstream<<"WARNING: ServerEnv: Could not find a block for "
1249 <<"storing newly added static active object"<<std::endl;
1252 return object->getId();
1256 Remove objects that satisfy (m_removed && m_known_by_count==0)
1258 void ServerEnvironment::removeRemovedObjects()
1260 core::list<u16> objects_to_remove;
1261 for(core::map<u16, ServerActiveObject*>::Iterator
1262 i = m_active_objects.getIterator();
1263 i.atEnd()==false; i++)
1265 u16 id = i.getNode()->getKey();
1266 ServerActiveObject* obj = i.getNode()->getValue();
1267 // This shouldn't happen but check it
1270 dstream<<"WARNING: NULL object found in ServerEnvironment"
1271 <<" while finding removed objects. id="<<id<<std::endl;
1272 // Id to be removed from m_active_objects
1273 objects_to_remove.push_back(id);
1278 We will delete objects that are marked as removed or thatare
1279 waiting for deletion after deactivation
1281 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1285 Delete static data from block if is marked as removed
1287 if(obj->m_static_exists && obj->m_removed)
1289 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1292 block->m_static_objects.remove(id);
1293 block->setChangedFlag();
1297 // If m_known_by_count > 0, don't actually remove.
1298 if(obj->m_known_by_count > 0)
1303 // Id to be removed from m_active_objects
1304 objects_to_remove.push_back(id);
1306 // Remove references from m_active_objects
1307 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1308 i != objects_to_remove.end(); i++)
1310 m_active_objects.remove(*i);
1315 Convert stored objects from blocks near the players to active.
1317 void ServerEnvironment::activateObjects(MapBlock *block)
1321 // Ignore if no stored objects (to not set changed flag)
1322 if(block->m_static_objects.m_stored.size() == 0)
1324 // A list for objects that couldn't be converted to static for some
1325 // reason. They will be stored back.
1326 core::list<StaticObject> new_stored;
1327 // Loop through stored static objects
1328 for(core::list<StaticObject>::Iterator
1329 i = block->m_static_objects.m_stored.begin();
1330 i != block->m_static_objects.m_stored.end(); i++)
1332 /*dstream<<"INFO: Server: Creating an active object from "
1333 <<"static data"<<std::endl;*/
1334 StaticObject &s_obj = *i;
1335 // Create an active object from the data
1336 ServerActiveObject *obj = ServerActiveObject::create
1337 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1338 // If couldn't create object, store static data back.
1341 new_stored.push_back(s_obj);
1344 // This will also add the object to the active static list
1345 addActiveObjectRaw(obj, false);
1346 //u16 id = addActiveObjectRaw(obj, false);
1348 // Clear stored list
1349 block->m_static_objects.m_stored.clear();
1350 // Add leftover failed stuff to stored list
1351 for(core::list<StaticObject>::Iterator
1352 i = new_stored.begin();
1353 i != new_stored.end(); i++)
1355 StaticObject &s_obj = *i;
1356 block->m_static_objects.m_stored.push_back(s_obj);
1358 // Block has been modified
1359 // NOTE: No it has not really. Save I/O here.
1360 //block->setChangedFlag();
1364 Convert objects that are not in active blocks to static.
1366 If m_known_by_count != 0, active object is not deleted, but static
1367 data is still updated.
1369 If force_delete is set, active object is deleted nevertheless. It
1370 shall only be set so in the destructor of the environment.
1372 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1374 core::list<u16> objects_to_remove;
1375 for(core::map<u16, ServerActiveObject*>::Iterator
1376 i = m_active_objects.getIterator();
1377 i.atEnd()==false; i++)
1379 ServerActiveObject* obj = i.getNode()->getValue();
1380 u16 id = i.getNode()->getKey();
1381 v3f objectpos = obj->getBasePosition();
1383 // This shouldn't happen but check it
1386 dstream<<"WARNING: NULL object found in ServerEnvironment"
1392 // The block in which the object resides in
1393 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1395 // If block is active, don't remove
1396 if(m_active_blocks.contains(blockpos_o))
1400 Update the static data
1403 // Delete old static object
1404 MapBlock *oldblock = NULL;
1405 if(obj->m_static_exists)
1407 MapBlock *block = m_map->getBlockNoCreateNoEx
1408 (obj->m_static_block);
1411 block->m_static_objects.remove(id);
1415 // Create new static object
1416 std::string staticdata = obj->getStaticData();
1417 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1418 // Add to the block where the object is located in
1419 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1420 // Get or generate the block
1421 MapBlock *block = m_map->emergeBlock(blockpos);
1423 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1426 // Block not found. Is the old block still ok?
1429 // Load from disk or generate
1431 block = m_map->emergeBlock(blockpos);
1436 block->m_static_objects.insert(0, s_obj);
1437 block->setChangedFlag();
1438 obj->m_static_exists = true;
1439 obj->m_static_block = block->getPos();
1442 dstream<<"WARNING: ServerEnv: Could not find or generate "
1443 <<"a block for storing static object"<<std::endl;
1444 obj->m_static_exists = false;
1449 Delete active object if not known by some client,
1450 else set pending deactivation
1453 // If known by some client, don't delete.
1454 if(obj->m_known_by_count > 0 && force_delete == false)
1456 obj->m_pending_deactivation = true;
1460 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1462 // Delete active object
1464 // Id to be removed from m_active_objects
1465 objects_to_remove.push_back(id);
1468 // Remove references from m_active_objects
1469 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1470 i != objects_to_remove.end(); i++)
1472 m_active_objects.remove(*i);
1483 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1491 ClientEnvironment::~ClientEnvironment()
1493 // delete active objects
1494 for(core::map<u16, ClientActiveObject*>::Iterator
1495 i = m_active_objects.getIterator();
1496 i.atEnd()==false; i++)
1498 delete i.getNode()->getValue();
1505 void ClientEnvironment::addPlayer(Player *player)
1507 DSTACK(__FUNCTION_NAME);
1509 It is a failure if player is local and there already is a local
1512 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1514 Environment::addPlayer(player);
1517 LocalPlayer * ClientEnvironment::getLocalPlayer()
1519 for(core::list<Player*>::Iterator i = m_players.begin();
1520 i != m_players.end(); i++)
1522 Player *player = *i;
1523 if(player->isLocal())
1524 return (LocalPlayer*)player;
1529 void ClientEnvironment::step(float dtime)
1531 DSTACK(__FUNCTION_NAME);
1533 // Get some settings
1534 bool free_move = g_settings.getBool("free_move");
1535 bool footprints = g_settings.getBool("footprints");
1538 LocalPlayer *lplayer = getLocalPlayer();
1540 // collision info queue
1541 core::list<CollisionInfo> player_collisions;
1544 Get the speed the player is going
1546 f32 player_speed = 0.001; // just some small value
1547 player_speed = lplayer->getSpeed().getLength();
1550 Maximum position increment
1552 //f32 position_max_increment = 0.05*BS;
1553 f32 position_max_increment = 0.1*BS;
1555 // Maximum time increment (for collision detection etc)
1556 // time = distance / speed
1557 f32 dtime_max_increment = position_max_increment / player_speed;
1559 // Maximum time increment is 10ms or lower
1560 if(dtime_max_increment > 0.01)
1561 dtime_max_increment = 0.01;
1563 // Don't allow overly huge dtime
1567 f32 dtime_downcount = dtime;
1570 Stuff that has a maximum time increment
1579 if(dtime_downcount > dtime_max_increment)
1581 dtime_part = dtime_max_increment;
1582 dtime_downcount -= dtime_part;
1586 dtime_part = dtime_downcount;
1588 Setting this to 0 (no -=dtime_part) disables an infinite loop
1589 when dtime_part is so small that dtime_downcount -= dtime_part
1592 dtime_downcount = 0;
1600 v3f lplayerpos = lplayer->getPosition();
1603 if(free_move == false)
1606 v3f speed = lplayer->getSpeed();
1607 if(lplayer->swimming_up == false)
1608 speed.Y -= 9.81 * BS * dtime_part * 2;
1611 if(lplayer->in_water_stable || lplayer->in_water)
1613 f32 max_down = 2.0*BS;
1614 if(speed.Y < -max_down) speed.Y = -max_down;
1617 if(speed.getLength() > max)
1619 speed = speed / speed.getLength() * max;
1623 lplayer->setSpeed(speed);
1628 This also does collision detection.
1630 lplayer->move(dtime_part, *m_map, position_max_increment,
1631 &player_collisions);
1634 while(dtime_downcount > 0.001);
1636 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1638 for(core::list<CollisionInfo>::Iterator
1639 i = player_collisions.begin();
1640 i != player_collisions.end(); i++)
1642 CollisionInfo &info = *i;
1643 if(info.t == COLLISION_FALL)
1645 //f32 tolerance = BS*10; // 2 without damage
1646 f32 tolerance = BS*12; // 3 without damage
1648 if(info.speed > tolerance)
1650 f32 damage_f = (info.speed - tolerance)/BS*factor;
1651 u16 damage = (u16)(damage_f+0.5);
1652 if(lplayer->hp > damage)
1653 lplayer->hp -= damage;
1657 ClientEnvEvent event;
1658 event.type = CEE_PLAYER_DAMAGE;
1659 event.player_damage.amount = damage;
1660 m_client_event_queue.push_back(event);
1666 Stuff that can be done in an arbitarily large dtime
1668 for(core::list<Player*>::Iterator i = m_players.begin();
1669 i != m_players.end(); i++)
1671 Player *player = *i;
1672 v3f playerpos = player->getPosition();
1675 Handle non-local players
1677 if(player->isLocal() == false)
1680 player->move(dtime, *m_map, 100*BS);
1682 // Update lighting on remote players on client
1683 u8 light = LIGHT_MAX;
1686 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1687 MapNode n = m_map->getNode(p);
1688 light = n.getLightBlend(getDayNightRatio());
1690 catch(InvalidPositionException &e) {}
1691 player->updateLight(light);
1695 Add footsteps to grass
1699 // Get node that is at BS/4 under player
1700 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1702 MapNode n = m_map->getNode(bottompos);
1703 if(n.d == CONTENT_GRASS)
1705 n.d = CONTENT_GRASS_FOOTSTEPS;
1706 m_map->setNode(bottompos, n);
1707 // Update mesh on client
1708 if(m_map->mapType() == MAPTYPE_CLIENT)
1710 v3s16 p_blocks = getNodeBlockPos(bottompos);
1711 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1712 //b->updateMesh(getDayNightRatio());
1713 b->setMeshExpired(true);
1717 catch(InvalidPositionException &e)
1724 Step active objects and update lighting of them
1727 for(core::map<u16, ClientActiveObject*>::Iterator
1728 i = m_active_objects.getIterator();
1729 i.atEnd()==false; i++)
1731 ClientActiveObject* obj = i.getNode()->getValue();
1733 obj->step(dtime, this);
1735 if(m_active_object_light_update_interval.step(dtime, 0.21))
1738 //u8 light = LIGHT_MAX;
1742 v3s16 p = obj->getLightPosition();
1743 MapNode n = m_map->getNode(p);
1744 light = n.getLightBlend(getDayNightRatio());
1746 catch(InvalidPositionException &e) {}
1747 obj->updateLight(light);
1752 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1754 m_map->updateMeshes(blockpos, getDayNightRatio());
1757 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1759 m_map->expireMeshes(only_daynight_diffed);
1762 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1764 core::map<u16, ClientActiveObject*>::Node *n;
1765 n = m_active_objects.find(id);
1768 return n->getValue();
1771 bool isFreeClientActiveObjectId(u16 id,
1772 core::map<u16, ClientActiveObject*> &objects)
1777 for(core::map<u16, ClientActiveObject*>::Iterator
1778 i = objects.getIterator();
1779 i.atEnd()==false; i++)
1781 if(i.getNode()->getKey() == id)
1787 u16 getFreeClientActiveObjectId(
1788 core::map<u16, ClientActiveObject*> &objects)
1793 if(isFreeClientActiveObjectId(new_id, objects))
1803 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1806 if(object->getId() == 0)
1808 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1811 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1812 <<"no free ids available"<<std::endl;
1816 object->setId(new_id);
1818 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1820 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1821 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1825 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1826 <<"added (id="<<object->getId()<<")"<<std::endl;
1827 m_active_objects.insert(object->getId(), object);
1828 object->addToScene(m_smgr);
1829 return object->getId();
1832 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1833 const std::string &init_data)
1835 ClientActiveObject* obj = ClientActiveObject::create(type);
1838 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1839 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1846 addActiveObject(obj);
1848 obj->initialize(init_data);
1851 void ClientEnvironment::removeActiveObject(u16 id)
1853 dstream<<"ClientEnvironment::removeActiveObject(): "
1854 <<"id="<<id<<std::endl;
1855 ClientActiveObject* obj = getActiveObject(id);
1858 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1859 <<"id="<<id<<" not found"<<std::endl;
1862 obj->removeFromScene();
1864 m_active_objects.remove(id);
1867 void ClientEnvironment::processActiveObjectMessage(u16 id,
1868 const std::string &data)
1870 ClientActiveObject* obj = getActiveObject(id);
1873 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1874 <<" got message for id="<<id<<", which doesn't exist."
1878 obj->processMessage(data);
1882 Callbacks for activeobjects
1885 void ClientEnvironment::damageLocalPlayer(u8 damage)
1887 LocalPlayer *lplayer = getLocalPlayer();
1890 if(lplayer->hp > damage)
1891 lplayer->hp -= damage;
1895 ClientEnvEvent event;
1896 event.type = CEE_PLAYER_DAMAGE;
1897 event.player_damage.amount = damage;
1898 m_client_event_queue.push_back(event);
1902 Client likes to call these
1905 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1906 core::array<DistanceSortedActiveObject> &dest)
1908 for(core::map<u16, ClientActiveObject*>::Iterator
1909 i = m_active_objects.getIterator();
1910 i.atEnd()==false; i++)
1912 ClientActiveObject* obj = i.getNode()->getValue();
1914 f32 d = (obj->getPosition() - origin).getLength();
1919 DistanceSortedActiveObject dso(obj, d);
1921 dest.push_back(dso);
1925 ClientEnvEvent ClientEnvironment::getClientEvent()
1927 if(m_client_event_queue.size() == 0)
1929 ClientEnvEvent event;
1930 event.type = CEE_NONE;
1933 return m_client_event_queue.pop_front();
1936 void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
1938 /*LocalPlayer *player = getLocalPlayer();
1940 v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
1941 v3f pos_f = camera_pos;
1942 v3s16 p_nodes = floatToInt(pos_f, BS);
1943 MapNode n = m_map->getNodeNoEx(p_nodes);
1944 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1946 v2u32 ss = driver->getScreenSize();
1947 core::rect<s32> rect(0,0, ss.X, ss.Y);
1948 driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
1952 #endif // #ifndef SERVER