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 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
431 dstream<<"Not loading player with invalid name: "
432 <<testplayer.getName()<<std::endl;
435 dstream<<"Loaded test player with name "<<testplayer.getName()
438 // Search for the player
439 std::string playername = testplayer.getName();
440 Player *player = getPlayer(playername.c_str());
441 bool newplayer = false;
444 dstream<<"Is a new player"<<std::endl;
445 player = new ServerRemotePlayer();
451 dstream<<"Reading player "<<testplayer.getName()<<" from "
453 // Open file and deserialize
454 std::ifstream is(path.c_str(), std::ios_base::binary);
455 if(is.good() == false)
457 dstream<<"Failed to read "<<path<<std::endl;
460 player->deSerialize(is);
468 void ServerEnvironment::saveMeta(const std::string &savedir)
470 std::string path = savedir + "/env_meta.txt";
472 // Open file and serialize
473 std::ofstream os(path.c_str(), std::ios_base::binary);
474 if(os.good() == false)
476 dstream<<"WARNING: ServerEnvironment::saveMeta(): Failed to open "
478 throw SerializationError("Couldn't save env meta");
482 args.setU64("game_time", m_game_time);
483 args.setU64("time_of_day", getTimeOfDay());
488 void ServerEnvironment::loadMeta(const std::string &savedir)
490 std::string path = savedir + "/env_meta.txt";
492 // Open file and deserialize
493 std::ifstream is(path.c_str(), std::ios_base::binary);
494 if(is.good() == false)
496 dstream<<"WARNING: ServerEnvironment::loadMeta(): Failed to open "
498 throw SerializationError("Couldn't load env meta");
506 throw SerializationError
507 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
509 std::getline(is, line);
510 std::string trimmedline = trim(line);
511 if(trimmedline == "EnvArgsEnd")
513 args.parseConfigLine(line);
517 m_game_time = args.getU64("game_time");
518 }catch(SettingNotFoundException &e){
519 // Getting this is crucial, otherwise timestamps are useless
520 throw SerializationError("Couldn't load env meta game_time");
524 m_time_of_day = args.getU64("time_of_day");
525 }catch(SettingNotFoundException &e){
526 // This is not as important
527 m_time_of_day = 9000;
532 // This is probably very useless
533 void spawnRandomObjects(MapBlock *block)
535 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
536 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
538 bool last_node_walkable = false;
539 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
542 MapNode n = block->getNodeNoEx(p);
543 if(n.d == CONTENT_IGNORE)
545 if(content_features(n.d).liquid_type != LIQUID_NONE)
547 if(content_features(n.d).walkable)
549 last_node_walkable = true;
552 if(last_node_walkable)
554 // If block contains light information
555 if(content_features(n.d).param_type == CPT_LIGHT)
557 if(n.getLight(LIGHTBANK_DAY) <= 5)
559 if(myrand() % 1000 == 0)
561 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
563 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
564 std::string data = obj->getStaticData();
565 StaticObject s_obj(obj->getType(),
566 obj->getBasePosition(), data);
568 block->m_static_objects.insert(0, s_obj);
570 block->setChangedFlag();
575 last_node_walkable = false;
581 void ServerEnvironment::step(float dtime)
583 DSTACK(__FUNCTION_NAME);
585 //TimeTaker timer("ServerEnv step");
588 bool footprints = g_settings.getBool("footprints");
594 m_game_time_fraction_counter += dtime;
595 u32 inc_i = (u32)m_game_time_fraction_counter;
596 m_game_time += inc_i;
597 m_game_time_fraction_counter -= (float)inc_i;
601 Let map update it's timers
604 //TimeTaker timer("Server m_map->timerUpdate()");
605 m_map->timerUpdate(dtime);
611 for(core::list<Player*>::Iterator i = m_players.begin();
612 i != m_players.end(); i++)
616 // Ignore disconnected players
617 if(player->peer_id == 0)
620 v3f playerpos = player->getPosition();
623 player->move(dtime, *m_map, 100*BS);
626 Add footsteps to grass
630 // Get node that is at BS/4 under player
631 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
633 MapNode n = m_map->getNode(bottompos);
634 if(n.d == CONTENT_GRASS)
636 n.d = CONTENT_GRASS_FOOTSTEPS;
637 m_map->setNode(bottompos, n);
640 catch(InvalidPositionException &e)
647 Manage active block list
649 if(m_active_blocks_management_interval.step(dtime, 2.0))
652 Get player block positions
654 core::list<v3s16> players_blockpos;
655 for(core::list<Player*>::Iterator
656 i = m_players.begin();
657 i != m_players.end(); i++)
660 // Ignore disconnected players
661 if(player->peer_id == 0)
663 v3s16 blockpos = getNodeBlockPos(
664 floatToInt(player->getPosition(), BS));
665 players_blockpos.push_back(blockpos);
669 Update list of active blocks, collecting changes
671 const s16 active_block_range = 5;
672 core::map<v3s16, bool> blocks_removed;
673 core::map<v3s16, bool> blocks_added;
674 m_active_blocks.update(players_blockpos, active_block_range,
675 blocks_removed, blocks_added);
678 Handle removed blocks
681 // Convert active objects that are no more in active blocks to static
682 deactivateFarObjects(false);
684 for(core::map<v3s16, bool>::Iterator
685 i = blocks_removed.getIterator();
686 i.atEnd()==false; i++)
688 v3s16 p = i.getNode()->getKey();
690 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
691 <<") became inactive"<<std::endl;*/
693 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
697 // Set current time as timestamp
698 block->setTimestamp(m_game_time);
705 for(core::map<v3s16, bool>::Iterator
706 i = blocks_added.getIterator();
707 i.atEnd()==false; i++)
709 v3s16 p = i.getNode()->getKey();
711 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
712 <<") became active"<<std::endl;*/
714 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
718 // Get time difference
720 u32 stamp = block->getTimestamp();
721 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
722 dtime_s = m_game_time - block->getTimestamp();
724 // Set current time as timestamp
725 block->setTimestamp(m_game_time);
727 //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
729 // Activate stored objects
730 activateObjects(block);
733 bool changed = block->m_node_metadata.step((float)dtime_s);
737 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
739 m_map->dispatchEvent(&event);
742 // TODO: Do something
743 // TODO: Implement usage of ActiveBlockModifier
745 // Here's a quick demonstration
747 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
748 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
749 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
751 v3s16 p = p0 + block->getPosRelative();
752 MapNode n = block->getNodeNoEx(p0);
754 // Convert all mud under proper day lighting to grass
755 if(n.d == CONTENT_MUD)
759 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
760 if(content_features(n_top.d).air_equivalent &&
761 n_top.getLight(LIGHTBANK_DAY) >= 13)
764 m_map->addNodeWithEvent(p, n);
773 Mess around in active blocks
775 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
779 for(core::map<v3s16, bool>::Iterator
780 i = m_active_blocks.m_list.getIterator();
781 i.atEnd()==false; i++)
783 v3s16 p = i.getNode()->getKey();
785 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
786 <<") being handled"<<std::endl;*/
788 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
792 // Set current time as timestamp
793 block->setTimestamp(m_game_time);
796 bool changed = block->m_node_metadata.step(dtime);
800 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
802 m_map->dispatchEvent(&event);
806 if(m_active_blocks_test_interval.step(dtime, 10.0))
808 //float dtime = 10.0;
810 for(core::map<v3s16, bool>::Iterator
811 i = m_active_blocks.m_list.getIterator();
812 i.atEnd()==false; i++)
814 v3s16 p = i.getNode()->getKey();
816 /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
817 <<") being handled"<<std::endl;*/
819 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
823 // Set current time as timestamp
824 block->setTimestamp(m_game_time);
829 Note that map modifications should be done using the event-
830 making map methods so that the server gets information
833 Reading can be done quickly directly from the block.
835 Everything should bind to inside this single content
836 searching loop to keep things fast.
838 // TODO: Implement usage of ActiveBlockModifier
841 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
842 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
843 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
845 v3s16 p = p0 + block->getPosRelative();
846 MapNode n = block->getNodeNoEx(p0);
850 Convert mud under proper lighting to grass
852 if(n.d == CONTENT_MUD)
856 MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
857 if(content_features(n_top.d).air_equivalent &&
858 n_top.getLightBlend(getDayNightRatio()) >= 13)
861 m_map->addNodeWithEvent(p, n);
873 //TimeTaker timer("Step active objects");
875 // This helps the objects to send data at the same time
876 bool send_recommended = false;
877 m_send_recommended_timer += dtime;
878 if(m_send_recommended_timer > 0.15)
880 m_send_recommended_timer = 0;
881 send_recommended = true;
884 for(core::map<u16, ServerActiveObject*>::Iterator
885 i = m_active_objects.getIterator();
886 i.atEnd()==false; i++)
888 ServerActiveObject* obj = i.getNode()->getValue();
889 // Don't step if is to be removed or stored statically
890 if(obj->m_removed || obj->m_pending_deactivation)
892 // Step object, putting messages directly to the queue
893 obj->step(dtime, m_active_object_messages, send_recommended);
898 Manage active objects
900 if(m_object_management_interval.step(dtime, 0.5))
903 Remove objects that satisfy (m_removed && m_known_by_count==0)
905 removeRemovedObjects();
908 if(g_settings.getBool("enable_experimental"))
915 m_random_spawn_timer -= dtime;
916 if(m_random_spawn_timer < 0)
918 //m_random_spawn_timer += myrand_range(2.0, 20.0);
919 //m_random_spawn_timer += 2.0;
920 m_random_spawn_timer += 200.0;
926 /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
927 s16 y = 1 + getServerMap().findGroundLevel(p2d);
928 v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
930 Player *player = getRandomConnectedPlayer();
933 pos = player->getPosition();
935 myrand_range(-3,3)*BS,
937 myrand_range(-3,3)*BS
941 Create a ServerActiveObject
944 //TestSAO *obj = new TestSAO(this, 0, pos);
945 //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
946 //ServerActiveObject *obj = new RatSAO(this, 0, pos);
947 ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
948 addActiveObject(obj);
952 } // enable_experimental
955 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
957 core::map<u16, ServerActiveObject*>::Node *n;
958 n = m_active_objects.find(id);
961 return n->getValue();
964 bool isFreeServerActiveObjectId(u16 id,
965 core::map<u16, ServerActiveObject*> &objects)
970 for(core::map<u16, ServerActiveObject*>::Iterator
971 i = objects.getIterator();
972 i.atEnd()==false; i++)
974 if(i.getNode()->getKey() == id)
980 u16 getFreeServerActiveObjectId(
981 core::map<u16, ServerActiveObject*> &objects)
986 if(isFreeServerActiveObjectId(new_id, objects))
996 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
999 if(object->getId() == 0)
1001 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1004 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
1005 <<"no free ids available"<<std::endl;
1009 object->setId(new_id);
1011 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1013 dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
1014 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1018 /*dstream<<"INGO: ServerEnvironment::addActiveObject(): "
1019 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1021 m_active_objects.insert(object->getId(), object);
1023 // Add static object to active static list of the block
1024 v3f objectpos = object->getBasePosition();
1025 std::string staticdata = object->getStaticData();
1026 StaticObject s_obj(object->getType(), objectpos, staticdata);
1027 // Add to the block where the object is located in
1028 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1029 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1032 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1033 object->m_static_exists = true;
1034 object->m_static_block = blockpos;
1037 dstream<<"WARNING: Server: Could not find a block for "
1038 <<"storing newly added static active object"<<std::endl;
1041 return object->getId();
1045 Finds out what new objects have been added to
1046 inside a radius around a position
1048 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1049 core::map<u16, bool> ¤t_objects,
1050 core::map<u16, bool> &added_objects)
1052 v3f pos_f = intToFloat(pos, BS);
1053 f32 radius_f = radius * BS;
1055 Go through the object list,
1056 - discard m_removed objects,
1057 - discard objects that are too far away,
1058 - discard objects that are found in current_objects.
1059 - add remaining objects to added_objects
1061 for(core::map<u16, ServerActiveObject*>::Iterator
1062 i = m_active_objects.getIterator();
1063 i.atEnd()==false; i++)
1065 u16 id = i.getNode()->getKey();
1067 ServerActiveObject *object = i.getNode()->getValue();
1070 // Discard if removed
1071 if(object->m_removed)
1073 // Discard if too far
1074 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1075 if(distance_f > radius_f)
1077 // Discard if already on current_objects
1078 core::map<u16, bool>::Node *n;
1079 n = current_objects.find(id);
1082 // Add to added_objects
1083 added_objects.insert(id, false);
1088 Finds out what objects have been removed from
1089 inside a radius around a position
1091 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1092 core::map<u16, bool> ¤t_objects,
1093 core::map<u16, bool> &removed_objects)
1095 v3f pos_f = intToFloat(pos, BS);
1096 f32 radius_f = radius * BS;
1098 Go through current_objects; object is removed if:
1099 - object is not found in m_active_objects (this is actually an
1100 error condition; objects should be set m_removed=true and removed
1101 only after all clients have been informed about removal), or
1102 - object has m_removed=true, or
1103 - object is too far away
1105 for(core::map<u16, bool>::Iterator
1106 i = current_objects.getIterator();
1107 i.atEnd()==false; i++)
1109 u16 id = i.getNode()->getKey();
1110 ServerActiveObject *object = getActiveObject(id);
1113 dstream<<"WARNING: ServerEnvironment::getRemovedActiveObjects():"
1114 <<" object in current_objects is NULL"<<std::endl;
1116 else if(object->m_removed == false)
1118 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1119 /*dstream<<"removed == false"
1120 <<"distance_f = "<<distance_f
1121 <<", radius_f = "<<radius_f<<std::endl;*/
1122 if(distance_f < radius_f)
1128 removed_objects.insert(id, false);
1132 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1134 if(m_active_object_messages.size() == 0)
1135 return ActiveObjectMessage(0);
1137 return m_active_object_messages.pop_front();
1141 ************ Private methods *************
1145 Remove objects that satisfy (m_removed && m_known_by_count==0)
1147 void ServerEnvironment::removeRemovedObjects()
1149 core::list<u16> objects_to_remove;
1150 for(core::map<u16, ServerActiveObject*>::Iterator
1151 i = m_active_objects.getIterator();
1152 i.atEnd()==false; i++)
1154 u16 id = i.getNode()->getKey();
1155 ServerActiveObject* obj = i.getNode()->getValue();
1156 // This shouldn't happen but check it
1159 dstream<<"WARNING: NULL object found in ServerEnvironment"
1160 <<" while finding removed objects. id="<<id<<std::endl;
1161 // Id to be removed from m_active_objects
1162 objects_to_remove.push_back(id);
1167 We will delete objects that are marked as removed or thatare
1168 waiting for deletion after deactivation
1170 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1174 Delete static data from block if is marked as removed
1176 if(obj->m_static_exists && obj->m_removed)
1178 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1181 block->m_static_objects.remove(id);
1182 block->setChangedFlag();
1186 // If m_known_by_count > 0, don't actually remove.
1187 if(obj->m_known_by_count > 0)
1192 // Id to be removed from m_active_objects
1193 objects_to_remove.push_back(id);
1195 // Remove references from m_active_objects
1196 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1197 i != objects_to_remove.end(); i++)
1199 m_active_objects.remove(*i);
1204 Convert stored objects from blocks near the players to active.
1206 void ServerEnvironment::activateObjects(MapBlock *block)
1210 // Ignore if no stored objects (to not set changed flag)
1211 if(block->m_static_objects.m_stored.size() == 0)
1213 // A list for objects that couldn't be converted to static for some
1214 // reason. They will be stored back.
1215 core::list<StaticObject> new_stored;
1216 // Loop through stored static objects
1217 for(core::list<StaticObject>::Iterator
1218 i = block->m_static_objects.m_stored.begin();
1219 i != block->m_static_objects.m_stored.end(); i++)
1221 /*dstream<<"INFO: Server: Creating an active object from "
1222 <<"static data"<<std::endl;*/
1223 StaticObject &s_obj = *i;
1224 // Create an active object from the data
1225 ServerActiveObject *obj = ServerActiveObject::create
1226 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1227 // If couldn't create object, store static data back.
1230 new_stored.push_back(s_obj);
1233 // This will also add the object to the active static list
1234 addActiveObject(obj);
1235 //u16 id = addActiveObject(obj);
1237 // Clear stored list
1238 block->m_static_objects.m_stored.clear();
1239 // Add leftover failed stuff to stored list
1240 for(core::list<StaticObject>::Iterator
1241 i = new_stored.begin();
1242 i != new_stored.end(); i++)
1244 StaticObject &s_obj = *i;
1245 block->m_static_objects.m_stored.push_back(s_obj);
1247 // Block has been modified
1248 block->setChangedFlag();
1252 Convert objects that are not in active blocks to static.
1254 If m_known_by_count != 0, active object is not deleted, but static
1255 data is still updated.
1257 If force_delete is set, active object is deleted nevertheless. It
1258 shall only be set so in the destructor of the environment.
1260 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1262 core::list<u16> objects_to_remove;
1263 for(core::map<u16, ServerActiveObject*>::Iterator
1264 i = m_active_objects.getIterator();
1265 i.atEnd()==false; i++)
1267 ServerActiveObject* obj = i.getNode()->getValue();
1268 u16 id = i.getNode()->getKey();
1269 v3f objectpos = obj->getBasePosition();
1271 // This shouldn't happen but check it
1274 dstream<<"WARNING: NULL object found in ServerEnvironment"
1280 // The block in which the object resides in
1281 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1283 // If block is active, don't remove
1284 if(m_active_blocks.contains(blockpos_o))
1288 Update the static data
1291 // Delete old static object
1292 MapBlock *oldblock = NULL;
1293 if(obj->m_static_exists)
1295 MapBlock *block = m_map->getBlockNoCreateNoEx
1296 (obj->m_static_block);
1299 block->m_static_objects.remove(id);
1303 // Create new static object
1304 std::string staticdata = obj->getStaticData();
1305 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1306 // Add to the block where the object is located in
1307 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1308 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1311 block->m_static_objects.insert(0, s_obj);
1312 block->setChangedFlag();
1313 obj->m_static_exists = true;
1314 obj->m_static_block = block->getPos();
1316 // If not possible, add back to previous block
1319 oldblock->m_static_objects.insert(0, s_obj);
1320 oldblock->setChangedFlag();
1321 obj->m_static_exists = true;
1322 obj->m_static_block = oldblock->getPos();
1325 dstream<<"WARNING: Server: Could not find a block for "
1326 <<"storing static object"<<std::endl;
1327 obj->m_static_exists = false;
1332 Delete active object if not known by some client,
1333 else set pending deactivation
1336 // If known by some client, don't delete.
1337 if(obj->m_known_by_count > 0 && force_delete == false)
1339 obj->m_pending_deactivation = true;
1343 /*dstream<<"INFO: Server: Stored static data. Deleting object."
1345 // Delete active object
1347 // Id to be removed from m_active_objects
1348 objects_to_remove.push_back(id);
1351 // Remove references from m_active_objects
1352 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1353 i != objects_to_remove.end(); i++)
1355 m_active_objects.remove(*i);
1366 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
1374 ClientEnvironment::~ClientEnvironment()
1376 // delete active objects
1377 for(core::map<u16, ClientActiveObject*>::Iterator
1378 i = m_active_objects.getIterator();
1379 i.atEnd()==false; i++)
1381 delete i.getNode()->getValue();
1388 void ClientEnvironment::addPlayer(Player *player)
1390 DSTACK(__FUNCTION_NAME);
1392 It is a failure if player is local and there already is a local
1395 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1397 Environment::addPlayer(player);
1400 LocalPlayer * ClientEnvironment::getLocalPlayer()
1402 for(core::list<Player*>::Iterator i = m_players.begin();
1403 i != m_players.end(); i++)
1405 Player *player = *i;
1406 if(player->isLocal())
1407 return (LocalPlayer*)player;
1412 void ClientEnvironment::step(float dtime)
1414 DSTACK(__FUNCTION_NAME);
1416 // Get some settings
1417 bool free_move = g_settings.getBool("free_move");
1418 bool footprints = g_settings.getBool("footprints");
1421 //TimeTaker timer("Client m_map->timerUpdate()");
1422 m_map->timerUpdate(dtime);
1426 LocalPlayer *lplayer = getLocalPlayer();
1428 // collision info queue
1429 core::list<CollisionInfo> player_collisions;
1432 Get the speed the player is going
1434 f32 player_speed = 0.001; // just some small value
1435 player_speed = lplayer->getSpeed().getLength();
1438 Maximum position increment
1440 //f32 position_max_increment = 0.05*BS;
1441 f32 position_max_increment = 0.1*BS;
1443 // Maximum time increment (for collision detection etc)
1444 // time = distance / speed
1445 f32 dtime_max_increment = position_max_increment / player_speed;
1447 // Maximum time increment is 10ms or lower
1448 if(dtime_max_increment > 0.01)
1449 dtime_max_increment = 0.01;
1451 // Don't allow overly huge dtime
1455 f32 dtime_downcount = dtime;
1458 Stuff that has a maximum time increment
1467 if(dtime_downcount > dtime_max_increment)
1469 dtime_part = dtime_max_increment;
1470 dtime_downcount -= dtime_part;
1474 dtime_part = dtime_downcount;
1476 Setting this to 0 (no -=dtime_part) disables an infinite loop
1477 when dtime_part is so small that dtime_downcount -= dtime_part
1480 dtime_downcount = 0;
1488 v3f lplayerpos = lplayer->getPosition();
1491 if(free_move == false)
1494 v3f speed = lplayer->getSpeed();
1495 if(lplayer->swimming_up == false)
1496 speed.Y -= 9.81 * BS * dtime_part * 2;
1499 if(lplayer->in_water_stable || lplayer->in_water)
1501 f32 max_down = 2.0*BS;
1502 if(speed.Y < -max_down) speed.Y = -max_down;
1505 if(speed.getLength() > max)
1507 speed = speed / speed.getLength() * max;
1511 lplayer->setSpeed(speed);
1516 This also does collision detection.
1518 lplayer->move(dtime_part, *m_map, position_max_increment,
1519 &player_collisions);
1522 while(dtime_downcount > 0.001);
1524 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
1526 for(core::list<CollisionInfo>::Iterator
1527 i = player_collisions.begin();
1528 i != player_collisions.end(); i++)
1530 CollisionInfo &info = *i;
1531 if(info.t == COLLISION_FALL)
1533 //f32 tolerance = BS*10; // 2 without damage
1534 f32 tolerance = BS*12; // 3 without damage
1536 if(info.speed > tolerance)
1538 f32 damage_f = (info.speed - tolerance)/BS*factor;
1539 u16 damage = (u16)(damage_f+0.5);
1540 if(lplayer->hp > damage)
1541 lplayer->hp -= damage;
1545 ClientEnvEvent event;
1546 event.type = CEE_PLAYER_DAMAGE;
1547 event.player_damage.amount = damage;
1548 m_client_event_queue.push_back(event);
1554 Stuff that can be done in an arbitarily large dtime
1556 for(core::list<Player*>::Iterator i = m_players.begin();
1557 i != m_players.end(); i++)
1559 Player *player = *i;
1560 v3f playerpos = player->getPosition();
1563 Handle non-local players
1565 if(player->isLocal() == false)
1568 player->move(dtime, *m_map, 100*BS);
1570 // Update lighting on remote players on client
1571 u8 light = LIGHT_MAX;
1574 v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS);
1575 MapNode n = m_map->getNode(p);
1576 light = n.getLightBlend(getDayNightRatio());
1578 catch(InvalidPositionException &e) {}
1579 player->updateLight(light);
1583 Add footsteps to grass
1587 // Get node that is at BS/4 under player
1588 v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
1590 MapNode n = m_map->getNode(bottompos);
1591 if(n.d == CONTENT_GRASS)
1593 n.d = CONTENT_GRASS_FOOTSTEPS;
1594 m_map->setNode(bottompos, n);
1595 // Update mesh on client
1596 if(m_map->mapType() == MAPTYPE_CLIENT)
1598 v3s16 p_blocks = getNodeBlockPos(bottompos);
1599 MapBlock *b = m_map->getBlockNoCreate(p_blocks);
1600 //b->updateMesh(getDayNightRatio());
1601 b->setMeshExpired(true);
1605 catch(InvalidPositionException &e)
1612 Step active objects and update lighting of them
1615 for(core::map<u16, ClientActiveObject*>::Iterator
1616 i = m_active_objects.getIterator();
1617 i.atEnd()==false; i++)
1619 ClientActiveObject* obj = i.getNode()->getValue();
1621 obj->step(dtime, this);
1623 //u8 light = LIGHT_MAX;
1627 v3s16 p = obj->getLightPosition();
1628 MapNode n = m_map->getNode(p);
1629 light = n.getLightBlend(getDayNightRatio());
1631 catch(InvalidPositionException &e) {}
1632 obj->updateLight(light);
1636 void ClientEnvironment::updateMeshes(v3s16 blockpos)
1638 m_map->updateMeshes(blockpos, getDayNightRatio());
1641 void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
1643 m_map->expireMeshes(only_daynight_diffed);
1646 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
1648 core::map<u16, ClientActiveObject*>::Node *n;
1649 n = m_active_objects.find(id);
1652 return n->getValue();
1655 bool isFreeClientActiveObjectId(u16 id,
1656 core::map<u16, ClientActiveObject*> &objects)
1661 for(core::map<u16, ClientActiveObject*>::Iterator
1662 i = objects.getIterator();
1663 i.atEnd()==false; i++)
1665 if(i.getNode()->getKey() == id)
1671 u16 getFreeClientActiveObjectId(
1672 core::map<u16, ClientActiveObject*> &objects)
1677 if(isFreeClientActiveObjectId(new_id, objects))
1687 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
1690 if(object->getId() == 0)
1692 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
1695 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1696 <<"no free ids available"<<std::endl;
1700 object->setId(new_id);
1702 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
1704 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1705 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1709 dstream<<"INGO: ClientEnvironment::addActiveObject(): "
1710 <<"added (id="<<object->getId()<<")"<<std::endl;
1711 m_active_objects.insert(object->getId(), object);
1712 object->addToScene(m_smgr);
1713 return object->getId();
1716 void ClientEnvironment::addActiveObject(u16 id, u8 type,
1717 const std::string &init_data)
1719 ClientActiveObject* obj = ClientActiveObject::create(type);
1722 dstream<<"WARNING: ClientEnvironment::addActiveObject(): "
1723 <<"id="<<id<<" type="<<type<<": Couldn't create object"
1730 addActiveObject(obj);
1732 obj->initialize(init_data);
1735 void ClientEnvironment::removeActiveObject(u16 id)
1737 dstream<<"ClientEnvironment::removeActiveObject(): "
1738 <<"id="<<id<<std::endl;
1739 ClientActiveObject* obj = getActiveObject(id);
1742 dstream<<"WARNING: ClientEnvironment::removeActiveObject(): "
1743 <<"id="<<id<<" not found"<<std::endl;
1746 obj->removeFromScene();
1748 m_active_objects.remove(id);
1751 void ClientEnvironment::processActiveObjectMessage(u16 id,
1752 const std::string &data)
1754 ClientActiveObject* obj = getActiveObject(id);
1757 dstream<<"WARNING: ClientEnvironment::processActiveObjectMessage():"
1758 <<" got message for id="<<id<<", which doesn't exist."
1762 obj->processMessage(data);
1766 Callbacks for activeobjects
1769 void ClientEnvironment::damageLocalPlayer(u8 damage)
1771 LocalPlayer *lplayer = getLocalPlayer();
1774 if(lplayer->hp > damage)
1775 lplayer->hp -= damage;
1779 ClientEnvEvent event;
1780 event.type = CEE_PLAYER_DAMAGE;
1781 event.player_damage.amount = damage;
1782 m_client_event_queue.push_back(event);
1786 Client likes to call these
1789 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
1790 core::array<DistanceSortedActiveObject> &dest)
1792 for(core::map<u16, ClientActiveObject*>::Iterator
1793 i = m_active_objects.getIterator();
1794 i.atEnd()==false; i++)
1796 ClientActiveObject* obj = i.getNode()->getValue();
1798 f32 d = (obj->getPosition() - origin).getLength();
1803 DistanceSortedActiveObject dso(obj, d);
1805 dest.push_back(dso);
1809 ClientEnvEvent ClientEnvironment::getClientEvent()
1811 if(m_client_event_queue.size() == 0)
1813 ClientEnvEvent event;
1814 event.type = CEE_NONE;
1817 return m_client_event_queue.pop_front();
1820 #endif // #ifndef SERVER