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"
26 Environment::Environment():
31 Environment::~Environment()
34 for(core::list<Player*>::Iterator i = m_players.begin();
35 i != m_players.end(); i++)
41 void Environment::addPlayer(Player *player)
43 DSTACK(__FUNCTION_NAME);
45 Check that peer_ids are unique.
46 Also check that names are unique.
47 Exception: there can be multiple players with peer_id=0
49 // If peer id is non-zero, it has to be unique.
50 if(player->peer_id != 0)
51 assert(getPlayer(player->peer_id) == NULL);
52 // Name has to be unique.
53 assert(getPlayer(player->getName()) == NULL);
55 m_players.push_back(player);
58 void Environment::removePlayer(u16 peer_id)
60 DSTACK(__FUNCTION_NAME);
62 for(core::list<Player*>::Iterator i = m_players.begin();
63 i != m_players.end(); i++)
66 if(player->peer_id != peer_id)
71 // See if there is an another one
72 // (shouldn't be, but just to be sure)
77 Player * Environment::getPlayer(u16 peer_id)
79 for(core::list<Player*>::Iterator i = m_players.begin();
80 i != m_players.end(); i++)
83 if(player->peer_id == peer_id)
89 Player * Environment::getPlayer(const char *name)
91 for(core::list<Player*>::Iterator i = m_players.begin();
92 i != m_players.end(); i++)
95 if(strcmp(player->getName(), name) == 0)
101 Player * Environment::getRandomConnectedPlayer()
103 core::list<Player*> connected_players = getPlayers(true);
104 u32 chosen_one = myrand() % connected_players.size();
106 for(core::list<Player*>::Iterator
107 i = connected_players.begin();
108 i != connected_players.end(); i++)
120 Player * Environment::getNearestConnectedPlayer(v3f pos)
122 core::list<Player*> connected_players = getPlayers(true);
124 Player *nearest_player = NULL;
125 for(core::list<Player*>::Iterator
126 i = connected_players.begin();
127 i != connected_players.end(); i++)
130 f32 d = player->getPosition().getDistanceFrom(pos);
131 if(d < nearest_d || nearest_player == NULL)
134 nearest_player = player;
137 return nearest_player;
140 core::list<Player*> Environment::getPlayers()
145 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
147 core::list<Player*> newlist;
148 for(core::list<Player*>::Iterator
149 i = m_players.begin();
150 i != m_players.end(); i++)
154 if(ignore_disconnected)
156 // Ignore disconnected players
157 if(player->peer_id == 0)
161 newlist.push_back(player);
166 void Environment::printPlayers(std::ostream &o)
168 o<<"Players in environment:"<<std::endl;
169 for(core::list<Player*>::Iterator i = m_players.begin();
170 i != m_players.end(); i++)
173 o<<"Player peer_id="<<player->peer_id<<std::endl;
177 /*void Environment::setDayNightRatio(u32 r)
179 getDayNightRatio() = r;
182 u32 Environment::getDayNightRatio()
184 //return getDayNightRatio();
185 return time_to_daynight_ratio(m_time_of_day);
192 void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list)
195 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
196 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
197 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
204 void ActiveBlockList::update(core::list<v3s16> &active_positions,
206 core::map<v3s16, bool> &blocks_removed,
207 core::map<v3s16, bool> &blocks_added)
212 core::map<v3s16, bool> newlist;
213 for(core::list<v3s16>::Iterator i = active_positions.begin();
214 i != active_positions.end(); i++)
216 fillRadiusBlock(*i, radius, newlist);
220 Find out which blocks on the old list are not on the new list
222 // Go through old list
223 for(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
224 i.atEnd()==false; i++)
226 v3s16 p = i.getNode()->getKey();
227 // If not on new list, it's been removed
228 if(newlist.find(p) == NULL)
229 blocks_removed.insert(p, true);
233 Find out which blocks on the new list are not on the old list
235 // Go through new list
236 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
237 i.atEnd()==false; i++)
239 v3s16 p = i.getNode()->getKey();
240 // If not on old list, it's been added
241 if(m_list.find(p) == NULL)
242 blocks_added.insert(p, true);
249 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
250 i.atEnd()==false; i++)
252 v3s16 p = i.getNode()->getKey();
253 m_list.insert(p, true);
261 ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
264 m_random_spawn_timer(3),
265 m_send_recommended_timer(0),
267 m_game_time_fraction_counter(0)
271 ServerEnvironment::~ServerEnvironment()
273 // Clear active block list.
274 // This makes the next one delete all active objects.
275 m_active_blocks.clear();
277 // Convert all objects to static and delete the active objects
278 deactivateFarObjects(true);
284 void ServerEnvironment::serializePlayers(const std::string &savedir)
286 std::string players_path = savedir + "/players";
287 fs::CreateDir(players_path);
289 core::map<Player*, bool> saved_players;
291 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
292 for(u32 i=0; i<player_files.size(); i++)
294 if(player_files[i].dir)
297 // Full path to this file
298 std::string path = players_path + "/" + player_files[i].name;
300 //dstream<<"Checking player file "<<path<<std::endl;
302 // Load player to see what is its name
303 ServerRemotePlayer testplayer;
305 // Open file and deserialize
306 std::ifstream is(path.c_str(), std::ios_base::binary);
307 if(is.good() == false)
309 dstream<<"Failed to read "<<path<<std::endl;
312 testplayer.deSerialize(is);
315 //dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
317 // Search for the player
318 std::string playername = testplayer.getName();
319 Player *player = getPlayer(playername.c_str());
322 dstream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
326 //dstream<<"Found matching player, overwriting."<<std::endl;
328 // OK, found. Save player there.
330 // Open file and serialize
331 std::ofstream os(path.c_str(), std::ios_base::binary);
332 if(os.good() == false)
334 dstream<<"Failed to overwrite "<<path<<std::endl;
337 player->serialize(os);
338 saved_players.insert(player, true);
342 for(core::list<Player*>::Iterator i = m_players.begin();
343 i != m_players.end(); i++)
346 if(saved_players.find(player) != NULL)
348 /*dstream<<"Player "<<player->getName()
349 <<" was already saved."<<std::endl;*/
352 std::string playername = player->getName();
353 // Don't save unnamed player
356 //dstream<<"Not saving unnamed player."<<std::endl;
362 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
363 playername = "player";
364 std::string path = players_path + "/" + playername;
366 for(u32 i=0; i<1000; i++)
368 if(fs::PathExists(path) == false)
373 path = players_path + "/" + playername + itos(i);
377 dstream<<"WARNING: Didn't find free file for player"<<std::endl;
382 /*dstream<<"Saving player "<<player->getName()<<" to "
384 // Open file and serialize
385 std::ofstream os(path.c_str(), std::ios_base::binary);
386 if(os.good() == false)
388 dstream<<"WARNING: Failed to overwrite "<<path<<std::endl;
391 player->serialize(os);
392 saved_players.insert(player, true);
396 //dstream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
399 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
401 std::string players_path = savedir + "/players";
403 core::map<Player*, bool> saved_players;
405 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
406 for(u32 i=0; i<player_files.size(); i++)
408 if(player_files[i].dir)
411 // Full path to this file
412 std::string path = players_path + "/" + player_files[i].name;
414 dstream<<"Checking player file "<<path<<std::endl;
416 // Load player to see what is its name
417 ServerRemotePlayer testplayer;
419 // Open file and deserialize
420 std::ifstream is(path.c_str(), std::ios_base::binary);
421 if(is.good() == false)
423 dstream<<"Failed to read "<<path<<std::endl;
426 testplayer.deSerialize(is);
429 dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
431 // Search for the player
432 std::string playername = testplayer.getName();
433 Player *player = getPlayer(playername.c_str());
434 bool newplayer = false;
437 dstream<<"Is a new player"<<std::endl;
438 player = new ServerRemotePlayer();
444 dstream<<"Reading player "<<testplayer.getName()<<" from "
446 // Open file and deserialize
447 std::ifstream is(path.c_str(), std::ios_base::binary);
448 if(is.good() == false)
450 dstream<<"Failed to read "<<path<<std::endl;
453 player->deSerialize(is);
461 void ServerEnvironment::saveMeta(const std::string &savedir)
463 std::string path = savedir + "/env_meta.txt";
465 // Open file and serialize
466 std::ofstream os(path.c_str(), std::ios_base::binary);
467 if(os.good() == false)
469 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
471 throw SerializationError("Couldn't save env meta");
475 args.setU64("game_time", m_game_time);
476 args.setU64("time_of_day", getTimeOfDay());
481 void ServerEnvironment::loadMeta(const std::string &savedir)
483 std::string path = savedir + "/env_meta.txt";
485 // Open file and deserialize
486 std::ifstream is(path.c_str(), std::ios_base::binary);
487 if(is.good() == false)
489 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
491 throw SerializationError("Couldn't load env meta");
499 throw SerializationError
500 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
502 std::getline(is, line);
503 std::string trimmedline = trim(line);
504 if(trimmedline == "EnvArgsEnd")
506 args.parseConfigLine(line);
510 m_game_time = args.getU64("game_time");
511 }catch(SettingNotFoundException &e){
512 // Getting this is crucial, otherwise timestamps are useless
513 throw SerializationError("Couldn't load env meta game_time");
517 m_time_of_day = args.getU64("time_of_day");
518 }catch(SettingNotFoundException &e){
519 // This is not as important
520 m_time_of_day = 9000;
525 // This is probably very useless
526 void spawnRandomObjects(MapBlock *block)
528 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
529 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
531 bool last_node_walkable = false;
532 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
535 MapNode n = block->getNodeNoEx(p);
536 if(n.d == CONTENT_IGNORE)
538 if(content_features(n.d).liquid_type != LIQUID_NONE)
540 if(content_features(n.d).walkable)
542 last_node_walkable = true;
545 if(last_node_walkable)
547 // If block contains light information
548 if(content_features(n.d).param_type == CPT_LIGHT)
550 if(n.getLight(LIGHTBANK_DAY) <= 5)
552 if(myrand() % 1000 == 0)
554 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
556 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
557 std::string data = obj->getStaticData();
558 StaticObject s_obj(obj->getType(),
559 obj->getBasePosition(), data);
561 block->m_static_objects.insert(0, s_obj);
563 block->setChangedFlag();
568 last_node_walkable = false;
574 void ServerEnvironment::step(float dtime)
576 DSTACK(__FUNCTION_NAME);
578 //TimeTaker timer("ServerEnv step");
581 bool footprints = g_settings.getBool("footprints");
587 m_game_time_fraction_counter += dtime;
588 u32 inc_i = (u32)m_game_time_fraction_counter;
589 m_game_time += inc_i;
590 m_game_time_fraction_counter -= (float)inc_i;
594 Let map update it's timers
597 //TimeTaker timer("Server m_map->timerUpdate()");
598 m_map->timerUpdate(dtime);
604 for(core::list<Player*>::Iterator i = m_players.begin();
605 i != m_players.end(); i++)
609 // Ignore disconnected players
610 if(player->peer_id == 0)
613 v3f playerpos = player->getPosition();
616 player->move(dtime, *m_map, 100*BS);
619 Add footsteps to grass
623 // Get node that is at BS/4 under player
624 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
626 MapNode n = m_map->getNode(bottompos);
627 if(n.d == CONTENT_GRASS)
629 n.d = CONTENT_GRASS_FOOTSTEPS;
630 m_map->setNode(bottompos, n);
633 catch(InvalidPositionException &e)
640 Manage active block list
642 if(m_active_blocks_management_interval.step(dtime, 2.0))
645 Get player block positions
647 core::list<v3s16> players_blockpos;
648 for(core::list<Player*>::Iterator
649 i = m_players.begin();
650 i != m_players.end(); i++)
653 // Ignore disconnected players
654 if(player->peer_id == 0)
656 v3s16 blockpos = getNodeBlockPos(
657 floatToInt(player->getPosition(), BS));
658 players_blockpos.push_back(blockpos);
662 Update list of active blocks, collecting changes
664 const s16 active_block_range = 5;
665 core::map<v3s16, bool> blocks_removed;
666 core::map<v3s16, bool> blocks_added;
667 m_active_blocks.update(players_blockpos, active_block_range,
668 blocks_removed, blocks_added);
671 Handle removed blocks
674 // Convert active objects that are no more in active blocks to static
675 deactivateFarObjects(false);
677 for(core::map<v3s16, bool>::Iterator
678 i = blocks_removed.getIterator();
679 i.atEnd()==false; i++)
681 v3s16 p = i.getNode()->getKey();
683 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
684 <<") became inactive"<<std::endl;*/
686 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
690 // Set current time as timestamp
691 block->setTimestamp(m_game_time);
698 for(core::map<v3s16, bool>::Iterator
699 i = blocks_added.getIterator();
700 i.atEnd()==false; i++)
702 v3s16 p = i.getNode()->getKey();
704 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
705 <<") became active"<<std::endl;*/
707 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
711 // Get time difference
713 u32 stamp = block->getTimestamp();
714 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
715 dtime_s = m_game_time - block->getTimestamp();
717 // Set current time as timestamp
718 block->setTimestamp(m_game_time);
720 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
722 // Activate stored objects
723 activateObjects(block);
725 // TODO: Do something
727 // Here's a quick demonstration
729 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
730 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
731 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
733 v3s16 p = p0 + block->getPosRelative();
734 MapNode n = block->getNodeNoEx(p0);
736 // Convert all mud under proper day lighting to grass
737 if(n.d == CONTENT_MUD)
741 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
742 if(content_features(n_top.d).air_equivalent &&
743 n_top.getLight(LIGHTBANK_DAY) >= 13)
746 m_map->addNodeWithEvent(p, n);
755 Mess around in active blocks
757 if(m_active_blocks_test_interval.step(dtime, 5.0))
759 for(core::map<v3s16, bool>::Iterator
760 i = m_active_blocks.m_list.getIterator();
761 i.atEnd()==false; i++)
763 v3s16 p = i.getNode()->getKey();
765 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
766 <<") being handled"<<std::endl;*/
768 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
772 // Set current time as timestamp
773 block->setTimestamp(m_game_time);
778 Note that map modifications should be done using the event-
779 making map methods so that the server gets information
782 Reading can be done quickly directly from the block.
784 Everything should bind to inside this single content
785 searching loop to keep things fast.
789 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
790 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
791 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
793 v3s16 p = p0 + block->getPosRelative();
794 MapNode n = block->getNodeNoEx(p0);
796 // Convert mud under proper lighting to grass
797 if(n.d == CONTENT_MUD)
801 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
802 if(content_features(n_top.d).air_equivalent &&
803 n_top.getLightBlend(getDayNightRatio()) >= 13)
806 m_map->addNodeWithEvent(p, n);
818 //TimeTaker timer("Step active objects");
820 // This helps the objects to send data at the same time
821 bool send_recommended = false;
822 m_send_recommended_timer += dtime;
823 if(m_send_recommended_timer > 0.15)
825 m_send_recommended_timer = 0;
826 send_recommended = true;
829 for(core::map<u16, ServerActiveObject*>::Iterator
830 i = m_active_objects.getIterator();
831 i.atEnd()==false; i++)
833 ServerActiveObject* obj = i.getNode()->getValue();
834 // Don't step if is to be removed or stored statically
835 if(obj->m_removed || obj->m_pending_deactivation)
837 // Step object, putting messages directly to the queue
838 obj->step(dtime, m_active_object_messages, send_recommended);
843 Manage active objects
845 if(m_object_management_interval.step(dtime, 0.5))
848 Remove objects that satisfy (m_removed && m_known_by_count==0)
850 removeRemovedObjects();
853 if(g_settings.getBool("enable_experimental"))
860 m_random_spawn_timer -= dtime;
861 if(m_random_spawn_timer < 0)
863 //m_random_spawn_timer += myrand_range(2.0, 20.0);
864 //m_random_spawn_timer += 2.0;
865 m_random_spawn_timer += 200.0;
871 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
872 s16 y = 1 + getServerMap().findGroundLevel(p2d);
873 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
875 Player *player = getRandomConnectedPlayer();
878 pos = player->getPosition();
880 myrand_range(-3,3)*BS,
882 myrand_range(-3,3)*BS
886 Create a ServerActiveObject
889 //TestSAO *obj = new TestSAO(this, 0, pos);
890 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
891 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
892 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
893 addActiveObject(obj);
897 } // enable_experimental
900 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
902 core::map<u16, ServerActiveObject*>::Node *n;
903 n = m_active_objects.find(id);
906 return n->getValue();
909 bool isFreeServerActiveObjectId(u16 id,
910 core::map<u16, ServerActiveObject*> &objects)
915 for(core::map<u16, ServerActiveObject*>::Iterator
916 i = objects.getIterator();
917 i.atEnd()==false; i++)
919 if(i.getNode()->getKey() == id)
925 u16 getFreeServerActiveObjectId(
926 core::map<u16, ServerActiveObject*> &objects)
931 if(isFreeServerActiveObjectId(new_id, objects))
941 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
944 if(object->getId() == 0)
946 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
949 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
950 <<"no free ids available"<<std::endl;
954 object->setId(new_id);
956 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
958 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
959 <<"id is not free ("<<object->getId()<<")"<<std::endl;
963 /*dstream<<"INGO: ServerEnvironment::addActiveObject(): "
964 <<"added (id="<<object->getId()<<")"<<std::endl;*/
966 m_active_objects.insert(object->getId(), object);
968 // Add static object to active static list of the block
969 v3f objectpos = object->getBasePosition();
970 std::string staticdata = object->getStaticData();
971 StaticObject s_obj(object->getType(), objectpos, staticdata);
972 // Add to the block where the object is located in
973 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
974 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
977 block->m_static_objects.m_active.insert(object->getId(), s_obj);
978 object->m_static_exists = true;
979 object->m_static_block = blockpos;
982 dstream<<"WARNING: Server: Could not find a block for "
983 <<"storing newly added static active object"<<std::endl;
986 return object->getId();
990 Finds out what new objects have been added to
991 inside a radius around a position
993 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
994 core::map<u16, bool> ¤t_objects,
995 core::map<u16, bool> &added_objects)
997 v3f pos_f = intToFloat(pos, BS);
998 f32 radius_f = radius * BS;
1000 Go through the object list,
1001 - discard m_removed objects,
1002 - discard objects that are too far away,
1003 - discard objects that are found in current_objects.
1004 - add remaining objects to added_objects
1006 for(core::map<u16, ServerActiveObject*>::Iterator
1007 i = m_active_objects.getIterator();
1008 i.atEnd()==false; i++)
1010 u16 id = i.getNode()->getKey();
1012 ServerActiveObject *object = i.getNode()->getValue();
1015 // Discard if removed
1016 if(object->m_removed)
1018 // Discard if too far
1019 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1020 if(distance_f > radius_f)
1022 // Discard if already on current_objects
1023 core::map<u16, bool>::Node *n;
1024 n = current_objects.find(id);
1027 // Add to added_objects
1028 added_objects.insert(id, false);
1033 Finds out what objects have been removed from
1034 inside a radius around a position
1036 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1037 core::map<u16, bool> ¤t_objects,
1038 core::map<u16, bool> &removed_objects)
1040 v3f pos_f = intToFloat(pos, BS);
1041 f32 radius_f = radius * BS;
1043 Go through current_objects; object is removed if:
1044 - object is not found in m_active_objects (this is actually an
1045 error condition; objects should be set m_removed=true and removed
1046 only after all clients have been informed about removal), or
1047 - object has m_removed=true, or
1048 - object is too far away
1050 for(core::map<u16, bool>::Iterator
1051 i = current_objects.getIterator();
1052 i.atEnd()==false; i++)
1054 u16 id = i.getNode()->getKey();
1055 ServerActiveObject *object = getActiveObject(id);
1058 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1059 <<" object in current_objects is NULL"<<std::endl;
1061 else if(object->m_removed == false)
1063 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1064 /*dstream<<"removed == false"
1065 <<"distance_f = "<<distance_f
1066 <<", radius_f = "<<radius_f<<std::endl;*/
1067 if(distance_f < radius_f)
1073 removed_objects.insert(id, false);
1077 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1079 if(m_active_object_messages.size() == 0)
1080 return ActiveObjectMessage(0);
1082 return m_active_object_messages.pop_front();
1086 ************ Private methods *************
1090 Remove objects that satisfy (m_removed && m_known_by_count==0)
1092 void ServerEnvironment::removeRemovedObjects()
1094 core::list<u16> objects_to_remove;
1095 for(core::map<u16, ServerActiveObject*>::Iterator
1096 i = m_active_objects.getIterator();
1097 i.atEnd()==false; i++)
1099 u16 id = i.getNode()->getKey();
1100 ServerActiveObject* obj = i.getNode()->getValue();
1101 // This shouldn't happen but check it
1104 dstream<<"WARNING: NULL object found in ServerEnvironment"
1105 <<" while finding removed objects. id="<<id<<std::endl;
1106 // Id to be removed from m_active_objects
1107 objects_to_remove.push_back(id);
1112 We will delete objects that are marked as removed or thatare
1113 waiting for deletion after deactivation
1115 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1119 Delete static data from block if is marked as removed
1121 if(obj->m_static_exists && obj->m_removed)
1123 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1126 block->m_static_objects.remove(id);
1127 block->setChangedFlag();
1131 // If m_known_by_count > 0, don't actually remove.
1132 if(obj->m_known_by_count > 0)
1137 // Id to be removed from m_active_objects
1138 objects_to_remove.push_back(id);
1140 // Remove references from m_active_objects
1141 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1142 i != objects_to_remove.end(); i++)
1144 m_active_objects.remove(*i);
1149 Convert stored objects from blocks near the players to active.
1151 void ServerEnvironment::activateObjects(MapBlock *block)
1155 // Ignore if no stored objects (to not set changed flag)
1156 if(block->m_static_objects.m_stored.size() == 0)
1158 // A list for objects that couldn't be converted to static for some
1159 // reason. They will be stored back.
1160 core::list<StaticObject> new_stored;
1161 // Loop through stored static objects
1162 for(core::list<StaticObject>::Iterator
1163 i = block->m_static_objects.m_stored.begin();
1164 i != block->m_static_objects.m_stored.end(); i++)
1166 /*dstream<<"INFO: Server: Creating an active object from "
1167 <<"static data"<<std::endl;*/
1168 StaticObject &s_obj = *i;
1169 // Create an active object from the data
1170 ServerActiveObject *obj = ServerActiveObject::create
1171 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1172 // If couldn't create object, store static data back.
1175 new_stored.push_back(s_obj);
1178 // This will also add the object to the active static list
1179 addActiveObject(obj);
1180 //u16 id = addActiveObject(obj);
1182 // Clear stored list
1183 block->m_static_objects.m_stored.clear();
1184 // Add leftover failed stuff to stored list
1185 for(core::list<StaticObject>::Iterator
1186 i = new_stored.begin();
1187 i != new_stored.end(); i++)
1189 StaticObject &s_obj = *i;
1190 block->m_static_objects.m_stored.push_back(s_obj);
1192 // Block has been modified
1193 block->setChangedFlag();
1197 Convert objects that are not in active blocks to static.
1199 If m_known_by_count != 0, active object is not deleted, but static
1200 data is still updated.
1202 If force_delete is set, active object is deleted nevertheless. It
1203 shall only be set so in the destructor of the environment.
1205 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1207 core::list<u16> objects_to_remove;
1208 for(core::map<u16, ServerActiveObject*>::Iterator
1209 i = m_active_objects.getIterator();
1210 i.atEnd()==false; i++)
1212 ServerActiveObject* obj = i.getNode()->getValue();
1213 u16 id = i.getNode()->getKey();
1214 v3f objectpos = obj->getBasePosition();
1216 // This shouldn't happen but check it
1219 dstream<<"WARNING: NULL object found in ServerEnvironment"
1225 // The block in which the object resides in
1226 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1228 // If block is active, don't remove
1229 if(m_active_blocks.contains(blockpos_o))
1233 Update the static data
1236 // Delete old static object
1237 MapBlock *oldblock = NULL;
1238 if(obj->m_static_exists)
1240 MapBlock *block = m_map->getBlockNoCreateNoEx
1241 (obj->m_static_block);
1244 block->m_static_objects.remove(id);
1248 // Create new static object
1249 std::string staticdata = obj->getStaticData();
1250 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1251 // Add to the block where the object is located in
1252 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1253 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1256 block->m_static_objects.insert(0, s_obj);
1257 block->setChangedFlag();
1258 obj->m_static_exists = true;
1259 obj->m_static_block = block->getPos();
1261 // If not possible, add back to previous block
1264 oldblock->m_static_objects.insert(0, s_obj);
1265 oldblock->setChangedFlag();
1266 obj->m_static_exists = true;
1267 obj->m_static_block = oldblock->getPos();
1270 dstream<<"WARNING: Server: Could not find a block for "
1271 <<"storing static object"<<std::endl;
1272 obj->m_static_exists = false;
1277 Delete active object if not known by some client,
1278 else set pending deactivation
1281 // If known by some client, don't delete.
1282 if(obj->m_known_by_count > 0 && force_delete == false)
1284 obj->m_pending_deactivation = true;
1288 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1290 // Delete active object
1292 // Id to be removed from m_active_objects
1293 objects_to_remove.push_back(id);
1296 // Remove references from m_active_objects
1297 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1298 i != objects_to_remove.end(); i++)
1300 m_active_objects.remove(*i);
1311 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1319 ClientEnvironment::~ClientEnvironment()
1321 // delete active objects
1322 for(core::map<u16, ClientActiveObject*>::Iterator
1323 i = m_active_objects.getIterator();
1324 i.atEnd()==false; i++)
1326 delete i.getNode()->getValue();
1333 void ClientEnvironment::addPlayer(Player *player)
1335 DSTACK(__FUNCTION_NAME);
1337 It is a failure if player is local and there already is a local
1340 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1342 Environment::addPlayer(player);
1345 LocalPlayer * ClientEnvironment::getLocalPlayer()
1347 for(core::list<Player*>::Iterator i = m_players.begin();
1348 i != m_players.end(); i++)
1350 Player *player = *i;
1351 if(player->isLocal())
1352 return (LocalPlayer*)player;
1357 void ClientEnvironment::step(float dtime)
1359 DSTACK(__FUNCTION_NAME);
1361 // Get some settings
1362 bool free_move = g_settings.getBool("free_move");
1363 bool footprints = g_settings.getBool("footprints");
1366 //TimeTaker timer("Client m_map->timerUpdate()");
1367 m_map->timerUpdate(dtime);
1371 LocalPlayer *lplayer = getLocalPlayer();
1373 // collision info queue
1374 core::list<CollisionInfo> player_collisions;
1377 Get the speed the player is going
1379 f32 player_speed = 0.001; // just some small value
1380 player_speed = lplayer->getSpeed().getLength();
1383 Maximum position increment
1385 //f32 position_max_increment = 0.05*BS;
1386 f32 position_max_increment = 0.1*BS;
1388 // Maximum time increment (for collision detection etc)
1389 // time = distance / speed
1390 f32 dtime_max_increment = position_max_increment / player_speed;
1392 // Maximum time increment is 10ms or lower
1393 if(dtime_max_increment > 0.01)
1394 dtime_max_increment = 0.01;
1396 // Don't allow overly huge dtime
1400 f32 dtime_downcount = dtime;
1403 Stuff that has a maximum time increment
1412 if(dtime_downcount > dtime_max_increment)
1414 dtime_part = dtime_max_increment;
1415 dtime_downcount -= dtime_part;
1419 dtime_part = dtime_downcount;
1421 Setting this to 0 (no -=dtime_part) disables an infinite loop
1422 when dtime_part is so small that dtime_downcount -= dtime_part
1425 dtime_downcount = 0;
1433 v3f lplayerpos = lplayer->getPosition();
1436 if(free_move == false)
1439 v3f speed = lplayer->getSpeed();
1440 if(lplayer->swimming_up == false)
1441 speed.Y -= 9.81 * BS * dtime_part * 2;
1444 if(lplayer->in_water_stable || lplayer->in_water)
1446 f32 max_down = 2.0*BS;
1447 if(speed.Y < -max_down) speed.Y = -max_down;
1450 if(speed.getLength() > max)
1452 speed = speed / speed.getLength() * max;
1456 lplayer->setSpeed(speed);
1461 This also does collision detection.
1463 lplayer->move(dtime_part, *m_map, position_max_increment,
1464 &player_collisions);
1467 while(dtime_downcount > 0.001);
1469 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1471 for(core::list<CollisionInfo>::Iterator
1472 i = player_collisions.begin();
1473 i != player_collisions.end(); i++)
1475 CollisionInfo &info = *i;
1476 if(info.t == COLLISION_FALL)
1478 //f32 tolerance = BS*10; // 2 without damage
1479 f32 tolerance = BS*12; // 3 without damage
1481 if(info.speed > tolerance)
1483 f32 damage_f = (info.speed - tolerance)/BS*factor;
1484 u16 damage = (u16)(damage_f+0.5);
1485 if(lplayer->hp > damage)
1486 lplayer->hp -= damage;
1490 ClientEnvEvent event;
1491 event.type = CEE_PLAYER_DAMAGE;
1492 event.player_damage.amount = damage;
1493 m_client_event_queue.push_back(event);
1499 Stuff that can be done in an arbitarily large dtime
1501 for(core::list<Player*>::Iterator i = m_players.begin();
1502 i != m_players.end(); i++)
1504 Player *player = *i;
1505 v3f playerpos = player->getPosition();
1508 Handle non-local players
1510 if(player->isLocal() == false)
1513 player->move(dtime, *m_map, 100*BS);
1515 // Update lighting on remote players on client
1516 u8 light = LIGHT_MAX;
1519 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1520 MapNode n = m_map->getNode(p);
1521 light = n.getLightBlend(getDayNightRatio());
1523 catch(InvalidPositionException &e) {}
1524 player->updateLight(light);
1528 Add footsteps to grass
1532 // Get node that is at BS/4 under player
1533 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1535 MapNode n = m_map->getNode(bottompos);
1536 if(n.d == CONTENT_GRASS)
1538 n.d = CONTENT_GRASS_FOOTSTEPS;
1539 m_map->setNode(bottompos, n);
1540 // Update mesh on client
1541 if(m_map->mapType() == MAPTYPE_CLIENT)
1543 v3s16 p_blocks = getNodeBlockPos(bottompos);
1544 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1545 //b->updateMesh(getDayNightRatio());
1546 b->setMeshExpired(true);
1550 catch(InvalidPositionException &e)
1557 Step active objects and update lighting of them
1560 for(core::map<u16, ClientActiveObject*>::Iterator
1561 i = m_active_objects.getIterator();
1562 i.atEnd()==false; i++)
1564 ClientActiveObject* obj = i.getNode()->getValue();
1566 obj->step(dtime, this);
1568 //u8 light = LIGHT_MAX;
1572 v3s16 p = obj->getLightPosition();
1573 MapNode n = m_map->getNode(p);
1574 light = n.getLightBlend(getDayNightRatio());
1576 catch(InvalidPositionException &e) {}
1577 obj->updateLight(light);
1581 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1583 m_map->updateMeshes(blockpos, getDayNightRatio());
1586 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1588 m_map->expireMeshes(only_daynight_diffed);
1591 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1593 core::map<u16, ClientActiveObject*>::Node *n;
1594 n = m_active_objects.find(id);
1597 return n->getValue();
1600 bool isFreeClientActiveObjectId(u16 id,
1601 core::map<u16, ClientActiveObject*> &objects)
1606 for(core::map<u16, ClientActiveObject*>::Iterator
1607 i = objects.getIterator();
1608 i.atEnd()==false; i++)
1610 if(i.getNode()->getKey() == id)
1616 u16 getFreeClientActiveObjectId(
1617 core::map<u16, ClientActiveObject*> &objects)
1622 if(isFreeClientActiveObjectId(new_id, objects))
1632 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1635 if(object->getId() == 0)
1637 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1640 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1641 <<"no free ids available"<<std::endl;
1645 object->setId(new_id);
1647 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1649 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1650 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1654 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1655 <<"added (id="<<object->getId()<<")"<<std::endl;
1656 m_active_objects.insert(object->getId(), object);
1657 object->addToScene(m_smgr);
1658 return object->getId();
1661 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1662 const std::string &init_data)
1664 ClientActiveObject* obj = ClientActiveObject::create(type);
1667 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1668 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1675 addActiveObject(obj);
1677 obj->initialize(init_data);
1680 void ClientEnvironment::removeActiveObject(u16 id)
1682 dstream<<"ClientEnvironment::removeActiveObject(): "
1683 <<"id="<<id<<std::endl;
1684 ClientActiveObject* obj = getActiveObject(id);
1687 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1688 <<"id="<<id<<" not found"<<std::endl;
1691 obj->removeFromScene();
1693 m_active_objects.remove(id);
1696 void ClientEnvironment::processActiveObjectMessage(u16 id,
1697 const std::string &data)
1699 ClientActiveObject* obj = getActiveObject(id);
1702 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1703 <<" got message for id="<<id<<", which doesn't exist."
1707 obj->processMessage(data);
1711 Callbacks for activeobjects
1714 void ClientEnvironment::damageLocalPlayer(u8 damage)
1716 LocalPlayer *lplayer = getLocalPlayer();
1719 if(lplayer->hp > damage)
1720 lplayer->hp -= damage;
1724 ClientEnvEvent event;
1725 event.type = CEE_PLAYER_DAMAGE;
1726 event.player_damage.amount = damage;
1727 m_client_event_queue.push_back(event);
1731 Client likes to call these
1734 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1735 core::array<DistanceSortedActiveObject> &dest)
1737 for(core::map<u16, ClientActiveObject*>::Iterator
1738 i = m_active_objects.getIterator();
1739 i.atEnd()==false; i++)
1741 ClientActiveObject* obj = i.getNode()->getValue();
1743 f32 d = (obj->getPosition() - origin).getLength();
1748 DistanceSortedActiveObject dso(obj, d);
1750 dest.push_back(dso);
1754 ClientEnvEvent ClientEnvironment::getClientEvent()
1756 if(m_client_event_queue.size() == 0)
1758 ClientEnvEvent event;
1759 event.type = CEE_NONE;
1762 return m_client_event_queue.pop_front();
1765 #endif // #ifndef SERVER