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.getContent() == CONTENT_IGNORE)
548 if(content_features(n).liquid_type != LIQUID_NONE)
550 if(content_features(n).walkable)
552 last_node_walkable = true;
555 if(last_node_walkable)
557 // If block contains light information
558 if(content_features(n).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.getContent() == CONTENT_MUD)
631 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
632 if(content_features(n_top).air_equivalent &&
633 n_top.getLight(LIGHTBANK_DAY) >= 13)
635 n.setContent(CONTENT_GRASS);
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.getContent() == CONTENT_GRASS)
691 n.setContent(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
850 // Find out how many objects the block contains
851 u32 active_object_count = block->m_static_objects.m_active.size();
852 // Find out how many objects this and all the neighbors contain
853 u32 active_object_count_wider = 0;
854 for(s16 x=-1; x<=1; x++)
855 for(s16 y=-1; y<=1; y++)
856 for(s16 z=-1; z<=1; z++)
858 MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
861 active_object_count_wider +=
862 block->m_static_objects.m_active.size();
866 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
867 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
868 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
870 v3s16 p = p0 + block->getPosRelative();
871 MapNode n = block->getNodeNoEx(p0);
875 Convert mud under proper lighting to grass
877 if(n.getContent() == CONTENT_MUD)
881 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
882 if(content_features(n_top).air_equivalent &&
883 n_top.getLightBlend(getDayNightRatio()) >= 13)
885 n.setContent(CONTENT_GRASS);
886 m_map->addNodeWithEvent(p, n);
891 Convert grass into mud if under something else than air
893 if(n.getContent() == CONTENT_GRASS)
895 //if(myrand()%20 == 0)
897 MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
898 if(content_features(n_top).air_equivalent == false)
900 n.setContent(CONTENT_MUD);
901 m_map->addNodeWithEvent(p, n);
906 Rats spawn around regular trees
908 if(n.getContent() == CONTENT_TREE ||
909 n.getContent() == CONTENT_JUNGLETREE)
911 if(myrand()%200 == 0 && active_object_count_wider == 0)
913 v3s16 p1 = p + v3s16(myrand_range(-2, 2),
914 0, myrand_range(-2, 2));
915 MapNode n1 = m_map->getNodeNoEx(p1);
916 MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
917 if(n1b.getContent() == CONTENT_GRASS &&
918 n1.getContent() == CONTENT_AIR)
920 v3f pos = intToFloat(p1, BS);
921 ServerActiveObject *obj = new RatSAO(this, 0, pos);
922 addActiveObject(obj);
934 //TimeTaker timer("Step active objects");
936 // This helps the objects to send data at the same time
937 bool send_recommended = false;
938 m_send_recommended_timer += dtime;
939 if(m_send_recommended_timer > 0.15)
941 m_send_recommended_timer = 0;
942 send_recommended = true;
945 for(core::map<u16, ServerActiveObject*>::Iterator
946 i = m_active_objects.getIterator();
947 i.atEnd()==false; i++)
949 ServerActiveObject* obj = i.getNode()->getValue();
950 // Don't step if is to be removed or stored statically
951 if(obj->m_removed || obj->m_pending_deactivation)
954 obj->step(dtime, send_recommended);
955 // Read messages from object
956 while(obj->m_messages_out.size() > 0)
958 m_active_object_messages.push_back(
959 obj->m_messages_out.pop_front());
965 Manage active objects
967 if(m_object_management_interval.step(dtime, 0.5))
970 Remove objects that satisfy (m_removed && m_known_by_count==0)
972 removeRemovedObjects();
975 if(g_settings.getBool("enable_experimental"))
982 m_random_spawn_timer -= dtime;
983 if(m_random_spawn_timer < 0)
985 //m_random_spawn_timer += myrand_range(2.0, 20.0);
986 //m_random_spawn_timer += 2.0;
987 m_random_spawn_timer += 200.0;
993 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
994 s16 y = 1 + getServerMap().findGroundLevel(p2d);
995 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
997 Player *player = getRandomConnectedPlayer();
1000 pos = player->getPosition();
1002 myrand_range(-3,3)*BS,
1004 myrand_range(-3,3)*BS
1008 Create a ServerActiveObject
1011 //TestSAO *obj = new TestSAO(this, 0, pos);
1012 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1013 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1014 //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1015 ServerActiveObject *obj = new FireflySAO(this, 0, pos);
1016 addActiveObject(obj);
1020 } // enable_experimental
1023 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1025 core::map<u16, ServerActiveObject*>::Node *n;
1026 n = m_active_objects.find(id);
1029 return n->getValue();
1032 bool isFreeServerActiveObjectId(u16 id,
1033 core::map<u16, ServerActiveObject*> &objects)
1038 for(core::map<u16, ServerActiveObject*>::Iterator
1039 i = objects.getIterator();
1040 i.atEnd()==false; i++)
1042 if(i.getNode()->getKey() == id)
1048 u16 getFreeServerActiveObjectId(
1049 core::map<u16, ServerActiveObject*> &objects)
1054 if(isFreeServerActiveObjectId(new_id, objects))
1064 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1067 u16 id = addActiveObjectRaw(object, true);
1072 Finds out what new objects have been added to
1073 inside a radius around a position
1075 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1076 core::map<u16, bool> ¤t_objects,
1077 core::map<u16, bool> &added_objects)
1079 v3f pos_f = intToFloat(pos, BS);
1080 f32 radius_f = radius * BS;
1082 Go through the object list,
1083 - discard m_removed objects,
1084 - discard objects that are too far away,
1085 - discard objects that are found in current_objects.
1086 - add remaining objects to added_objects
1088 for(core::map<u16, ServerActiveObject*>::Iterator
1089 i = m_active_objects.getIterator();
1090 i.atEnd()==false; i++)
1092 u16 id = i.getNode()->getKey();
1094 ServerActiveObject *object = i.getNode()->getValue();
1097 // Discard if removed
1098 if(object->m_removed)
1100 // Discard if too far
1101 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1102 if(distance_f > radius_f)
1104 // Discard if already on current_objects
1105 core::map<u16, bool>::Node *n;
1106 n = current_objects.find(id);
1109 // Add to added_objects
1110 added_objects.insert(id, false);
1115 Finds out what objects have been removed from
1116 inside a radius around a position
1118 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1119 core::map<u16, bool> ¤t_objects,
1120 core::map<u16, bool> &removed_objects)
1122 v3f pos_f = intToFloat(pos, BS);
1123 f32 radius_f = radius * BS;
1125 Go through current_objects; object is removed if:
1126 - object is not found in m_active_objects (this is actually an
1127 error condition; objects should be set m_removed=true and removed
1128 only after all clients have been informed about removal), or
1129 - object has m_removed=true, or
1130 - object is too far away
1132 for(core::map<u16, bool>::Iterator
1133 i = current_objects.getIterator();
1134 i.atEnd()==false; i++)
1136 u16 id = i.getNode()->getKey();
1137 ServerActiveObject *object = getActiveObject(id);
1140 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1141 <<" object in current_objects is NULL"<<std::endl;
1143 else if(object->m_removed == false)
1145 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1146 /*dstream<<"removed == false"
1147 <<"distance_f = "<<distance_f
1148 <<", radius_f = "<<radius_f<<std::endl;*/
1149 if(distance_f < radius_f)
1155 removed_objects.insert(id, false);
1159 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1161 if(m_active_object_messages.size() == 0)
1162 return ActiveObjectMessage(0);
1164 return m_active_object_messages.pop_front();
1168 ************ Private methods *************
1171 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1175 if(object->getId() == 0)
1177 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1180 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1181 <<"no free ids available"<<std::endl;
1185 object->setId(new_id);
1187 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1189 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1190 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1194 /*dstream<<"INFO: ServerEnvironment::addActiveObjectRaw(): "
1195 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1197 m_active_objects.insert(object->getId(), object);
1199 // Add static object to active static list of the block
1200 v3f objectpos = object->getBasePosition();
1201 std::string staticdata = object->getStaticData();
1202 StaticObject s_obj(object->getType(), objectpos, staticdata);
1203 // Add to the block where the object is located in
1204 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1205 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1208 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1209 object->m_static_exists = true;
1210 object->m_static_block = blockpos;
1213 block->setChangedFlag();
1216 dstream<<"WARNING: ServerEnv: Could not find a block for "
1217 <<"storing newly added static active object"<<std::endl;
1220 return object->getId();
1224 Remove objects that satisfy (m_removed && m_known_by_count==0)
1226 void ServerEnvironment::removeRemovedObjects()
1228 core::list<u16> objects_to_remove;
1229 for(core::map<u16, ServerActiveObject*>::Iterator
1230 i = m_active_objects.getIterator();
1231 i.atEnd()==false; i++)
1233 u16 id = i.getNode()->getKey();
1234 ServerActiveObject* obj = i.getNode()->getValue();
1235 // This shouldn't happen but check it
1238 dstream<<"WARNING: NULL object found in ServerEnvironment"
1239 <<" while finding removed objects. id="<<id<<std::endl;
1240 // Id to be removed from m_active_objects
1241 objects_to_remove.push_back(id);
1246 We will delete objects that are marked as removed or thatare
1247 waiting for deletion after deactivation
1249 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1253 Delete static data from block if is marked as removed
1255 if(obj->m_static_exists && obj->m_removed)
1257 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1260 block->m_static_objects.remove(id);
1261 block->setChangedFlag();
1265 // If m_known_by_count > 0, don't actually remove.
1266 if(obj->m_known_by_count > 0)
1271 // Id to be removed from m_active_objects
1272 objects_to_remove.push_back(id);
1274 // Remove references from m_active_objects
1275 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1276 i != objects_to_remove.end(); i++)
1278 m_active_objects.remove(*i);
1283 Convert stored objects from blocks near the players to active.
1285 void ServerEnvironment::activateObjects(MapBlock *block)
1289 // Ignore if no stored objects (to not set changed flag)
1290 if(block->m_static_objects.m_stored.size() == 0)
1292 // A list for objects that couldn't be converted to static for some
1293 // reason. They will be stored back.
1294 core::list<StaticObject> new_stored;
1295 // Loop through stored static objects
1296 for(core::list<StaticObject>::Iterator
1297 i = block->m_static_objects.m_stored.begin();
1298 i != block->m_static_objects.m_stored.end(); i++)
1300 /*dstream<<"INFO: Server: Creating an active object from "
1301 <<"static data"<<std::endl;*/
1302 StaticObject &s_obj = *i;
1303 // Create an active object from the data
1304 ServerActiveObject *obj = ServerActiveObject::create
1305 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1306 // If couldn't create object, store static data back.
1309 new_stored.push_back(s_obj);
1312 // This will also add the object to the active static list
1313 addActiveObjectRaw(obj, false);
1314 //u16 id = addActiveObjectRaw(obj, false);
1316 // Clear stored list
1317 block->m_static_objects.m_stored.clear();
1318 // Add leftover failed stuff to stored list
1319 for(core::list<StaticObject>::Iterator
1320 i = new_stored.begin();
1321 i != new_stored.end(); i++)
1323 StaticObject &s_obj = *i;
1324 block->m_static_objects.m_stored.push_back(s_obj);
1326 // Block has been modified
1327 // NOTE: No it has not really. Save I/O here.
1328 //block->setChangedFlag();
1332 Convert objects that are not in active blocks to static.
1334 If m_known_by_count != 0, active object is not deleted, but static
1335 data is still updated.
1337 If force_delete is set, active object is deleted nevertheless. It
1338 shall only be set so in the destructor of the environment.
1340 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1342 core::list<u16> objects_to_remove;
1343 for(core::map<u16, ServerActiveObject*>::Iterator
1344 i = m_active_objects.getIterator();
1345 i.atEnd()==false; i++)
1347 ServerActiveObject* obj = i.getNode()->getValue();
1349 // This shouldn't happen but check it
1352 dstream<<"WARNING: NULL object found in ServerEnvironment"
1358 u16 id = i.getNode()->getKey();
1359 v3f objectpos = obj->getBasePosition();
1361 // The block in which the object resides in
1362 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1364 // If block is active, don't remove
1365 if(m_active_blocks.contains(blockpos_o))
1369 Update the static data
1372 // Delete old static object
1373 MapBlock *oldblock = NULL;
1374 if(obj->m_static_exists)
1376 MapBlock *block = m_map->getBlockNoCreateNoEx
1377 (obj->m_static_block);
1380 block->m_static_objects.remove(id);
1384 // Create new static object
1385 std::string staticdata = obj->getStaticData();
1386 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1387 // Add to the block where the object is located in
1388 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1389 // Get or generate the block
1390 MapBlock *block = m_map->emergeBlock(blockpos);
1392 /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1395 // Block not found. Is the old block still ok?
1398 // Load from disk or generate
1400 block = m_map->emergeBlock(blockpos);
1405 block->m_static_objects.insert(0, s_obj);
1406 block->setChangedFlag();
1407 obj->m_static_exists = true;
1408 obj->m_static_block = block->getPos();
1411 dstream<<"WARNING: ServerEnv: Could not find or generate "
1412 <<"a block for storing static object"<<std::endl;
1413 obj->m_static_exists = false;
1418 Delete active object if not known by some client,
1419 else set pending deactivation
1422 // If known by some client, don't delete.
1423 if(obj->m_known_by_count > 0 && force_delete == false)
1425 obj->m_pending_deactivation = true;
1429 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1431 // Delete active object
1433 // Id to be removed from m_active_objects
1434 objects_to_remove.push_back(id);
1437 // Remove references from m_active_objects
1438 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1439 i != objects_to_remove.end(); i++)
1441 m_active_objects.remove(*i);
1452 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1460 ClientEnvironment::~ClientEnvironment()
1462 // delete active objects
1463 for(core::map<u16, ClientActiveObject*>::Iterator
1464 i = m_active_objects.getIterator();
1465 i.atEnd()==false; i++)
1467 delete i.getNode()->getValue();
1474 void ClientEnvironment::addPlayer(Player *player)
1476 DSTACK(__FUNCTION_NAME);
1478 It is a failure if player is local and there already is a local
1481 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1483 Environment::addPlayer(player);
1486 LocalPlayer * ClientEnvironment::getLocalPlayer()
1488 for(core::list<Player*>::Iterator i = m_players.begin();
1489 i != m_players.end(); i++)
1491 Player *player = *i;
1492 if(player->isLocal())
1493 return (LocalPlayer*)player;
1498 void ClientEnvironment::step(float dtime)
1500 DSTACK(__FUNCTION_NAME);
1502 // Get some settings
1503 bool free_move = g_settings.getBool("free_move");
1504 bool footprints = g_settings.getBool("footprints");
1507 LocalPlayer *lplayer = getLocalPlayer();
1509 // collision info queue
1510 core::list<CollisionInfo> player_collisions;
1513 Get the speed the player is going
1515 bool is_climbing = lplayer->is_climbing;
1518 Check if the player is frozen (don't apply physics)
1520 bool is_frozen = lplayer->is_frozen;
1522 f32 player_speed = 0.001; // just some small value
1523 player_speed = lplayer->getSpeed().getLength();
1526 Maximum position increment
1528 //f32 position_max_increment = 0.05*BS;
1529 f32 position_max_increment = 0.1*BS;
1531 // Maximum time increment (for collision detection etc)
1532 // time = distance / speed
1533 f32 dtime_max_increment = position_max_increment / player_speed;
1535 // Maximum time increment is 10ms or lower
1536 if(dtime_max_increment > 0.01)
1537 dtime_max_increment = 0.01;
1539 // Don't allow overly huge dtime
1543 f32 dtime_downcount = dtime;
1546 Stuff that has a maximum time increment
1555 if(dtime_downcount > dtime_max_increment)
1557 dtime_part = dtime_max_increment;
1558 dtime_downcount -= dtime_part;
1562 dtime_part = dtime_downcount;
1564 Setting this to 0 (no -=dtime_part) disables an infinite loop
1565 when dtime_part is so small that dtime_downcount -= dtime_part
1568 dtime_downcount = 0;
1576 v3f lplayerpos = lplayer->getPosition();
1579 if(free_move == false && is_climbing == false && is_frozen == false)
1582 v3f speed = lplayer->getSpeed();
1583 if(lplayer->swimming_up == false)
1584 speed.Y -= 9.81 * BS * dtime_part * 2;
1587 if(lplayer->in_water_stable || lplayer->in_water)
1589 f32 max_down = 2.0*BS;
1590 if(speed.Y < -max_down) speed.Y = -max_down;
1593 if(speed.getLength() > max)
1595 speed = speed / speed.getLength() * max;
1599 lplayer->setSpeed(speed);
1604 This also does collision detection.
1606 lplayer->move(dtime_part, *m_map, position_max_increment,
1607 &player_collisions);
1610 while(dtime_downcount > 0.001);
1612 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1614 for(core::list<CollisionInfo>::Iterator
1615 i = player_collisions.begin();
1616 i != player_collisions.end(); i++)
1618 CollisionInfo &info = *i;
1619 if(info.t == COLLISION_FALL)
1621 //f32 tolerance = BS*10; // 2 without damage
1622 f32 tolerance = BS*12; // 3 without damage
1624 if(info.speed > tolerance)
1626 f32 damage_f = (info.speed - tolerance)/BS*factor;
1627 u16 damage = (u16)(damage_f+0.5);
1628 if(lplayer->hp > damage)
1629 lplayer->hp -= damage;
1633 ClientEnvEvent event;
1634 event.type = CEE_PLAYER_DAMAGE;
1635 event.player_damage.amount = damage;
1636 m_client_event_queue.push_back(event);
1642 A quick draft of lava damage
1644 if(m_lava_hurt_interval.step(dtime, 1.0))
1646 v3f pf = lplayer->getPosition();
1648 // Feet, middle and head
1649 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
1650 MapNode n1 = m_map->getNodeNoEx(p1);
1651 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
1652 MapNode n2 = m_map->getNodeNoEx(p2);
1653 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
1654 MapNode n3 = m_map->getNodeNoEx(p2);
1656 u32 damage_per_second = 0;
1657 damage_per_second = MYMAX(damage_per_second,
1658 content_features(n1).damage_per_second);
1659 damage_per_second = MYMAX(damage_per_second,
1660 content_features(n2).damage_per_second);
1661 damage_per_second = MYMAX(damage_per_second,
1662 content_features(n3).damage_per_second);
1664 if(damage_per_second != 0)
1666 ClientEnvEvent event;
1667 event.type = CEE_PLAYER_DAMAGE;
1668 event.player_damage.amount = damage_per_second;
1669 m_client_event_queue.push_back(event);
1674 Stuff that can be done in an arbitarily large dtime
1676 for(core::list<Player*>::Iterator i = m_players.begin();
1677 i != m_players.end(); i++)
1679 Player *player = *i;
1680 v3f playerpos = player->getPosition();
1683 Handle non-local players
1685 if(player->isLocal() == false)
1688 player->move(dtime, *m_map, 100*BS);
1690 // Update lighting on remote players on client
1691 u8 light = LIGHT_MAX;
1694 v3s16 p = player->getLightPosition();
1695 MapNode n = m_map->getNode(p);
1696 light = n.getLightBlend(getDayNightRatio());
1698 catch(InvalidPositionException &e) {}
1699 player->updateLight(light);
1703 Add footsteps to grass
1707 // Get node that is at BS/4 under player
1708 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1710 MapNode n = m_map->getNode(bottompos);
1711 if(n.getContent() == CONTENT_GRASS)
1713 n.setContent(CONTENT_GRASS_FOOTSTEPS);
1714 m_map->setNode(bottompos, n);
1715 // Update mesh on client
1716 if(m_map->mapType() == MAPTYPE_CLIENT)
1718 v3s16 p_blocks = getNodeBlockPos(bottompos);
1719 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1720 //b->updateMesh(getDayNightRatio());
1721 b->setMeshExpired(true);
1725 catch(InvalidPositionException &e)
1732 Step active objects and update lighting of them
1735 for(core::map<u16, ClientActiveObject*>::Iterator
1736 i = m_active_objects.getIterator();
1737 i.atEnd()==false; i++)
1739 ClientActiveObject* obj = i.getNode()->getValue();
1741 obj->step(dtime, this);
1743 if(m_active_object_light_update_interval.step(dtime, 0.21))
1746 //u8 light = LIGHT_MAX;
1750 v3s16 p = obj->getLightPosition();
1751 MapNode n = m_map->getNode(p);
1752 light = n.getLightBlend(getDayNightRatio());
1754 catch(InvalidPositionException &e) {}
1755 obj->updateLight(light);
1760 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1762 m_map->updateMeshes(blockpos, getDayNightRatio());
1765 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1767 m_map->expireMeshes(only_daynight_diffed);
1770 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1772 core::map<u16, ClientActiveObject*>::Node *n;
1773 n = m_active_objects.find(id);
1776 return n->getValue();
1779 bool isFreeClientActiveObjectId(u16 id,
1780 core::map<u16, ClientActiveObject*> &objects)
1785 for(core::map<u16, ClientActiveObject*>::Iterator
1786 i = objects.getIterator();
1787 i.atEnd()==false; i++)
1789 if(i.getNode()->getKey() == id)
1795 u16 getFreeClientActiveObjectId(
1796 core::map<u16, ClientActiveObject*> &objects)
1801 if(isFreeClientActiveObjectId(new_id, objects))
1811 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1814 if(object->getId() == 0)
1816 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1819 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1820 <<"no free ids available"<<std::endl;
1824 object->setId(new_id);
1826 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1828 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1829 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1833 dstream<<"INFO: ClientEnvironment::addActiveObject(): "
1834 <<"added (id="<<object->getId()<<")"<<std::endl;
1835 m_active_objects.insert(object->getId(), object);
1836 object->addToScene(m_smgr);
1837 return object->getId();
1840 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1841 const std::string &init_data)
1843 ClientActiveObject* obj = ClientActiveObject::create(type);
1846 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1847 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1854 addActiveObject(obj);
1856 obj->initialize(init_data);
1859 void ClientEnvironment::removeActiveObject(u16 id)
1861 dstream<<"ClientEnvironment::removeActiveObject(): "
1862 <<"id="<<id<<std::endl;
1863 ClientActiveObject* obj = getActiveObject(id);
1866 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1867 <<"id="<<id<<" not found"<<std::endl;
1870 obj->removeFromScene();
1872 m_active_objects.remove(id);
1875 void ClientEnvironment::processActiveObjectMessage(u16 id,
1876 const std::string &data)
1878 ClientActiveObject* obj = getActiveObject(id);
1881 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1882 <<" got message for id="<<id<<", which doesn't exist."
1886 obj->processMessage(data);
1890 Callbacks for activeobjects
1893 void ClientEnvironment::damageLocalPlayer(u8 damage)
1895 LocalPlayer *lplayer = getLocalPlayer();
1898 if(lplayer->hp > damage)
1899 lplayer->hp -= damage;
1903 ClientEnvEvent event;
1904 event.type = CEE_PLAYER_DAMAGE;
1905 event.player_damage.amount = damage;
1906 m_client_event_queue.push_back(event);
1910 Client likes to call these
1913 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1914 core::array<DistanceSortedActiveObject> &dest)
1916 for(core::map<u16, ClientActiveObject*>::Iterator
1917 i = m_active_objects.getIterator();
1918 i.atEnd()==false; i++)
1920 ClientActiveObject* obj = i.getNode()->getValue();
1922 f32 d = (obj->getPosition() - origin).getLength();
1927 DistanceSortedActiveObject dso(obj, d);
1929 dest.push_back(dso);
1933 ClientEnvEvent ClientEnvironment::getClientEvent()
1935 if(m_client_event_queue.size() == 0)
1937 ClientEnvEvent event;
1938 event.type = CEE_NONE;
1941 return m_client_event_queue.pop_front();
1944 void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos)
1946 /*LocalPlayer *player = getLocalPlayer();
1948 v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/
1949 v3f pos_f = camera_pos;
1950 v3s16 p_nodes = floatToInt(pos_f, BS);
1951 MapNode n = m_map->getNodeNoEx(p_nodes);
1952 if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
1954 v2u32 ss = driver->getScreenSize();
1955 core::rect<s32> rect(0,0, ss.X, ss.Y);
1956 driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect);
1958 else if(content_features(n).solidness == 2 &&
1959 g_settings.getBool("free_move") == false)
1961 v2u32 ss = driver->getScreenSize();
1962 core::rect<s32> rect(0,0, ss.X, ss.Y);
1963 driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect);
1967 #endif // #ifndef SERVER