3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
27 Environment::Environment():
32 Environment::~Environment()
35 for(core::list<Player*>::Iterator i = m_players.begin();
36 i != m_players.end(); i++)
42 void Environment::addPlayer(Player *player)
44 DSTACK(__FUNCTION_NAME);
46 Check that peer_ids are unique.
47 Also check that names are unique.
48 Exception: there can be multiple players with peer_id=0
50 // If peer id is non-zero, it has to be unique.
51 if(player->peer_id != 0)
52 assert(getPlayer(player->peer_id) == NULL);
53 // Name has to be unique.
54 assert(getPlayer(player->getName()) == NULL);
56 m_players.push_back(player);
59 void Environment::removePlayer(u16 peer_id)
61 DSTACK(__FUNCTION_NAME);
63 for(core::list<Player*>::Iterator i = m_players.begin();
64 i != m_players.end(); i++)
67 if(player->peer_id != peer_id)
72 // See if there is an another one
73 // (shouldn't be, but just to be sure)
78 Player * Environment::getPlayer(u16 peer_id)
80 for(core::list<Player*>::Iterator i = m_players.begin();
81 i != m_players.end(); i++)
84 if(player->peer_id == peer_id)
90 Player * Environment::getPlayer(const char *name)
92 for(core::list<Player*>::Iterator i = m_players.begin();
93 i != m_players.end(); i++)
96 if(strcmp(player->getName(), name) == 0)
102 Player * Environment::getRandomConnectedPlayer()
104 core::list<Player*> connected_players = getPlayers(true);
105 u32 chosen_one = myrand() % connected_players.size();
107 for(core::list<Player*>::Iterator
108 i = connected_players.begin();
109 i != connected_players.end(); i++)
121 Player * Environment::getNearestConnectedPlayer(v3f pos)
123 core::list<Player*> connected_players = getPlayers(true);
125 Player *nearest_player = NULL;
126 for(core::list<Player*>::Iterator
127 i = connected_players.begin();
128 i != connected_players.end(); i++)
131 f32 d = player->getPosition().getDistanceFrom(pos);
132 if(d < nearest_d || nearest_player == NULL)
135 nearest_player = player;
138 return nearest_player;
141 core::list<Player*> Environment::getPlayers()
146 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
148 core::list<Player*> newlist;
149 for(core::list<Player*>::Iterator
150 i = m_players.begin();
151 i != m_players.end(); i++)
155 if(ignore_disconnected)
157 // Ignore disconnected players
158 if(player->peer_id == 0)
162 newlist.push_back(player);
167 void Environment::printPlayers(std::ostream &o)
169 o<<"Players in environment:"<<std::endl;
170 for(core::list<Player*>::Iterator i = m_players.begin();
171 i != m_players.end(); i++)
174 o<<"Player peer_id="<<player->peer_id<<std::endl;
178 /*void Environment::setDayNightRatio(u32 r)
180 getDayNightRatio() = r;
183 u32 Environment::getDayNightRatio()
185 //return getDayNightRatio();
186 return time_to_daynight_ratio(m_time_of_day);
193 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
196 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
197 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
198 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
205 void ActiveBlockList::update(core::list<v3s16> &active_positions,
207 core::map<v3s16, bool> &blocks_removed,
208 core::map<v3s16, bool> &blocks_added)
213 core::map<v3s16, bool> newlist;
214 for(core::list<v3s16>::Iterator i = active_positions.begin();
215 i != active_positions.end(); i++)
217 fillRadiusBlock(*i, radius, newlist);
221 Find out which blocks on the old list are not on the new list
223 // Go through old list
224 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
225 i.atEnd()==false; i++)
227 v3s16 p = i.getNode()->getKey();
228 // If not on new list, it's been removed
229 if(newlist.find(p) == NULL)
230 blocks_removed.insert(p, true);
234 Find out which blocks on the new list are not on the old list
236 // Go through new list
237 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
238 i.atEnd()==false; i++)
240 v3s16 p = i.getNode()->getKey();
241 // If not on old list, it's been added
242 if(m_list.find(p) == NULL)
243 blocks_added.insert(p, true);
250 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
251 i.atEnd()==false; i++)
253 v3s16 p = i.getNode()->getKey();
254 m_list.insert(p, true);
262 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
265 m_random_spawn_timer(3),
266 m_send_recommended_timer(0),
268 m_game_time_fraction_counter(0)
272 ServerEnvironment::~ServerEnvironment()
274 // Clear active block list.
275 // This makes the next one delete all active objects.
276 m_active_blocks.clear();
278 // Convert all objects to static and delete the active objects
279 deactivateFarObjects(true);
285 void ServerEnvironment::serializePlayers(const std::string &savedir)
287 std::string players_path = savedir + "/players";
288 fs::CreateDir(players_path);
290 core::map<Player*, bool> saved_players;
292 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
293 for(u32 i=0; i<player_files.size(); i++)
295 if(player_files[i].dir)
298 // Full path to this file
299 std::string path = players_path + "/" + player_files[i].name;
301 //dstream<<"Checking player file "<<path<<std::endl;
303 // Load player to see what is its name
304 ServerRemotePlayer testplayer;
306 // Open file and deserialize
307 std::ifstream is(path.c_str(), std::ios_base::binary);
308 if(is.good() == false)
310 dstream<<"Failed to read "<<path<<std::endl;
313 testplayer.deSerialize(is);
316 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
318 // Search for the player
319 std::string playername = testplayer.getName();
320 Player *player = getPlayer(playername.c_str());
323 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
327 //dstream<<"Found matching player, overwriting."<<std::endl;
329 // OK, found. Save player there.
331 // Open file and serialize
332 std::ofstream os(path.c_str(), std::ios_base::binary);
333 if(os.good() == false)
335 dstream<<"Failed to overwrite "<<path<<std::endl;
338 player->serialize(os);
339 saved_players.insert(player, true);
343 for(core::list<Player*>::Iterator i = m_players.begin();
344 i != m_players.end(); i++)
347 if(saved_players.find(player) != NULL)
349 /*dstream<<"Player "<<player->getName()
350 <<" was already saved."<<std::endl;*/
353 std::string playername = player->getName();
354 // Don't save unnamed player
357 //dstream<<"Not saving unnamed player."<<std::endl;
363 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
364 playername = "player";
365 std::string path = players_path + "/" + playername;
367 for(u32 i=0; i<1000; i++)
369 if(fs::PathExists(path) == false)
374 path = players_path + "/" + playername + itos(i);
378 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
383 /*dstream<<"Saving player "<<player->getName()<<" to "
385 // Open file and serialize
386 std::ofstream os(path.c_str(), std::ios_base::binary);
387 if(os.good() == false)
389 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
392 player->serialize(os);
393 saved_players.insert(player, true);
397 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
400 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
402 std::string players_path = savedir + "/players";
404 core::map<Player*, bool> saved_players;
406 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
407 for(u32 i=0; i<player_files.size(); i++)
409 if(player_files[i].dir)
412 // Full path to this file
413 std::string path = players_path + "/" + player_files[i].name;
415 dstream<<"Checking player file "<<path<<std::endl;
417 // Load player to see what is its name
418 ServerRemotePlayer testplayer;
420 // Open file and deserialize
421 std::ifstream is(path.c_str(), std::ios_base::binary);
422 if(is.good() == false)
424 dstream<<"Failed to read "<<path<<std::endl;
427 testplayer.deSerialize(is);
430 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
432 dstream<<"Not loading player with invalid name: "
433 <<testplayer.getName()<<std::endl;
436 dstream<<"Loaded test player with name "<<testplayer.getName()
439 // Search for the player
440 std::string playername = testplayer.getName();
441 Player *player = getPlayer(playername.c_str());
442 bool newplayer = false;
445 dstream<<"Is a new player"<<std::endl;
446 player = new ServerRemotePlayer();
452 dstream<<"Reading player "<<testplayer.getName()<<" from "
454 // Open file and deserialize
455 std::ifstream is(path.c_str(), std::ios_base::binary);
456 if(is.good() == false)
458 dstream<<"Failed to read "<<path<<std::endl;
461 player->deSerialize(is);
469 void ServerEnvironment::saveMeta(const std::string &savedir)
471 std::string path = savedir + "/env_meta.txt";
473 // Open file and serialize
474 std::ofstream os(path.c_str(), std::ios_base::binary);
475 if(os.good() == false)
477 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
479 throw SerializationError("Couldn't save env meta");
483 args.setU64("game_time", m_game_time);
484 args.setU64("time_of_day", getTimeOfDay());
489 void ServerEnvironment::loadMeta(const std::string &savedir)
491 std::string path = savedir + "/env_meta.txt";
493 // Open file and deserialize
494 std::ifstream is(path.c_str(), std::ios_base::binary);
495 if(is.good() == false)
497 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
499 throw SerializationError("Couldn't load env meta");
507 throw SerializationError
508 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
510 std::getline(is, line);
511 std::string trimmedline = trim(line);
512 if(trimmedline == "EnvArgsEnd")
514 args.parseConfigLine(line);
518 m_game_time = args.getU64("game_time");
519 }catch(SettingNotFoundException &e){
520 // Getting this is crucial, otherwise timestamps are useless
521 throw SerializationError("Couldn't load env meta game_time");
525 m_time_of_day = args.getU64("time_of_day");
526 }catch(SettingNotFoundException &e){
527 // This is not as important
528 m_time_of_day = 9000;
533 // This is probably very useless
534 void spawnRandomObjects(MapBlock *block)
536 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
537 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
539 bool last_node_walkable = false;
540 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
543 MapNode n = block->getNodeNoEx(p);
544 if(n.d == CONTENT_IGNORE)
546 if(content_features(n.d).liquid_type != LIQUID_NONE)
548 if(content_features(n.d).walkable)
550 last_node_walkable = true;
553 if(last_node_walkable)
555 // If block contains light information
556 if(content_features(n.d).param_type == CPT_LIGHT)
558 if(n.getLight(LIGHTBANK_DAY) <= 5)
560 if(myrand() % 1000 == 0)
562 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
564 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
565 std::string data = obj->getStaticData();
566 StaticObject s_obj(obj->getType(),
567 obj->getBasePosition(), data);
569 block->m_static_objects.insert(0, s_obj);
571 block->setChangedFlag();
576 last_node_walkable = false;
582 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
584 // Get time difference
586 u32 stamp = block->getTimestamp();
587 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
588 dtime_s = m_game_time - block->getTimestamp();
589 dtime_s += additional_dtime;
591 // Set current time as timestamp (and let it set ChangedFlag)
592 block->setTimestamp(m_game_time);
594 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
596 // Activate stored objects
597 activateObjects(block);
600 bool changed = block->m_node_metadata.step((float)dtime_s);
604 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
605 event.p = block->getPos();
606 m_map->dispatchEvent(&event);
608 block->setChangedFlag();
611 // TODO: Do something
612 // TODO: Implement usage of ActiveBlockModifier
614 // Here's a quick demonstration
616 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
617 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
618 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
620 v3s16 p = p0 + block->getPosRelative();
621 MapNode n = block->getNodeNoEx(p0);
624 // Convert all mud under proper day lighting to grass
625 if(n.d == CONTENT_MUD)
629 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
630 if(content_features(n_top.d).air_equivalent &&
631 n_top.getLight(LIGHTBANK_DAY) >= 13)
634 m_map->addNodeWithEvent(p, n);
642 void ServerEnvironment::step(float dtime)
644 DSTACK(__FUNCTION_NAME);
646 //TimeTaker timer("ServerEnv step");
649 bool footprints = g_settings.getBool("footprints");
655 m_game_time_fraction_counter += dtime;
656 u32 inc_i = (u32)m_game_time_fraction_counter;
657 m_game_time += inc_i;
658 m_game_time_fraction_counter -= (float)inc_i;
662 Let map update it's timers
665 //TimeTaker timer("Server m_map->timerUpdate()");
666 m_map->timerUpdate(dtime);
672 for(core::list<Player*>::Iterator i = m_players.begin();
673 i != m_players.end(); i++)
677 // Ignore disconnected players
678 if(player->peer_id == 0)
681 v3f playerpos = player->getPosition();
684 player->move(dtime, *m_map, 100*BS);
687 Add footsteps to grass
691 // Get node that is at BS/4 under player
692 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
694 MapNode n = m_map->getNode(bottompos);
695 if(n.d == CONTENT_GRASS)
697 n.d = CONTENT_GRASS_FOOTSTEPS;
698 m_map->setNode(bottompos, n);
701 catch(InvalidPositionException &e)
708 Manage active block list
710 if(m_active_blocks_management_interval.step(dtime, 2.0))
713 Get player block positions
715 core::list<v3s16> players_blockpos;
716 for(core::list<Player*>::Iterator
717 i = m_players.begin();
718 i != m_players.end(); i++)
721 // Ignore disconnected players
722 if(player->peer_id == 0)
724 v3s16 blockpos = getNodeBlockPos(
725 floatToInt(player->getPosition(), BS));
726 players_blockpos.push_back(blockpos);
730 Update list of active blocks, collecting changes
732 const s16 active_block_range = 5;
733 core::map<v3s16, bool> blocks_removed;
734 core::map<v3s16, bool> blocks_added;
735 m_active_blocks.update(players_blockpos, active_block_range,
736 blocks_removed, blocks_added);
739 Handle removed blocks
742 // Convert active objects that are no more in active blocks to static
743 deactivateFarObjects(false);
745 for(core::map<v3s16, bool>::Iterator
746 i = blocks_removed.getIterator();
747 i.atEnd()==false; i++)
749 v3s16 p = i.getNode()->getKey();
751 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
752 <<") became inactive"<<std::endl;*/
754 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
757 // Set current time as timestamp (and let it set ChangedFlag)
759 block->setTimestamp(m_game_time);
766 for(core::map<v3s16, bool>::Iterator
767 i = blocks_added.getIterator();
768 i.atEnd()==false; i++)
770 v3s16 p = i.getNode()->getKey();
772 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
773 <<") became active"<<std::endl;*/
775 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
779 // Get time difference
781 u32 stamp = block->getTimestamp();
782 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
783 dtime_s = m_game_time - block->getTimestamp();
785 // Set current time as timestamp (and let it set ChangedFlag)
786 block->setTimestamp(m_game_time);
788 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
790 // Activate stored objects
791 activateObjects(block);
794 bool changed = block->m_node_metadata.step((float)dtime_s);
798 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
800 m_map->dispatchEvent(&event);
802 block->setChangedFlag();
805 // TODO: Do something
806 // TODO: Implement usage of ActiveBlockModifier
808 // Here's a quick demonstration
810 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
811 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
812 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
814 v3s16 p = p0 + block->getPosRelative();
815 MapNode n = block->getNodeNoEx(p0);
817 // Convert all mud under proper day lighting to grass
818 if(n.d == CONTENT_MUD)
822 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
823 if(content_features(n_top.d).air_equivalent &&
824 n_top.getLight(LIGHTBANK_DAY) >= 13)
827 m_map->addNodeWithEvent(p, n);
832 Convert grass into mud if under something else than air
834 else if(n.d == CONTENT_GRASS)
836 //if(myrand()%20 == 0)
838 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
839 if(n_top.d != CONTENT_AIR
840 && n_top.d != CONTENT_IGNORE)
843 m_map->addNodeWithEvent(p, n);
852 Mess around in active blocks
854 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
858 for(core::map<v3s16, bool>::Iterator
859 i = m_active_blocks.m_list.getIterator();
860 i.atEnd()==false; i++)
862 v3s16 p = i.getNode()->getKey();
864 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
865 <<") being handled"<<std::endl;*/
867 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
871 // Set current time as timestamp
872 block->setTimestampNoChangedFlag(m_game_time);
875 bool changed = block->m_node_metadata.step(dtime);
879 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
881 m_map->dispatchEvent(&event);
883 block->setChangedFlag();
887 if(m_active_blocks_test_interval.step(dtime, 10.0))
889 //float dtime = 10.0;
891 for(core::map<v3s16, bool>::Iterator
892 i = m_active_blocks.m_list.getIterator();
893 i.atEnd()==false; i++)
895 v3s16 p = i.getNode()->getKey();
897 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
898 <<") being handled"<<std::endl;*/
900 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
904 // Set current time as timestamp
905 block->setTimestampNoChangedFlag(m_game_time);
910 Note that map modifications should be done using the event-
911 making map methods so that the server gets information
914 Reading can be done quickly directly from the block.
916 Everything should bind to inside this single content
917 searching loop to keep things fast.
919 // TODO: Implement usage of ActiveBlockModifier
922 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
923 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
924 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
926 v3s16 p = p0 + block->getPosRelative();
927 MapNode n = block->getNodeNoEx(p0);
931 Convert mud under proper lighting to grass
933 if(n.d == CONTENT_MUD)
937 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
938 if(content_features(n_top.d).air_equivalent &&
939 n_top.getLightBlend(getDayNightRatio()) >= 13)
942 m_map->addNodeWithEvent(p, n);
947 Convert grass into mud if under something else than air
949 else if(n.d == CONTENT_GRASS)
951 //if(myrand()%20 == 0)
953 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
954 if(n_top.d != CONTENT_AIR
955 && n_top.d != CONTENT_IGNORE)
958 m_map->addNodeWithEvent(p, n);
970 //TimeTaker timer("Step active objects");
972 // This helps the objects to send data at the same time
973 bool send_recommended = false;
974 m_send_recommended_timer += dtime;
975 if(m_send_recommended_timer > 0.15)
977 m_send_recommended_timer = 0;
978 send_recommended = true;
981 for(core::map<u16, ServerActiveObject*>::Iterator
982 i = m_active_objects.getIterator();
983 i.atEnd()==false; i++)
985 ServerActiveObject* obj = i.getNode()->getValue();
986 // Don't step if is to be removed or stored statically
987 if(obj->m_removed || obj->m_pending_deactivation)
989 // Step object, putting messages directly to the queue
990 obj->step(dtime, m_active_object_messages, send_recommended);
995 Manage active objects
997 if(m_object_management_interval.step(dtime, 0.5))
1000 Remove objects that satisfy (m_removed && m_known_by_count==0)
1002 removeRemovedObjects();
1005 if(g_settings.getBool("enable_experimental"))
1012 m_random_spawn_timer -= dtime;
1013 if(m_random_spawn_timer < 0)
1015 //m_random_spawn_timer += myrand_range(2.0, 20.0);
1016 //m_random_spawn_timer += 2.0;
1017 m_random_spawn_timer += 200.0;
1023 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
1024 s16 y = 1 + getServerMap().findGroundLevel(p2d);
1025 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
1027 Player *player = getRandomConnectedPlayer();
1030 pos = player->getPosition();
1032 myrand_range(-3,3)*BS,
1034 myrand_range(-3,3)*BS
1038 Create a ServerActiveObject
1041 //TestSAO *obj = new TestSAO(this, 0, pos);
1042 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
1043 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
1044 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
1045 addActiveObject(obj);
1049 } // enable_experimental
1052 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1054 core::map<u16, ServerActiveObject*>::Node *n;
1055 n = m_active_objects.find(id);
1058 return n->getValue();
1061 bool isFreeServerActiveObjectId(u16 id,
1062 core::map<u16, ServerActiveObject*> &objects)
1067 for(core::map<u16, ServerActiveObject*>::Iterator
1068 i = objects.getIterator();
1069 i.atEnd()==false; i++)
1071 if(i.getNode()->getKey() == id)
1077 u16 getFreeServerActiveObjectId(
1078 core::map<u16, ServerActiveObject*> &objects)
1083 if(isFreeServerActiveObjectId(new_id, objects))
1093 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1096 u16 id = addActiveObjectRaw(object, true);
1101 Finds out what new objects have been added to
1102 inside a radius around a position
1104 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1105 core::map<u16, bool> ¤t_objects,
1106 core::map<u16, bool> &added_objects)
1108 v3f pos_f = intToFloat(pos, BS);
1109 f32 radius_f = radius * BS;
1111 Go through the object list,
1112 - discard m_removed objects,
1113 - discard objects that are too far away,
1114 - discard objects that are found in current_objects.
1115 - add remaining objects to added_objects
1117 for(core::map<u16, ServerActiveObject*>::Iterator
1118 i = m_active_objects.getIterator();
1119 i.atEnd()==false; i++)
1121 u16 id = i.getNode()->getKey();
1123 ServerActiveObject *object = i.getNode()->getValue();
1126 // Discard if removed
1127 if(object->m_removed)
1129 // Discard if too far
1130 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1131 if(distance_f > radius_f)
1133 // Discard if already on current_objects
1134 core::map<u16, bool>::Node *n;
1135 n = current_objects.find(id);
1138 // Add to added_objects
1139 added_objects.insert(id, false);
1144 Finds out what objects have been removed from
1145 inside a radius around a position
1147 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1148 core::map<u16, bool> ¤t_objects,
1149 core::map<u16, bool> &removed_objects)
1151 v3f pos_f = intToFloat(pos, BS);
1152 f32 radius_f = radius * BS;
1154 Go through current_objects; object is removed if:
1155 - object is not found in m_active_objects (this is actually an
1156 error condition; objects should be set m_removed=true and removed
1157 only after all clients have been informed about removal), or
1158 - object has m_removed=true, or
1159 - object is too far away
1161 for(core::map<u16, bool>::Iterator
1162 i = current_objects.getIterator();
1163 i.atEnd()==false; i++)
1165 u16 id = i.getNode()->getKey();
1166 ServerActiveObject *object = getActiveObject(id);
1169 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1170 <<" object in current_objects is NULL"<<std::endl;
1172 else if(object->m_removed == false)
1174 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1175 /*dstream<<"removed == false"
1176 <<"distance_f = "<<distance_f
1177 <<", radius_f = "<<radius_f<<std::endl;*/
1178 if(distance_f < radius_f)
1184 removed_objects.insert(id, false);
1188 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1190 if(m_active_object_messages.size() == 0)
1191 return ActiveObjectMessage(0);
1193 return m_active_object_messages.pop_front();
1197 ************ Private methods *************
1200 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1204 if(object->getId() == 0)
1206 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1209 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1210 <<"no free ids available"<<std::endl;
1214 object->setId(new_id);
1216 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1218 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1219 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1223 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1224 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1226 m_active_objects.insert(object->getId(), object);
1228 // Add static object to active static list of the block
1229 v3f objectpos = object->getBasePosition();
1230 std::string staticdata = object->getStaticData();
1231 StaticObject s_obj(object->getType(), objectpos, staticdata);
1232 // Add to the block where the object is located in
1233 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1234 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1237 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1238 object->m_static_exists = true;
1239 object->m_static_block = blockpos;
1242 block->setChangedFlag();
1245 dstream<<"WARNING: Server: Could not find a block for "
1246 <<"storing newly added static active object"<<std::endl;
1249 return object->getId();
1253 Remove objects that satisfy (m_removed && m_known_by_count==0)
1255 void ServerEnvironment::removeRemovedObjects()
1257 core::list<u16> objects_to_remove;
1258 for(core::map<u16, ServerActiveObject*>::Iterator
1259 i = m_active_objects.getIterator();
1260 i.atEnd()==false; i++)
1262 u16 id = i.getNode()->getKey();
1263 ServerActiveObject* obj = i.getNode()->getValue();
1264 // This shouldn't happen but check it
1267 dstream<<"WARNING: NULL object found in ServerEnvironment"
1268 <<" while finding removed objects. id="<<id<<std::endl;
1269 // Id to be removed from m_active_objects
1270 objects_to_remove.push_back(id);
1275 We will delete objects that are marked as removed or thatare
1276 waiting for deletion after deactivation
1278 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1282 Delete static data from block if is marked as removed
1284 if(obj->m_static_exists && obj->m_removed)
1286 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1289 block->m_static_objects.remove(id);
1290 block->setChangedFlag();
1294 // If m_known_by_count > 0, don't actually remove.
1295 if(obj->m_known_by_count > 0)
1300 // Id to be removed from m_active_objects
1301 objects_to_remove.push_back(id);
1303 // Remove references from m_active_objects
1304 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1305 i != objects_to_remove.end(); i++)
1307 m_active_objects.remove(*i);
1312 Convert stored objects from blocks near the players to active.
1314 void ServerEnvironment::activateObjects(MapBlock *block)
1318 // Ignore if no stored objects (to not set changed flag)
1319 if(block->m_static_objects.m_stored.size() == 0)
1321 // A list for objects that couldn't be converted to static for some
1322 // reason. They will be stored back.
1323 core::list<StaticObject> new_stored;
1324 // Loop through stored static objects
1325 for(core::list<StaticObject>::Iterator
1326 i = block->m_static_objects.m_stored.begin();
1327 i != block->m_static_objects.m_stored.end(); i++)
1329 /*dstream<<"INFO: Server: Creating an active object from "
1330 <<"static data"<<std::endl;*/
1331 StaticObject &s_obj = *i;
1332 // Create an active object from the data
1333 ServerActiveObject *obj = ServerActiveObject::create
1334 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1335 // If couldn't create object, store static data back.
1338 new_stored.push_back(s_obj);
1341 // This will also add the object to the active static list
1342 addActiveObjectRaw(obj, false);
1343 //u16 id = addActiveObjectRaw(obj, false);
1345 // Clear stored list
1346 block->m_static_objects.m_stored.clear();
1347 // Add leftover failed stuff to stored list
1348 for(core::list<StaticObject>::Iterator
1349 i = new_stored.begin();
1350 i != new_stored.end(); i++)
1352 StaticObject &s_obj = *i;
1353 block->m_static_objects.m_stored.push_back(s_obj);
1355 // Block has been modified
1356 // NOTE: No it has not really. Save I/O here.
1357 //block->setChangedFlag();
1361 Convert objects that are not in active blocks to static.
1363 If m_known_by_count != 0, active object is not deleted, but static
1364 data is still updated.
1366 If force_delete is set, active object is deleted nevertheless. It
1367 shall only be set so in the destructor of the environment.
1369 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1371 core::list<u16> objects_to_remove;
1372 for(core::map<u16, ServerActiveObject*>::Iterator
1373 i = m_active_objects.getIterator();
1374 i.atEnd()==false; i++)
1376 ServerActiveObject* obj = i.getNode()->getValue();
1377 u16 id = i.getNode()->getKey();
1378 v3f objectpos = obj->getBasePosition();
1380 // This shouldn't happen but check it
1383 dstream<<"WARNING: NULL object found in ServerEnvironment"
1389 // The block in which the object resides in
1390 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1392 // If block is active, don't remove
1393 if(m_active_blocks.contains(blockpos_o))
1397 Update the static data
1400 // Delete old static object
1401 MapBlock *oldblock = NULL;
1402 if(obj->m_static_exists)
1404 MapBlock *block = m_map->getBlockNoCreateNoEx
1405 (obj->m_static_block);
1408 block->m_static_objects.remove(id);
1412 // Create new static object
1413 std::string staticdata = obj->getStaticData();
1414 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1415 // Add to the block where the object is located in
1416 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1417 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1420 block->m_static_objects.insert(0, s_obj);
1421 block->setChangedFlag();
1422 obj->m_static_exists = true;
1423 obj->m_static_block = block->getPos();
1425 // If not possible, add back to previous block
1428 oldblock->m_static_objects.insert(0, s_obj);
1429 oldblock->setChangedFlag();
1430 obj->m_static_exists = true;
1431 obj->m_static_block = oldblock->getPos();
1434 dstream<<"WARNING: Server: Could not find a block for "
1435 <<"storing static object"<<std::endl;
1436 obj->m_static_exists = false;
1441 Delete active object if not known by some client,
1442 else set pending deactivation
1445 // If known by some client, don't delete.
1446 if(obj->m_known_by_count > 0 && force_delete == false)
1448 obj->m_pending_deactivation = true;
1452 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1454 // Delete active object
1456 // Id to be removed from m_active_objects
1457 objects_to_remove.push_back(id);
1460 // Remove references from m_active_objects
1461 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1462 i != objects_to_remove.end(); i++)
1464 m_active_objects.remove(*i);
1475 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1483 ClientEnvironment::~ClientEnvironment()
1485 // delete active objects
1486 for(core::map<u16, ClientActiveObject*>::Iterator
1487 i = m_active_objects.getIterator();
1488 i.atEnd()==false; i++)
1490 delete i.getNode()->getValue();
1497 void ClientEnvironment::addPlayer(Player *player)
1499 DSTACK(__FUNCTION_NAME);
1501 It is a failure if player is local and there already is a local
1504 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1506 Environment::addPlayer(player);
1509 LocalPlayer * ClientEnvironment::getLocalPlayer()
1511 for(core::list<Player*>::Iterator i = m_players.begin();
1512 i != m_players.end(); i++)
1514 Player *player = *i;
1515 if(player->isLocal())
1516 return (LocalPlayer*)player;
1521 void ClientEnvironment::step(float dtime)
1523 DSTACK(__FUNCTION_NAME);
1525 // Get some settings
1526 bool free_move = g_settings.getBool("free_move");
1527 bool footprints = g_settings.getBool("footprints");
1530 //TimeTaker timer("Client m_map->timerUpdate()");
1531 m_map->timerUpdate(dtime);
1535 LocalPlayer *lplayer = getLocalPlayer();
1537 // collision info queue
1538 core::list<CollisionInfo> player_collisions;
1541 Get the speed the player is going
1543 f32 player_speed = 0.001; // just some small value
1544 player_speed = lplayer->getSpeed().getLength();
1547 Maximum position increment
1549 //f32 position_max_increment = 0.05*BS;
1550 f32 position_max_increment = 0.1*BS;
1552 // Maximum time increment (for collision detection etc)
1553 // time = distance / speed
1554 f32 dtime_max_increment = position_max_increment / player_speed;
1556 // Maximum time increment is 10ms or lower
1557 if(dtime_max_increment > 0.01)
1558 dtime_max_increment = 0.01;
1560 // Don't allow overly huge dtime
1564 f32 dtime_downcount = dtime;
1567 Stuff that has a maximum time increment
1576 if(dtime_downcount > dtime_max_increment)
1578 dtime_part = dtime_max_increment;
1579 dtime_downcount -= dtime_part;
1583 dtime_part = dtime_downcount;
1585 Setting this to 0 (no -=dtime_part) disables an infinite loop
1586 when dtime_part is so small that dtime_downcount -= dtime_part
1589 dtime_downcount = 0;
1597 v3f lplayerpos = lplayer->getPosition();
1600 if(free_move == false)
1603 v3f speed = lplayer->getSpeed();
1604 if(lplayer->swimming_up == false)
1605 speed.Y -= 9.81 * BS * dtime_part * 2;
1608 if(lplayer->in_water_stable || lplayer->in_water)
1610 f32 max_down = 2.0*BS;
1611 if(speed.Y < -max_down) speed.Y = -max_down;
1614 if(speed.getLength() > max)
1616 speed = speed / speed.getLength() * max;
1620 lplayer->setSpeed(speed);
1625 This also does collision detection.
1627 lplayer->move(dtime_part, *m_map, position_max_increment,
1628 &player_collisions);
1631 while(dtime_downcount > 0.001);
1633 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1635 for(core::list<CollisionInfo>::Iterator
1636 i = player_collisions.begin();
1637 i != player_collisions.end(); i++)
1639 CollisionInfo &info = *i;
1640 if(info.t == COLLISION_FALL)
1642 //f32 tolerance = BS*10; // 2 without damage
1643 f32 tolerance = BS*12; // 3 without damage
1645 if(info.speed > tolerance)
1647 f32 damage_f = (info.speed - tolerance)/BS*factor;
1648 u16 damage = (u16)(damage_f+0.5);
1649 if(lplayer->hp > damage)
1650 lplayer->hp -= damage;
1654 ClientEnvEvent event;
1655 event.type = CEE_PLAYER_DAMAGE;
1656 event.player_damage.amount = damage;
1657 m_client_event_queue.push_back(event);
1663 Stuff that can be done in an arbitarily large dtime
1665 for(core::list<Player*>::Iterator i = m_players.begin();
1666 i != m_players.end(); i++)
1668 Player *player = *i;
1669 v3f playerpos = player->getPosition();
1672 Handle non-local players
1674 if(player->isLocal() == false)
1677 player->move(dtime, *m_map, 100*BS);
1679 // Update lighting on remote players on client
1680 u8 light = LIGHT_MAX;
1683 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1684 MapNode n = m_map->getNode(p);
1685 light = n.getLightBlend(getDayNightRatio());
1687 catch(InvalidPositionException &e) {}
1688 player->updateLight(light);
1692 Add footsteps to grass
1696 // Get node that is at BS/4 under player
1697 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1699 MapNode n = m_map->getNode(bottompos);
1700 if(n.d == CONTENT_GRASS)
1702 n.d = CONTENT_GRASS_FOOTSTEPS;
1703 m_map->setNode(bottompos, n);
1704 // Update mesh on client
1705 if(m_map->mapType() == MAPTYPE_CLIENT)
1707 v3s16 p_blocks = getNodeBlockPos(bottompos);
1708 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1709 //b->updateMesh(getDayNightRatio());
1710 b->setMeshExpired(true);
1714 catch(InvalidPositionException &e)
1721 Step active objects and update lighting of them
1724 for(core::map<u16, ClientActiveObject*>::Iterator
1725 i = m_active_objects.getIterator();
1726 i.atEnd()==false; i++)
1728 ClientActiveObject* obj = i.getNode()->getValue();
1730 obj->step(dtime, this);
1732 //u8 light = LIGHT_MAX;
1736 v3s16 p = obj->getLightPosition();
1737 MapNode n = m_map->getNode(p);
1738 light = n.getLightBlend(getDayNightRatio());
1740 catch(InvalidPositionException &e) {}
1741 obj->updateLight(light);
1745 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1747 m_map->updateMeshes(blockpos, getDayNightRatio());
1750 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1752 m_map->expireMeshes(only_daynight_diffed);
1755 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1757 core::map<u16, ClientActiveObject*>::Node *n;
1758 n = m_active_objects.find(id);
1761 return n->getValue();
1764 bool isFreeClientActiveObjectId(u16 id,
1765 core::map<u16, ClientActiveObject*> &objects)
1770 for(core::map<u16, ClientActiveObject*>::Iterator
1771 i = objects.getIterator();
1772 i.atEnd()==false; i++)
1774 if(i.getNode()->getKey() == id)
1780 u16 getFreeClientActiveObjectId(
1781 core::map<u16, ClientActiveObject*> &objects)
1786 if(isFreeClientActiveObjectId(new_id, objects))
1796 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1799 if(object->getId() == 0)
1801 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1804 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1805 <<"no free ids available"<<std::endl;
1809 object->setId(new_id);
1811 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1813 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1814 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1818 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1819 <<"added (id="<<object->getId()<<")"<<std::endl;
1820 m_active_objects.insert(object->getId(), object);
1821 object->addToScene(m_smgr);
1822 return object->getId();
1825 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1826 const std::string &init_data)
1828 ClientActiveObject* obj = ClientActiveObject::create(type);
1831 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1832 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1839 addActiveObject(obj);
1841 obj->initialize(init_data);
1844 void ClientEnvironment::removeActiveObject(u16 id)
1846 dstream<<"ClientEnvironment::removeActiveObject(): "
1847 <<"id="<<id<<std::endl;
1848 ClientActiveObject* obj = getActiveObject(id);
1851 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1852 <<"id="<<id<<" not found"<<std::endl;
1855 obj->removeFromScene();
1857 m_active_objects.remove(id);
1860 void ClientEnvironment::processActiveObjectMessage(u16 id,
1861 const std::string &data)
1863 ClientActiveObject* obj = getActiveObject(id);
1866 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1867 <<" got message for id="<<id<<", which doesn't exist."
1871 obj->processMessage(data);
1875 Callbacks for activeobjects
1878 void ClientEnvironment::damageLocalPlayer(u8 damage)
1880 LocalPlayer *lplayer = getLocalPlayer();
1883 if(lplayer->hp > damage)
1884 lplayer->hp -= damage;
1888 ClientEnvEvent event;
1889 event.type = CEE_PLAYER_DAMAGE;
1890 event.player_damage.amount = damage;
1891 m_client_event_queue.push_back(event);
1895 Client likes to call these
1898 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1899 core::array<DistanceSortedActiveObject> &dest)
1901 for(core::map<u16, ClientActiveObject*>::Iterator
1902 i = m_active_objects.getIterator();
1903 i.atEnd()==false; i++)
1905 ClientActiveObject* obj = i.getNode()->getValue();
1907 f32 d = (obj->getPosition() - origin).getLength();
1912 DistanceSortedActiveObject dso(obj, d);
1914 dest.push_back(dso);
1918 ClientEnvEvent ClientEnvironment::getClientEvent()
1920 if(m_client_event_queue.size() == 0)
1922 ClientEnvEvent event;
1923 event.type = CEE_NONE;
1926 return m_client_event_queue.pop_front();
1929 #endif // #ifndef SERVER