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);
758 // 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 activateBlock(block);
784 Mess around in active blocks
786 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
790 for(core::map<v3s16, bool>::Iterator
791 i = m_active_blocks.m_list.getIterator();
792 i.atEnd()==false; i++)
794 v3s16 p = i.getNode()->getKey();
796 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
797 <<") being handled"<<std::endl;*/
799 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
803 // Set current time as timestamp
804 block->setTimestampNoChangedFlag(m_game_time);
807 bool changed = block->m_node_metadata.step(dtime);
811 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
813 m_map->dispatchEvent(&event);
815 block->setChangedFlag();
819 if(m_active_blocks_test_interval.step(dtime, 10.0))
821 //float dtime = 10.0;
823 for(core::map<v3s16, bool>::Iterator
824 i = m_active_blocks.m_list.getIterator();
825 i.atEnd()==false; i++)
827 v3s16 p = i.getNode()->getKey();
829 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
830 <<") being handled"<<std::endl;*/
832 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
836 // Set current time as timestamp
837 block->setTimestampNoChangedFlag(m_game_time);
842 Note that map modifications should be done using the event-
843 making map methods so that the server gets information
846 Reading can be done quickly directly from the block.
848 Everything should bind to inside this single content
849 searching loop to keep things fast.
851 // TODO: Implement usage of ActiveBlockModifier
854 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
855 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
856 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
858 v3s16 p = p0 + block->getPosRelative();
859 MapNode n = block->getNodeNoEx(p0);
863 Convert mud under proper lighting to grass
865 if(n.d == CONTENT_MUD)
869 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
870 if(content_features(n_top.d).air_equivalent &&
871 n_top.getLightBlend(getDayNightRatio()) >= 13)
874 m_map->addNodeWithEvent(p, n);
879 Convert grass into mud if under something else than air
881 else if(n.d == CONTENT_GRASS)
883 //if(myrand()%20 == 0)
885 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
886 if(n_top.d != CONTENT_AIR
887 && n_top.d != CONTENT_IGNORE)
890 m_map->addNodeWithEvent(p, n);
902 //TimeTaker timer("Step active objects");
904 // This helps the objects to send data at the same time
905 bool send_recommended = false;
906 m_send_recommended_timer += dtime;
907 if(m_send_recommended_timer > 0.15)
909 m_send_recommended_timer = 0;
910 send_recommended = true;
913 for(core::map<u16, ServerActiveObject*>::Iterator
914 i = m_active_objects.getIterator();
915 i.atEnd()==false; i++)
917 ServerActiveObject* obj = i.getNode()->getValue();
918 // Don't step if is to be removed or stored statically
919 if(obj->m_removed || obj->m_pending_deactivation)
921 // Step object, putting messages directly to the queue
922 obj->step(dtime, m_active_object_messages, send_recommended);
927 Manage active objects
929 if(m_object_management_interval.step(dtime, 0.5))
932 Remove objects that satisfy (m_removed && m_known_by_count==0)
934 removeRemovedObjects();
937 if(g_settings.getBool("enable_experimental"))
944 m_random_spawn_timer -= dtime;
945 if(m_random_spawn_timer < 0)
947 //m_random_spawn_timer += myrand_range(2.0, 20.0);
948 //m_random_spawn_timer += 2.0;
949 m_random_spawn_timer += 200.0;
955 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
956 s16 y = 1 + getServerMap().findGroundLevel(p2d);
957 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
959 Player *player = getRandomConnectedPlayer();
962 pos = player->getPosition();
964 myrand_range(-3,3)*BS,
966 myrand_range(-3,3)*BS
970 Create a ServerActiveObject
973 //TestSAO *obj = new TestSAO(this, 0, pos);
974 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
975 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
976 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
977 addActiveObject(obj);
981 } // enable_experimental
984 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
986 core::map<u16, ServerActiveObject*>::Node *n;
987 n = m_active_objects.find(id);
990 return n->getValue();
993 bool isFreeServerActiveObjectId(u16 id,
994 core::map<u16, ServerActiveObject*> &objects)
999 for(core::map<u16, ServerActiveObject*>::Iterator
1000 i = objects.getIterator();
1001 i.atEnd()==false; i++)
1003 if(i.getNode()->getKey() == id)
1009 u16 getFreeServerActiveObjectId(
1010 core::map<u16, ServerActiveObject*> &objects)
1015 if(isFreeServerActiveObjectId(new_id, objects))
1025 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1028 u16 id = addActiveObjectRaw(object, true);
1033 Finds out what new objects have been added to
1034 inside a radius around a position
1036 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1037 core::map<u16, bool> ¤t_objects,
1038 core::map<u16, bool> &added_objects)
1040 v3f pos_f = intToFloat(pos, BS);
1041 f32 radius_f = radius * BS;
1043 Go through the object list,
1044 - discard m_removed objects,
1045 - discard objects that are too far away,
1046 - discard objects that are found in current_objects.
1047 - add remaining objects to added_objects
1049 for(core::map<u16, ServerActiveObject*>::Iterator
1050 i = m_active_objects.getIterator();
1051 i.atEnd()==false; i++)
1053 u16 id = i.getNode()->getKey();
1055 ServerActiveObject *object = i.getNode()->getValue();
1058 // Discard if removed
1059 if(object->m_removed)
1061 // Discard if too far
1062 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1063 if(distance_f > radius_f)
1065 // Discard if already on current_objects
1066 core::map<u16, bool>::Node *n;
1067 n = current_objects.find(id);
1070 // Add to added_objects
1071 added_objects.insert(id, false);
1076 Finds out what objects have been removed from
1077 inside a radius around a position
1079 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1080 core::map<u16, bool> ¤t_objects,
1081 core::map<u16, bool> &removed_objects)
1083 v3f pos_f = intToFloat(pos, BS);
1084 f32 radius_f = radius * BS;
1086 Go through current_objects; object is removed if:
1087 - object is not found in m_active_objects (this is actually an
1088 error condition; objects should be set m_removed=true and removed
1089 only after all clients have been informed about removal), or
1090 - object has m_removed=true, or
1091 - object is too far away
1093 for(core::map<u16, bool>::Iterator
1094 i = current_objects.getIterator();
1095 i.atEnd()==false; i++)
1097 u16 id = i.getNode()->getKey();
1098 ServerActiveObject *object = getActiveObject(id);
1101 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1102 <<" object in current_objects is NULL"<<std::endl;
1104 else if(object->m_removed == false)
1106 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1107 /*dstream<<"removed == false"
1108 <<"distance_f = "<<distance_f
1109 <<", radius_f = "<<radius_f<<std::endl;*/
1110 if(distance_f < radius_f)
1116 removed_objects.insert(id, false);
1120 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1122 if(m_active_object_messages.size() == 0)
1123 return ActiveObjectMessage(0);
1125 return m_active_object_messages.pop_front();
1129 ************ Private methods *************
1132 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1136 if(object->getId() == 0)
1138 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1141 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1142 <<"no free ids available"<<std::endl;
1146 object->setId(new_id);
1148 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1150 dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
1151 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1155 /*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
1156 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1158 m_active_objects.insert(object->getId(), object);
1160 // Add static object to active static list of the block
1161 v3f objectpos = object->getBasePosition();
1162 std::string staticdata = object->getStaticData();
1163 StaticObject s_obj(object->getType(), objectpos, staticdata);
1164 // Add to the block where the object is located in
1165 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1166 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1169 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1170 object->m_static_exists = true;
1171 object->m_static_block = blockpos;
1174 block->setChangedFlag();
1177 dstream<<"WARNING: Server: Could not find a block for "
1178 <<"storing newly added static active object"<<std::endl;
1181 return object->getId();
1185 Remove objects that satisfy (m_removed && m_known_by_count==0)
1187 void ServerEnvironment::removeRemovedObjects()
1189 core::list<u16> objects_to_remove;
1190 for(core::map<u16, ServerActiveObject*>::Iterator
1191 i = m_active_objects.getIterator();
1192 i.atEnd()==false; i++)
1194 u16 id = i.getNode()->getKey();
1195 ServerActiveObject* obj = i.getNode()->getValue();
1196 // This shouldn't happen but check it
1199 dstream<<"WARNING: NULL object found in ServerEnvironment"
1200 <<" while finding removed objects. id="<<id<<std::endl;
1201 // Id to be removed from m_active_objects
1202 objects_to_remove.push_back(id);
1207 We will delete objects that are marked as removed or thatare
1208 waiting for deletion after deactivation
1210 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1214 Delete static data from block if is marked as removed
1216 if(obj->m_static_exists && obj->m_removed)
1218 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1221 block->m_static_objects.remove(id);
1222 block->setChangedFlag();
1226 // If m_known_by_count > 0, don't actually remove.
1227 if(obj->m_known_by_count > 0)
1232 // Id to be removed from m_active_objects
1233 objects_to_remove.push_back(id);
1235 // Remove references from m_active_objects
1236 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1237 i != objects_to_remove.end(); i++)
1239 m_active_objects.remove(*i);
1244 Convert stored objects from blocks near the players to active.
1246 void ServerEnvironment::activateObjects(MapBlock *block)
1250 // Ignore if no stored objects (to not set changed flag)
1251 if(block->m_static_objects.m_stored.size() == 0)
1253 // A list for objects that couldn't be converted to static for some
1254 // reason. They will be stored back.
1255 core::list<StaticObject> new_stored;
1256 // Loop through stored static objects
1257 for(core::list<StaticObject>::Iterator
1258 i = block->m_static_objects.m_stored.begin();
1259 i != block->m_static_objects.m_stored.end(); i++)
1261 /*dstream<<"INFO: Server: Creating an active object from "
1262 <<"static data"<<std::endl;*/
1263 StaticObject &s_obj = *i;
1264 // Create an active object from the data
1265 ServerActiveObject *obj = ServerActiveObject::create
1266 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1267 // If couldn't create object, store static data back.
1270 new_stored.push_back(s_obj);
1273 // This will also add the object to the active static list
1274 addActiveObjectRaw(obj, false);
1275 //u16 id = addActiveObjectRaw(obj, false);
1277 // Clear stored list
1278 block->m_static_objects.m_stored.clear();
1279 // Add leftover failed stuff to stored list
1280 for(core::list<StaticObject>::Iterator
1281 i = new_stored.begin();
1282 i != new_stored.end(); i++)
1284 StaticObject &s_obj = *i;
1285 block->m_static_objects.m_stored.push_back(s_obj);
1287 // Block has been modified
1288 // NOTE: No it has not really. Save I/O here.
1289 //block->setChangedFlag();
1293 Convert objects that are not in active blocks to static.
1295 If m_known_by_count != 0, active object is not deleted, but static
1296 data is still updated.
1298 If force_delete is set, active object is deleted nevertheless. It
1299 shall only be set so in the destructor of the environment.
1301 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1303 core::list<u16> objects_to_remove;
1304 for(core::map<u16, ServerActiveObject*>::Iterator
1305 i = m_active_objects.getIterator();
1306 i.atEnd()==false; i++)
1308 ServerActiveObject* obj = i.getNode()->getValue();
1309 u16 id = i.getNode()->getKey();
1310 v3f objectpos = obj->getBasePosition();
1312 // This shouldn't happen but check it
1315 dstream<<"WARNING: NULL object found in ServerEnvironment"
1321 // The block in which the object resides in
1322 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1324 // If block is active, don't remove
1325 if(m_active_blocks.contains(blockpos_o))
1329 Update the static data
1332 // Delete old static object
1333 MapBlock *oldblock = NULL;
1334 if(obj->m_static_exists)
1336 MapBlock *block = m_map->getBlockNoCreateNoEx
1337 (obj->m_static_block);
1340 block->m_static_objects.remove(id);
1344 // Create new static object
1345 std::string staticdata = obj->getStaticData();
1346 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1347 // Add to the block where the object is located in
1348 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1349 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1352 block->m_static_objects.insert(0, s_obj);
1353 block->setChangedFlag();
1354 obj->m_static_exists = true;
1355 obj->m_static_block = block->getPos();
1357 // If not possible, add back to previous block
1360 oldblock->m_static_objects.insert(0, s_obj);
1361 oldblock->setChangedFlag();
1362 obj->m_static_exists = true;
1363 obj->m_static_block = oldblock->getPos();
1366 dstream<<"WARNING: Server: Could not find a block for "
1367 <<"storing static object"<<std::endl;
1368 obj->m_static_exists = false;
1373 Delete active object if not known by some client,
1374 else set pending deactivation
1377 // If known by some client, don't delete.
1378 if(obj->m_known_by_count > 0 && force_delete == false)
1380 obj->m_pending_deactivation = true;
1384 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1386 // Delete active object
1388 // Id to be removed from m_active_objects
1389 objects_to_remove.push_back(id);
1392 // Remove references from m_active_objects
1393 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1394 i != objects_to_remove.end(); i++)
1396 m_active_objects.remove(*i);
1407 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1415 ClientEnvironment::~ClientEnvironment()
1417 // delete active objects
1418 for(core::map<u16, ClientActiveObject*>::Iterator
1419 i = m_active_objects.getIterator();
1420 i.atEnd()==false; i++)
1422 delete i.getNode()->getValue();
1429 void ClientEnvironment::addPlayer(Player *player)
1431 DSTACK(__FUNCTION_NAME);
1433 It is a failure if player is local and there already is a local
1436 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1438 Environment::addPlayer(player);
1441 LocalPlayer * ClientEnvironment::getLocalPlayer()
1443 for(core::list<Player*>::Iterator i = m_players.begin();
1444 i != m_players.end(); i++)
1446 Player *player = *i;
1447 if(player->isLocal())
1448 return (LocalPlayer*)player;
1453 void ClientEnvironment::step(float dtime)
1455 DSTACK(__FUNCTION_NAME);
1457 // Get some settings
1458 bool free_move = g_settings.getBool("free_move");
1459 bool footprints = g_settings.getBool("footprints");
1462 //TimeTaker timer("Client m_map->timerUpdate()");
1463 m_map->timerUpdate(dtime);
1467 LocalPlayer *lplayer = getLocalPlayer();
1469 // collision info queue
1470 core::list<CollisionInfo> player_collisions;
1473 Get the speed the player is going
1475 f32 player_speed = 0.001; // just some small value
1476 player_speed = lplayer->getSpeed().getLength();
1479 Maximum position increment
1481 //f32 position_max_increment = 0.05*BS;
1482 f32 position_max_increment = 0.1*BS;
1484 // Maximum time increment (for collision detection etc)
1485 // time = distance / speed
1486 f32 dtime_max_increment = position_max_increment / player_speed;
1488 // Maximum time increment is 10ms or lower
1489 if(dtime_max_increment > 0.01)
1490 dtime_max_increment = 0.01;
1492 // Don't allow overly huge dtime
1496 f32 dtime_downcount = dtime;
1499 Stuff that has a maximum time increment
1508 if(dtime_downcount > dtime_max_increment)
1510 dtime_part = dtime_max_increment;
1511 dtime_downcount -= dtime_part;
1515 dtime_part = dtime_downcount;
1517 Setting this to 0 (no -=dtime_part) disables an infinite loop
1518 when dtime_part is so small that dtime_downcount -= dtime_part
1521 dtime_downcount = 0;
1529 v3f lplayerpos = lplayer->getPosition();
1532 if(free_move == false)
1535 v3f speed = lplayer->getSpeed();
1536 if(lplayer->swimming_up == false)
1537 speed.Y -= 9.81 * BS * dtime_part * 2;
1540 if(lplayer->in_water_stable || lplayer->in_water)
1542 f32 max_down = 2.0*BS;
1543 if(speed.Y < -max_down) speed.Y = -max_down;
1546 if(speed.getLength() > max)
1548 speed = speed / speed.getLength() * max;
1552 lplayer->setSpeed(speed);
1557 This also does collision detection.
1559 lplayer->move(dtime_part, *m_map, position_max_increment,
1560 &player_collisions);
1563 while(dtime_downcount > 0.001);
1565 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1567 for(core::list<CollisionInfo>::Iterator
1568 i = player_collisions.begin();
1569 i != player_collisions.end(); i++)
1571 CollisionInfo &info = *i;
1572 if(info.t == COLLISION_FALL)
1574 //f32 tolerance = BS*10; // 2 without damage
1575 f32 tolerance = BS*12; // 3 without damage
1577 if(info.speed > tolerance)
1579 f32 damage_f = (info.speed - tolerance)/BS*factor;
1580 u16 damage = (u16)(damage_f+0.5);
1581 if(lplayer->hp > damage)
1582 lplayer->hp -= damage;
1586 ClientEnvEvent event;
1587 event.type = CEE_PLAYER_DAMAGE;
1588 event.player_damage.amount = damage;
1589 m_client_event_queue.push_back(event);
1595 Stuff that can be done in an arbitarily large dtime
1597 for(core::list<Player*>::Iterator i = m_players.begin();
1598 i != m_players.end(); i++)
1600 Player *player = *i;
1601 v3f playerpos = player->getPosition();
1604 Handle non-local players
1606 if(player->isLocal() == false)
1609 player->move(dtime, *m_map, 100*BS);
1611 // Update lighting on remote players on client
1612 u8 light = LIGHT_MAX;
1615 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1616 MapNode n = m_map->getNode(p);
1617 light = n.getLightBlend(getDayNightRatio());
1619 catch(InvalidPositionException &e) {}
1620 player->updateLight(light);
1624 Add footsteps to grass
1628 // Get node that is at BS/4 under player
1629 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1631 MapNode n = m_map->getNode(bottompos);
1632 if(n.d == CONTENT_GRASS)
1634 n.d = CONTENT_GRASS_FOOTSTEPS;
1635 m_map->setNode(bottompos, n);
1636 // Update mesh on client
1637 if(m_map->mapType() == MAPTYPE_CLIENT)
1639 v3s16 p_blocks = getNodeBlockPos(bottompos);
1640 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1641 //b->updateMesh(getDayNightRatio());
1642 b->setMeshExpired(true);
1646 catch(InvalidPositionException &e)
1653 Step active objects and update lighting of them
1656 for(core::map<u16, ClientActiveObject*>::Iterator
1657 i = m_active_objects.getIterator();
1658 i.atEnd()==false; i++)
1660 ClientActiveObject* obj = i.getNode()->getValue();
1662 obj->step(dtime, this);
1664 //u8 light = LIGHT_MAX;
1668 v3s16 p = obj->getLightPosition();
1669 MapNode n = m_map->getNode(p);
1670 light = n.getLightBlend(getDayNightRatio());
1672 catch(InvalidPositionException &e) {}
1673 obj->updateLight(light);
1677 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1679 m_map->updateMeshes(blockpos, getDayNightRatio());
1682 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1684 m_map->expireMeshes(only_daynight_diffed);
1687 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1689 core::map<u16, ClientActiveObject*>::Node *n;
1690 n = m_active_objects.find(id);
1693 return n->getValue();
1696 bool isFreeClientActiveObjectId(u16 id,
1697 core::map<u16, ClientActiveObject*> &objects)
1702 for(core::map<u16, ClientActiveObject*>::Iterator
1703 i = objects.getIterator();
1704 i.atEnd()==false; i++)
1706 if(i.getNode()->getKey() == id)
1712 u16 getFreeClientActiveObjectId(
1713 core::map<u16, ClientActiveObject*> &objects)
1718 if(isFreeClientActiveObjectId(new_id, objects))
1728 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1731 if(object->getId() == 0)
1733 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1736 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1737 <<"no free ids available"<<std::endl;
1741 object->setId(new_id);
1743 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1745 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1746 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1750 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1751 <<"added (id="<<object->getId()<<")"<<std::endl;
1752 m_active_objects.insert(object->getId(), object);
1753 object->addToScene(m_smgr);
1754 return object->getId();
1757 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1758 const std::string &init_data)
1760 ClientActiveObject* obj = ClientActiveObject::create(type);
1763 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1764 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1771 addActiveObject(obj);
1773 obj->initialize(init_data);
1776 void ClientEnvironment::removeActiveObject(u16 id)
1778 dstream<<"ClientEnvironment::removeActiveObject(): "
1779 <<"id="<<id<<std::endl;
1780 ClientActiveObject* obj = getActiveObject(id);
1783 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1784 <<"id="<<id<<" not found"<<std::endl;
1787 obj->removeFromScene();
1789 m_active_objects.remove(id);
1792 void ClientEnvironment::processActiveObjectMessage(u16 id,
1793 const std::string &data)
1795 ClientActiveObject* obj = getActiveObject(id);
1798 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1799 <<" got message for id="<<id<<", which doesn't exist."
1803 obj->processMessage(data);
1807 Callbacks for activeobjects
1810 void ClientEnvironment::damageLocalPlayer(u8 damage)
1812 LocalPlayer *lplayer = getLocalPlayer();
1815 if(lplayer->hp > damage)
1816 lplayer->hp -= damage;
1820 ClientEnvEvent event;
1821 event.type = CEE_PLAYER_DAMAGE;
1822 event.player_damage.amount = damage;
1823 m_client_event_queue.push_back(event);
1827 Client likes to call these
1830 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1831 core::array<DistanceSortedActiveObject> &dest)
1833 for(core::map<u16, ClientActiveObject*>::Iterator
1834 i = m_active_objects.getIterator();
1835 i.atEnd()==false; i++)
1837 ClientActiveObject* obj = i.getNode()->getValue();
1839 f32 d = (obj->getPosition() - origin).getLength();
1844 DistanceSortedActiveObject dso(obj, d);
1846 dest.push_back(dso);
1850 ClientEnvEvent ClientEnvironment::getClientEvent()
1852 if(m_client_event_queue.size() == 0)
1854 ClientEnvEvent event;
1855 event.type = CEE_NONE;
1858 return m_client_event_queue.pop_front();
1861 #endif // #ifndef SERVER