3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "jthread/jmutexautolock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
53 m_time_of_day_f(9000./24000),
54 m_time_of_day_speed(0),
56 m_enable_day_night_ratio_override(false),
57 m_day_night_ratio_override(0.0f)
59 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
62 Environment::~Environment()
65 for(std::vector<Player*>::iterator i = m_players.begin();
66 i != m_players.end(); ++i) {
71 void Environment::addPlayer(Player *player)
73 DSTACK(__FUNCTION_NAME);
75 Check that peer_ids are unique.
76 Also check that names are unique.
77 Exception: there can be multiple players with peer_id=0
79 // If peer id is non-zero, it has to be unique.
80 if(player->peer_id != 0)
81 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
82 // Name has to be unique.
83 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
85 m_players.push_back(player);
88 void Environment::removePlayer(u16 peer_id)
90 DSTACK(__FUNCTION_NAME);
92 for(std::vector<Player*>::iterator i = m_players.begin();
93 i != m_players.end();)
96 if(player->peer_id == peer_id) {
98 i = m_players.erase(i);
105 void Environment::removePlayer(const char *name)
107 for (std::vector<Player*>::iterator it = m_players.begin();
108 it != m_players.end(); ++it) {
109 if (strcmp((*it)->getName(), name) == 0) {
117 Player * Environment::getPlayer(u16 peer_id)
119 for(std::vector<Player*>::iterator i = m_players.begin();
120 i != m_players.end(); ++i) {
122 if(player->peer_id == peer_id)
128 Player * Environment::getPlayer(const char *name)
130 for(std::vector<Player*>::iterator i = m_players.begin();
131 i != m_players.end(); ++i) {
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::vector<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
147 if(j == chosen_one) {
156 Player * Environment::getNearestConnectedPlayer(v3f pos)
158 std::vector<Player*> connected_players = getPlayers(true);
160 Player *nearest_player = NULL;
161 for(std::vector<Player*>::iterator
162 i = connected_players.begin();
163 i != connected_players.end(); ++i) {
165 f32 d = player->getPosition().getDistanceFrom(pos);
166 if(d < nearest_d || nearest_player == NULL) {
168 nearest_player = player;
171 return nearest_player;
174 std::vector<Player*> Environment::getPlayers()
179 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
181 std::vector<Player*> newlist;
182 for(std::vector<Player*>::iterator
183 i = m_players.begin();
184 i != m_players.end(); ++i) {
187 if(ignore_disconnected) {
188 // Ignore disconnected players
189 if(player->peer_id == 0)
193 newlist.push_back(player);
198 u32 Environment::getDayNightRatio()
200 if(m_enable_day_night_ratio_override)
201 return m_day_night_ratio_override;
202 return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
205 void Environment::setTimeOfDaySpeed(float speed)
207 JMutexAutoLock(this->m_timeofday_lock);
208 m_time_of_day_speed = speed;
211 float Environment::getTimeOfDaySpeed()
213 JMutexAutoLock(this->m_timeofday_lock);
214 float retval = m_time_of_day_speed;
218 void Environment::setTimeOfDay(u32 time)
220 JMutexAutoLock(this->m_time_lock);
221 m_time_of_day = time;
222 m_time_of_day_f = (float)time / 24000.0;
225 u32 Environment::getTimeOfDay()
227 JMutexAutoLock(this->m_time_lock);
228 u32 retval = m_time_of_day;
232 float Environment::getTimeOfDayF()
234 JMutexAutoLock(this->m_time_lock);
235 float retval = m_time_of_day_f;
239 void Environment::stepTimeOfDay(float dtime)
241 // getTimeOfDaySpeed lock the value we need to prevent MT problems
242 float day_speed = getTimeOfDaySpeed();
244 m_time_counter += dtime;
245 f32 speed = day_speed * 24000./(24.*3600);
246 u32 units = (u32)(m_time_counter*speed);
250 if(m_time_of_day + units >= 24000)
252 m_time_of_day = (m_time_of_day + units) % 24000;
254 m_time_of_day_f = (float)m_time_of_day / 24000.0;
257 m_time_counter -= (f32)units / speed;
260 m_time_of_day_f += day_speed/24/3600*dtime;
261 if(m_time_of_day_f > 1.0)
262 m_time_of_day_f -= 1.0;
263 if(m_time_of_day_f < 0.0)
264 m_time_of_day_f += 1.0;
272 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
276 // Initialize timer to random value to spread processing
277 float itv = abm->getTriggerInterval();
278 itv = MYMAX(0.001, itv); // No less than 1ms
279 int minval = MYMAX(-0.51*itv, -60); // Clamp to
280 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
281 timer = myrand_range(minval, maxval);
288 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
291 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
292 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
293 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
300 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
302 std::set<v3s16> &blocks_removed,
303 std::set<v3s16> &blocks_added)
308 std::set<v3s16> newlist = m_forceloaded_list;
309 for(std::vector<v3s16>::iterator i = active_positions.begin();
310 i != active_positions.end(); ++i)
312 fillRadiusBlock(*i, radius, newlist);
316 Find out which blocks on the old list are not on the new list
318 // Go through old list
319 for(std::set<v3s16>::iterator i = m_list.begin();
320 i != m_list.end(); ++i)
323 // If not on new list, it's been removed
324 if(newlist.find(p) == newlist.end())
325 blocks_removed.insert(p);
329 Find out which blocks on the new list are not on the old list
331 // Go through new list
332 for(std::set<v3s16>::iterator i = newlist.begin();
333 i != newlist.end(); ++i)
336 // If not on old list, it's been added
337 if(m_list.find(p) == m_list.end())
338 blocks_added.insert(p);
345 for(std::set<v3s16>::iterator i = newlist.begin();
346 i != newlist.end(); ++i)
357 ServerEnvironment::ServerEnvironment(ServerMap *map,
358 GameScripting *scriptIface, IGameDef *gamedef,
359 const std::string &path_world) :
361 m_script(scriptIface),
363 m_path_world(path_world),
364 m_send_recommended_timer(0),
365 m_active_block_interval_overload_skip(0),
367 m_game_time_fraction_counter(0),
368 m_recommended_send_interval(0.1),
369 m_max_lag_estimate(0.1)
373 ServerEnvironment::~ServerEnvironment()
375 // Clear active block list.
376 // This makes the next one delete all active objects.
377 m_active_blocks.clear();
379 // Convert all objects to static and delete the active objects
380 deactivateFarObjects(true);
385 // Delete ActiveBlockModifiers
386 for(std::vector<ABMWithState>::iterator
387 i = m_abms.begin(); i != m_abms.end(); ++i){
392 Map & ServerEnvironment::getMap()
397 ServerMap & ServerEnvironment::getServerMap()
402 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
404 float distance = pos1.getDistanceFrom(pos2);
406 //calculate normalized direction vector
407 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
408 (pos2.Y - pos1.Y)/distance,
409 (pos2.Z - pos1.Z)/distance);
411 //find out if there's a node on path between pos1 and pos2
412 for (float i = 1; i < distance; i += stepsize) {
413 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
414 normalized_vector.Y * i,
415 normalized_vector.Z * i) +pos1,BS);
417 MapNode n = getMap().getNodeNoEx(pos);
419 if(n.param0 != CONTENT_AIR) {
429 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
430 const std::string &str_reason, bool reconnect)
432 for (std::vector<Player*>::iterator it = m_players.begin();
433 it != m_players.end();
435 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
436 (*it)->protocol_version, (AccessDeniedCode)reason,
437 str_reason, reconnect);
441 void ServerEnvironment::saveLoadedPlayers()
443 std::string players_path = m_path_world + DIR_DELIM "players";
444 fs::CreateDir(players_path);
446 for (std::vector<Player*>::iterator it = m_players.begin();
447 it != m_players.end();
449 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
450 if (player->checkModified()) {
451 player->save(players_path);
456 void ServerEnvironment::savePlayer(const std::string &playername)
458 std::string players_path = m_path_world + DIR_DELIM "players";
459 fs::CreateDir(players_path);
461 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
463 player->save(players_path);
467 Player *ServerEnvironment::loadPlayer(const std::string &playername)
469 bool newplayer = false;
471 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
472 std::string path = players_path + playername;
474 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
476 player = new RemotePlayer(m_gamedef, "");
480 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
481 //// Open file and deserialize
482 std::ifstream is(path.c_str(), std::ios_base::binary);
485 player->deSerialize(is, path);
488 if (player->getName() == playername) {
493 path = players_path + playername + itos(i);
497 infostream << "Player file for player " << playername
498 << " not found" << std::endl;
506 player->setModified(false);
510 void ServerEnvironment::saveMeta()
512 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
514 // Open file and serialize
515 std::ostringstream ss(std::ios_base::binary);
518 args.setU64("game_time", m_game_time);
519 args.setU64("time_of_day", getTimeOfDay());
523 if(!fs::safeWriteToFile(path, ss.str()))
525 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
527 throw SerializationError("Couldn't save env meta");
531 void ServerEnvironment::loadMeta()
533 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
535 // Open file and deserialize
536 std::ifstream is(path.c_str(), std::ios_base::binary);
538 infostream << "ServerEnvironment::loadMeta(): Failed to open "
539 << path << std::endl;
540 throw SerializationError("Couldn't load env meta");
545 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
546 throw SerializationError("ServerEnvironment::loadMeta(): "
547 "EnvArgsEnd not found!");
551 m_game_time = args.getU64("game_time");
552 } catch (SettingNotFoundException &e) {
553 // Getting this is crucial, otherwise timestamps are useless
554 throw SerializationError("Couldn't load env meta game_time");
558 m_time_of_day = args.getU64("time_of_day");
559 } catch (SettingNotFoundException &e) {
560 // This is not as important
561 m_time_of_day = 9000;
567 ActiveBlockModifier *abm;
569 std::set<content_t> required_neighbors;
575 ServerEnvironment *m_env;
576 std::map<content_t, std::vector<ActiveABM> > m_aabms;
578 ABMHandler(std::vector<ABMWithState> &abms,
579 float dtime_s, ServerEnvironment *env,
585 INodeDefManager *ndef = env->getGameDef()->ndef();
586 for(std::vector<ABMWithState>::iterator
587 i = abms.begin(); i != abms.end(); ++i) {
588 ActiveBlockModifier *abm = i->abm;
589 float trigger_interval = abm->getTriggerInterval();
590 if(trigger_interval < 0.001)
591 trigger_interval = 0.001;
592 float actual_interval = dtime_s;
595 if(i->timer < trigger_interval)
597 i->timer -= trigger_interval;
598 actual_interval = trigger_interval;
600 float intervals = actual_interval / trigger_interval;
603 float chance = abm->getTriggerChance();
608 aabm.chance = chance / intervals;
612 std::set<std::string> required_neighbors_s
613 = abm->getRequiredNeighbors();
614 for(std::set<std::string>::iterator
615 i = required_neighbors_s.begin();
616 i != required_neighbors_s.end(); i++)
618 ndef->getIds(*i, aabm.required_neighbors);
621 std::set<std::string> contents_s = abm->getTriggerContents();
622 for(std::set<std::string>::iterator
623 i = contents_s.begin(); i != contents_s.end(); i++)
625 std::set<content_t> ids;
626 ndef->getIds(*i, ids);
627 for(std::set<content_t>::const_iterator k = ids.begin();
631 std::map<content_t, std::vector<ActiveABM> >::iterator j;
633 if(j == m_aabms.end()){
634 std::vector<ActiveABM> aabmlist;
635 m_aabms[c] = aabmlist;
638 j->second.push_back(aabm);
643 // Find out how many objects the given block and its neighbours contain.
644 // Returns the number of objects in the block, and also in 'wider' the
645 // number of objects in the block and all its neighbours. The latter
646 // may an estimate if any neighbours are unloaded.
647 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
650 u32 wider_unknown_count = 0;
651 for(s16 x=-1; x<=1; x++)
652 for(s16 y=-1; y<=1; y++)
653 for(s16 z=-1; z<=1; z++)
655 MapBlock *block2 = map->getBlockNoCreateNoEx(
656 block->getPos() + v3s16(x,y,z));
658 wider_unknown_count++;
661 wider += block2->m_static_objects.m_active.size()
662 + block2->m_static_objects.m_stored.size();
665 u32 active_object_count = block->m_static_objects.m_active.size();
666 u32 wider_known_count = 3*3*3 - wider_unknown_count;
667 wider += wider_unknown_count * wider / wider_known_count;
668 return active_object_count;
671 void apply(MapBlock *block)
676 ServerMap *map = &m_env->getServerMap();
678 u32 active_object_count_wider;
679 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
680 m_env->m_added_objects = 0;
683 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
684 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
685 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
687 MapNode n = block->getNodeNoEx(p0);
688 content_t c = n.getContent();
689 v3s16 p = p0 + block->getPosRelative();
691 std::map<content_t, std::vector<ActiveABM> >::iterator j;
693 if(j == m_aabms.end())
696 for(std::vector<ActiveABM>::iterator
697 i = j->second.begin(); i != j->second.end(); i++) {
698 if(myrand() % i->chance != 0)
702 if(!i->required_neighbors.empty())
705 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
706 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
707 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
711 MapNode n = map->getNodeNoEx(p1);
712 content_t c = n.getContent();
713 std::set<content_t>::const_iterator k;
714 k = i->required_neighbors.find(c);
715 if(k != i->required_neighbors.end()){
719 // No required neighbor found
724 // Call all the trigger variations
725 i->abm->trigger(m_env, p, n);
726 i->abm->trigger(m_env, p, n,
727 active_object_count, active_object_count_wider);
729 // Count surrounding objects again if the abms added any
730 if(m_env->m_added_objects > 0) {
731 active_object_count = countObjects(block, map, active_object_count_wider);
732 m_env->m_added_objects = 0;
739 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
741 // Reset usage timer immediately, otherwise a block that becomes active
742 // again at around the same time as it would normally be unloaded will
743 // get unloaded incorrectly. (I think this still leaves a small possibility
744 // of a race condition between this and server::AsyncRunStep, which only
745 // some kind of synchronisation will fix, but it at least reduces the window
746 // of opportunity for it to break from seconds to nanoseconds)
747 block->resetUsageTimer();
749 // Get time difference
751 u32 stamp = block->getTimestamp();
752 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
753 dtime_s = m_game_time - block->getTimestamp();
754 dtime_s += additional_dtime;
756 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
757 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
759 // Set current time as timestamp
760 block->setTimestampNoChangedFlag(m_game_time);
762 /*infostream<<"ServerEnvironment::activateBlock(): block is "
763 <<dtime_s<<" seconds old."<<std::endl;*/
765 // Activate stored objects
766 activateObjects(block, dtime_s);
769 std::map<v3s16, NodeTimer> elapsed_timers =
770 block->m_node_timers.step((float)dtime_s);
771 if(!elapsed_timers.empty()){
773 for(std::map<v3s16, NodeTimer>::iterator
774 i = elapsed_timers.begin();
775 i != elapsed_timers.end(); i++){
776 n = block->getNodeNoEx(i->first);
777 v3s16 p = i->first + block->getPosRelative();
778 if(m_script->node_on_timer(p,n,i->second.elapsed))
779 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
783 /* Handle ActiveBlockModifiers */
784 ABMHandler abmhandler(m_abms, dtime_s, this, false);
785 abmhandler.apply(block);
788 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
790 m_abms.push_back(ABMWithState(abm));
793 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
795 INodeDefManager *ndef = m_gamedef->ndef();
796 MapNode n_old = m_map->getNodeNoEx(p);
799 if (ndef->get(n_old).has_on_destruct)
800 m_script->node_on_destruct(p, n_old);
803 if (!m_map->addNodeWithEvent(p, n))
806 // Update active VoxelManipulator if a mapgen thread
807 m_map->updateVManip(p);
809 // Call post-destructor
810 if (ndef->get(n_old).has_after_destruct)
811 m_script->node_after_destruct(p, n_old);
814 if (ndef->get(n).has_on_construct)
815 m_script->node_on_construct(p, n);
820 bool ServerEnvironment::removeNode(v3s16 p)
822 INodeDefManager *ndef = m_gamedef->ndef();
823 MapNode n_old = m_map->getNodeNoEx(p);
826 if (ndef->get(n_old).has_on_destruct)
827 m_script->node_on_destruct(p, n_old);
830 // This is slightly optimized compared to addNodeWithEvent(air)
831 if (!m_map->removeNodeWithEvent(p))
834 // Update active VoxelManipulator if a mapgen thread
835 m_map->updateVManip(p);
837 // Call post-destructor
838 if (ndef->get(n_old).has_after_destruct)
839 m_script->node_after_destruct(p, n_old);
841 // Air doesn't require constructor
845 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
847 if (!m_map->addNodeWithEvent(p, n, false))
850 // Update active VoxelManipulator if a mapgen thread
851 m_map->updateVManip(p);
856 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
858 for(std::map<u16, ServerActiveObject*>::iterator
859 i = m_active_objects.begin();
860 i != m_active_objects.end(); ++i)
862 ServerActiveObject* obj = i->second;
864 v3f objectpos = obj->getBasePosition();
865 if(objectpos.getDistanceFrom(pos) > radius)
867 objects.push_back(id);
871 void ServerEnvironment::clearAllObjects()
873 infostream<<"ServerEnvironment::clearAllObjects(): "
874 <<"Removing all active objects"<<std::endl;
875 std::vector<u16> objects_to_remove;
876 for(std::map<u16, ServerActiveObject*>::iterator
877 i = m_active_objects.begin();
878 i != m_active_objects.end(); ++i) {
879 ServerActiveObject* obj = i->second;
880 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
883 // Delete static object if block is loaded
884 if(obj->m_static_exists){
885 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
887 block->m_static_objects.remove(id);
888 block->raiseModified(MOD_STATE_WRITE_NEEDED,
889 MOD_REASON_CLEAR_ALL_OBJECTS);
890 obj->m_static_exists = false;
893 // If known by some client, don't delete immediately
894 if(obj->m_known_by_count > 0){
895 obj->m_pending_deactivation = true;
896 obj->m_removed = true;
900 // Tell the object about removal
901 obj->removingFromEnvironment();
902 // Deregister in scripting api
903 m_script->removeObjectReference(obj);
905 // Delete active object
906 if(obj->environmentDeletes())
908 // Id to be removed from m_active_objects
909 objects_to_remove.push_back(id);
912 // Remove references from m_active_objects
913 for(std::vector<u16>::iterator i = objects_to_remove.begin();
914 i != objects_to_remove.end(); ++i) {
915 m_active_objects.erase(*i);
918 // Get list of loaded blocks
919 std::vector<v3s16> loaded_blocks;
920 infostream<<"ServerEnvironment::clearAllObjects(): "
921 <<"Listing all loaded blocks"<<std::endl;
922 m_map->listAllLoadedBlocks(loaded_blocks);
923 infostream<<"ServerEnvironment::clearAllObjects(): "
924 <<"Done listing all loaded blocks: "
925 <<loaded_blocks.size()<<std::endl;
927 // Get list of loadable blocks
928 std::vector<v3s16> loadable_blocks;
929 infostream<<"ServerEnvironment::clearAllObjects(): "
930 <<"Listing all loadable blocks"<<std::endl;
931 m_map->listAllLoadableBlocks(loadable_blocks);
932 infostream<<"ServerEnvironment::clearAllObjects(): "
933 <<"Done listing all loadable blocks: "
934 <<loadable_blocks.size()
935 <<", now clearing"<<std::endl;
937 // Grab a reference on each loaded block to avoid unloading it
938 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
939 i != loaded_blocks.end(); ++i) {
941 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
942 assert(block != NULL);
946 // Remove objects in all loadable blocks
947 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
948 unload_interval = MYMAX(unload_interval, 1);
949 u32 report_interval = loadable_blocks.size() / 10;
950 u32 num_blocks_checked = 0;
951 u32 num_blocks_cleared = 0;
952 u32 num_objs_cleared = 0;
953 for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
954 i != loadable_blocks.end(); ++i) {
956 MapBlock *block = m_map->emergeBlock(p, false);
958 errorstream<<"ServerEnvironment::clearAllObjects(): "
959 <<"Failed to emerge block "<<PP(p)<<std::endl;
962 u32 num_stored = block->m_static_objects.m_stored.size();
963 u32 num_active = block->m_static_objects.m_active.size();
964 if(num_stored != 0 || num_active != 0){
965 block->m_static_objects.m_stored.clear();
966 block->m_static_objects.m_active.clear();
967 block->raiseModified(MOD_STATE_WRITE_NEEDED,
968 MOD_REASON_CLEAR_ALL_OBJECTS);
969 num_objs_cleared += num_stored + num_active;
970 num_blocks_cleared++;
972 num_blocks_checked++;
974 if(report_interval != 0 &&
975 num_blocks_checked % report_interval == 0){
976 float percent = 100.0 * (float)num_blocks_checked /
977 loadable_blocks.size();
978 infostream<<"ServerEnvironment::clearAllObjects(): "
979 <<"Cleared "<<num_objs_cleared<<" objects"
980 <<" in "<<num_blocks_cleared<<" blocks ("
981 <<percent<<"%)"<<std::endl;
983 if(num_blocks_checked % unload_interval == 0){
984 m_map->unloadUnreferencedBlocks();
987 m_map->unloadUnreferencedBlocks();
989 // Drop references that were added above
990 for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
991 i != loaded_blocks.end(); ++i) {
993 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
998 infostream<<"ServerEnvironment::clearAllObjects(): "
999 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1000 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1003 void ServerEnvironment::step(float dtime)
1005 DSTACK(__FUNCTION_NAME);
1007 //TimeTaker timer("ServerEnv step");
1009 /* Step time of day */
1010 stepTimeOfDay(dtime);
1013 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1014 // really matter that much.
1015 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1021 m_game_time_fraction_counter += dtime;
1022 u32 inc_i = (u32)m_game_time_fraction_counter;
1023 m_game_time += inc_i;
1024 m_game_time_fraction_counter -= (float)inc_i;
1031 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1032 for(std::vector<Player*>::iterator i = m_players.begin();
1033 i != m_players.end(); ++i)
1035 Player *player = *i;
1037 // Ignore disconnected players
1038 if(player->peer_id == 0)
1042 player->move(dtime, this, 100*BS);
1047 Manage active block list
1049 if(m_active_blocks_management_interval.step(dtime, 2.0))
1051 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1053 Get player block positions
1055 std::vector<v3s16> players_blockpos;
1056 for(std::vector<Player*>::iterator
1057 i = m_players.begin();
1058 i != m_players.end(); ++i) {
1059 Player *player = *i;
1060 // Ignore disconnected players
1061 if(player->peer_id == 0)
1064 v3s16 blockpos = getNodeBlockPos(
1065 floatToInt(player->getPosition(), BS));
1066 players_blockpos.push_back(blockpos);
1070 Update list of active blocks, collecting changes
1072 const s16 active_block_range = g_settings->getS16("active_block_range");
1073 std::set<v3s16> blocks_removed;
1074 std::set<v3s16> blocks_added;
1075 m_active_blocks.update(players_blockpos, active_block_range,
1076 blocks_removed, blocks_added);
1079 Handle removed blocks
1082 // Convert active objects that are no more in active blocks to static
1083 deactivateFarObjects(false);
1085 for(std::set<v3s16>::iterator
1086 i = blocks_removed.begin();
1087 i != blocks_removed.end(); ++i)
1091 /* infostream<<"Server: Block " << PP(p)
1092 << " became inactive"<<std::endl; */
1094 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1098 // Set current time as timestamp (and let it set ChangedFlag)
1099 block->setTimestamp(m_game_time);
1106 for(std::set<v3s16>::iterator
1107 i = blocks_added.begin();
1108 i != blocks_added.end(); ++i)
1112 MapBlock *block = m_map->getBlockOrEmerge(p);
1114 m_active_blocks.m_list.erase(p);
1118 activateBlock(block);
1119 /* infostream<<"Server: Block " << PP(p)
1120 << " became active"<<std::endl; */
1125 Mess around in active blocks
1127 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1129 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1133 for(std::set<v3s16>::iterator
1134 i = m_active_blocks.m_list.begin();
1135 i != m_active_blocks.m_list.end(); ++i)
1139 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1140 <<") being handled"<<std::endl;*/
1142 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1146 // Reset block usage timer
1147 block->resetUsageTimer();
1149 // Set current time as timestamp
1150 block->setTimestampNoChangedFlag(m_game_time);
1151 // If time has changed much from the one on disk,
1152 // set block to be saved when it is unloaded
1153 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1154 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1155 MOD_REASON_BLOCK_EXPIRED);
1158 std::map<v3s16, NodeTimer> elapsed_timers =
1159 block->m_node_timers.step((float)dtime);
1160 if(!elapsed_timers.empty()){
1162 for(std::map<v3s16, NodeTimer>::iterator
1163 i = elapsed_timers.begin();
1164 i != elapsed_timers.end(); i++){
1165 n = block->getNodeNoEx(i->first);
1166 p = i->first + block->getPosRelative();
1167 if(m_script->node_on_timer(p,n,i->second.elapsed))
1168 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1174 const float abm_interval = 1.0;
1175 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1177 if(m_active_block_interval_overload_skip > 0){
1178 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1179 m_active_block_interval_overload_skip--;
1182 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1183 TimeTaker timer("modify in active blocks");
1185 // Initialize handling of ActiveBlockModifiers
1186 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1188 for(std::set<v3s16>::iterator
1189 i = m_active_blocks.m_list.begin();
1190 i != m_active_blocks.m_list.end(); ++i)
1194 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1195 <<") being handled"<<std::endl;*/
1197 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1201 // Set current time as timestamp
1202 block->setTimestampNoChangedFlag(m_game_time);
1204 /* Handle ActiveBlockModifiers */
1205 abmhandler.apply(block);
1208 u32 time_ms = timer.stop(true);
1209 u32 max_time_ms = 200;
1210 if(time_ms > max_time_ms){
1211 infostream<<"WARNING: active block modifiers took "
1212 <<time_ms<<"ms (longer than "
1213 <<max_time_ms<<"ms)"<<std::endl;
1214 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1219 Step script environment (run global on_step())
1221 m_script->environment_Step(dtime);
1227 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1228 //TimeTaker timer("Step active objects");
1230 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1232 // This helps the objects to send data at the same time
1233 bool send_recommended = false;
1234 m_send_recommended_timer += dtime;
1235 if(m_send_recommended_timer > getSendRecommendedInterval())
1237 m_send_recommended_timer -= getSendRecommendedInterval();
1238 send_recommended = true;
1241 for(std::map<u16, ServerActiveObject*>::iterator
1242 i = m_active_objects.begin();
1243 i != m_active_objects.end(); ++i)
1245 ServerActiveObject* obj = i->second;
1246 // Don't step if is to be removed or stored statically
1247 if(obj->m_removed || obj->m_pending_deactivation)
1250 obj->step(dtime, send_recommended);
1251 // Read messages from object
1252 while(!obj->m_messages_out.empty())
1254 m_active_object_messages.push(
1255 obj->m_messages_out.front());
1256 obj->m_messages_out.pop();
1262 Manage active objects
1264 if(m_object_management_interval.step(dtime, 0.5))
1266 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1268 Remove objects that satisfy (m_removed && m_known_by_count==0)
1270 removeRemovedObjects();
1274 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1276 std::map<u16, ServerActiveObject*>::iterator n;
1277 n = m_active_objects.find(id);
1278 if(n == m_active_objects.end())
1283 bool isFreeServerActiveObjectId(u16 id,
1284 std::map<u16, ServerActiveObject*> &objects)
1289 return objects.find(id) == objects.end();
1292 u16 getFreeServerActiveObjectId(
1293 std::map<u16, ServerActiveObject*> &objects)
1295 //try to reuse id's as late as possible
1296 static u16 last_used_id = 0;
1297 u16 startid = last_used_id;
1301 if(isFreeServerActiveObjectId(last_used_id, objects))
1302 return last_used_id;
1304 if(last_used_id == startid)
1309 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1311 assert(object); // Pre-condition
1313 u16 id = addActiveObjectRaw(object, true, 0);
1318 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1322 v3f objectpos = obj->getBasePosition();
1324 // The block in which the object resides in
1325 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1328 Update the static data
1331 // Create new static object
1332 std::string staticdata = obj->getStaticData();
1333 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1334 // Add to the block where the object is located in
1335 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1336 // Get or generate the block
1337 MapBlock *block = m_map->emergeBlock(blockpos);
1339 bool succeeded = false;
1343 block->m_static_objects.insert(0, s_obj);
1344 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1345 "addActiveObjectAsStatic");
1349 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1350 <<"Could not find or generate "
1351 <<"a block for storing static object"<<std::endl;
1355 if(obj->environmentDeletes())
1363 Finds out what new objects have been added to
1364 inside a radius around a position
1366 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1368 std::set<u16> ¤t_objects,
1369 std::set<u16> &added_objects)
1371 v3f pos_f = intToFloat(pos, BS);
1372 f32 radius_f = radius * BS;
1373 f32 player_radius_f = player_radius * BS;
1375 if (player_radius_f < 0)
1376 player_radius_f = 0;
1379 Go through the object list,
1380 - discard m_removed objects,
1381 - discard objects that are too far away,
1382 - discard objects that are found in current_objects.
1383 - add remaining objects to added_objects
1385 for(std::map<u16, ServerActiveObject*>::iterator
1386 i = m_active_objects.begin();
1387 i != m_active_objects.end(); ++i)
1391 ServerActiveObject *object = i->second;
1394 // Discard if removed or deactivating
1395 if(object->m_removed || object->m_pending_deactivation)
1398 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1399 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1400 // Discard if too far
1401 if (distance_f > player_radius_f && player_radius_f != 0)
1403 } else if (distance_f > radius_f)
1406 // Discard if already on current_objects
1407 std::set<u16>::iterator n;
1408 n = current_objects.find(id);
1409 if(n != current_objects.end())
1411 // Add to added_objects
1412 added_objects.insert(id);
1417 Finds out what objects have been removed from
1418 inside a radius around a position
1420 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1422 std::set<u16> ¤t_objects,
1423 std::set<u16> &removed_objects)
1425 v3f pos_f = intToFloat(pos, BS);
1426 f32 radius_f = radius * BS;
1427 f32 player_radius_f = player_radius * BS;
1429 if (player_radius_f < 0)
1430 player_radius_f = 0;
1433 Go through current_objects; object is removed if:
1434 - object is not found in m_active_objects (this is actually an
1435 error condition; objects should be set m_removed=true and removed
1436 only after all clients have been informed about removal), or
1437 - object has m_removed=true, or
1438 - object is too far away
1440 for(std::set<u16>::iterator
1441 i = current_objects.begin();
1442 i != current_objects.end(); ++i)
1445 ServerActiveObject *object = getActiveObject(id);
1448 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1449 <<" object in current_objects is NULL"<<std::endl;
1450 removed_objects.insert(id);
1454 if(object->m_removed || object->m_pending_deactivation)
1456 removed_objects.insert(id);
1460 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1461 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1462 if (distance_f <= player_radius_f || player_radius_f == 0)
1464 } else if (distance_f <= radius_f)
1467 // Object is no longer visible
1468 removed_objects.insert(id);
1472 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1474 if(m_active_object_messages.empty())
1475 return ActiveObjectMessage(0);
1477 ActiveObjectMessage message = m_active_object_messages.front();
1478 m_active_object_messages.pop();
1483 ************ Private methods *************
1486 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1487 bool set_changed, u32 dtime_s)
1489 assert(object); // Pre-condition
1490 if(object->getId() == 0){
1491 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1494 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1495 <<"no free ids available"<<std::endl;
1496 if(object->environmentDeletes())
1500 object->setId(new_id);
1503 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1504 <<"supplied with id "<<object->getId()<<std::endl;
1506 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1508 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1509 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1510 if(object->environmentDeletes())
1514 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1515 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1517 m_active_objects[object->getId()] = object;
1519 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1520 <<"Added id="<<object->getId()<<"; there are now "
1521 <<m_active_objects.size()<<" active objects."
1524 // Register reference in scripting api (must be done before post-init)
1525 m_script->addObjectReference(object);
1526 // Post-initialize object
1527 object->addedToEnvironment(dtime_s);
1529 // Add static data to block
1530 if(object->isStaticAllowed())
1532 // Add static object to active static list of the block
1533 v3f objectpos = object->getBasePosition();
1534 std::string staticdata = object->getStaticData();
1535 StaticObject s_obj(object->getType(), objectpos, staticdata);
1536 // Add to the block where the object is located in
1537 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1538 MapBlock *block = m_map->emergeBlock(blockpos);
1540 block->m_static_objects.m_active[object->getId()] = s_obj;
1541 object->m_static_exists = true;
1542 object->m_static_block = blockpos;
1545 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1546 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1548 v3s16 p = floatToInt(objectpos, BS);
1549 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1550 <<"could not emerge block for storing id="<<object->getId()
1551 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1555 return object->getId();
1559 Remove objects that satisfy (m_removed && m_known_by_count==0)
1561 void ServerEnvironment::removeRemovedObjects()
1563 std::vector<u16> objects_to_remove;
1564 for(std::map<u16, ServerActiveObject*>::iterator
1565 i = m_active_objects.begin();
1566 i != m_active_objects.end(); ++i) {
1568 ServerActiveObject* obj = i->second;
1569 // This shouldn't happen but check it
1572 infostream<<"NULL object found in ServerEnvironment"
1573 <<" while finding removed objects. id="<<id<<std::endl;
1574 // Id to be removed from m_active_objects
1575 objects_to_remove.push_back(id);
1580 We will delete objects that are marked as removed or thatare
1581 waiting for deletion after deactivation
1583 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1587 Delete static data from block if is marked as removed
1589 if(obj->m_static_exists && obj->m_removed)
1591 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1593 block->m_static_objects.remove(id);
1594 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1595 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1596 obj->m_static_exists = false;
1598 infostream<<"Failed to emerge block from which an object to "
1599 <<"be removed was loaded from. id="<<id<<std::endl;
1603 // If m_known_by_count > 0, don't actually remove. On some future
1604 // invocation this will be 0, which is when removal will continue.
1605 if(obj->m_known_by_count > 0)
1609 Move static data from active to stored if not marked as removed
1611 if(obj->m_static_exists && !obj->m_removed){
1612 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1614 std::map<u16, StaticObject>::iterator i =
1615 block->m_static_objects.m_active.find(id);
1616 if(i != block->m_static_objects.m_active.end()){
1617 block->m_static_objects.m_stored.push_back(i->second);
1618 block->m_static_objects.m_active.erase(id);
1619 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1620 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1623 infostream<<"Failed to emerge block from which an object to "
1624 <<"be deactivated was loaded from. id="<<id<<std::endl;
1628 // Tell the object about removal
1629 obj->removingFromEnvironment();
1630 // Deregister in scripting api
1631 m_script->removeObjectReference(obj);
1634 if(obj->environmentDeletes())
1637 // Id to be removed from m_active_objects
1638 objects_to_remove.push_back(id);
1640 // Remove references from m_active_objects
1641 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1642 i != objects_to_remove.end(); ++i) {
1643 m_active_objects.erase(*i);
1647 static void print_hexdump(std::ostream &o, const std::string &data)
1649 const int linelength = 16;
1650 for(int l=0; ; l++){
1651 int i0 = linelength * l;
1652 bool at_end = false;
1653 int thislinelength = linelength;
1654 if(i0 + thislinelength > (int)data.size()){
1655 thislinelength = data.size() - i0;
1658 for(int di=0; di<linelength; di++){
1661 if(di<thislinelength)
1662 snprintf(buf, 4, "%.2x ", data[i]);
1664 snprintf(buf, 4, " ");
1668 for(int di=0; di<thislinelength; di++){
1682 Convert stored objects from blocks near the players to active.
1684 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1689 // Ignore if no stored objects (to not set changed flag)
1690 if(block->m_static_objects.m_stored.empty())
1693 verbosestream<<"ServerEnvironment::activateObjects(): "
1694 <<"activating objects of block "<<PP(block->getPos())
1695 <<" ("<<block->m_static_objects.m_stored.size()
1696 <<" objects)"<<std::endl;
1697 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1699 errorstream<<"suspiciously large amount of objects detected: "
1700 <<block->m_static_objects.m_stored.size()<<" in "
1701 <<PP(block->getPos())
1702 <<"; removing all of them."<<std::endl;
1703 // Clear stored list
1704 block->m_static_objects.m_stored.clear();
1705 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1706 MOD_REASON_TOO_MANY_OBJECTS);
1710 // Activate stored objects
1711 std::vector<StaticObject> new_stored;
1712 for (std::vector<StaticObject>::iterator
1713 i = block->m_static_objects.m_stored.begin();
1714 i != block->m_static_objects.m_stored.end(); ++i) {
1715 StaticObject &s_obj = *i;
1717 // Create an active object from the data
1718 ServerActiveObject *obj = ServerActiveObject::create
1719 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1720 // If couldn't create object, store static data back.
1722 errorstream<<"ServerEnvironment::activateObjects(): "
1723 <<"failed to create active object from static object "
1724 <<"in block "<<PP(s_obj.pos/BS)
1725 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1726 print_hexdump(verbosestream, s_obj.data);
1728 new_stored.push_back(s_obj);
1731 verbosestream<<"ServerEnvironment::activateObjects(): "
1732 <<"activated static object pos="<<PP(s_obj.pos/BS)
1733 <<" type="<<(int)s_obj.type<<std::endl;
1734 // This will also add the object to the active static list
1735 addActiveObjectRaw(obj, false, dtime_s);
1737 // Clear stored list
1738 block->m_static_objects.m_stored.clear();
1739 // Add leftover failed stuff to stored list
1740 for(std::vector<StaticObject>::iterator
1741 i = new_stored.begin();
1742 i != new_stored.end(); ++i) {
1743 StaticObject &s_obj = *i;
1744 block->m_static_objects.m_stored.push_back(s_obj);
1747 // Turn the active counterparts of activated objects not pending for
1749 for(std::map<u16, StaticObject>::iterator
1750 i = block->m_static_objects.m_active.begin();
1751 i != block->m_static_objects.m_active.end(); ++i)
1754 ServerActiveObject *object = getActiveObject(id);
1756 object->m_pending_deactivation = false;
1760 Note: Block hasn't really been modified here.
1761 The objects have just been activated and moved from the stored
1762 static list to the active static list.
1763 As such, the block is essentially the same.
1764 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1765 Otherwise there would be a huge amount of unnecessary I/O.
1770 Convert objects that are not standing inside active blocks to static.
1772 If m_known_by_count != 0, active object is not deleted, but static
1773 data is still updated.
1775 If force_delete is set, active object is deleted nevertheless. It
1776 shall only be set so in the destructor of the environment.
1778 If block wasn't generated (not in memory or on disk),
1780 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1782 std::vector<u16> objects_to_remove;
1783 for(std::map<u16, ServerActiveObject*>::iterator
1784 i = m_active_objects.begin();
1785 i != m_active_objects.end(); ++i) {
1786 ServerActiveObject* obj = i->second;
1789 // Do not deactivate if static data creation not allowed
1790 if(!force_delete && !obj->isStaticAllowed())
1793 // If pending deactivation, let removeRemovedObjects() do it
1794 if(!force_delete && obj->m_pending_deactivation)
1798 v3f objectpos = obj->getBasePosition();
1800 // The block in which the object resides in
1801 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1803 // If object's static data is stored in a deactivated block and object
1804 // is actually located in an active block, re-save to the block in
1805 // which the object is actually located in.
1807 obj->m_static_exists &&
1808 !m_active_blocks.contains(obj->m_static_block) &&
1809 m_active_blocks.contains(blockpos_o))
1811 v3s16 old_static_block = obj->m_static_block;
1813 // Save to block where object is located
1814 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1816 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1817 <<"Could not save object id="<<id
1818 <<" to it's current block "<<PP(blockpos_o)
1822 std::string staticdata_new = obj->getStaticData();
1823 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1824 block->m_static_objects.insert(id, s_obj);
1825 obj->m_static_block = blockpos_o;
1826 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827 MOD_REASON_STATIC_DATA_ADDED);
1829 // Delete from block where object was located
1830 block = m_map->emergeBlock(old_static_block, false);
1832 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1833 <<"Could not delete object id="<<id
1834 <<" from it's previous block "<<PP(old_static_block)
1838 block->m_static_objects.remove(id);
1839 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1840 MOD_REASON_STATIC_DATA_REMOVED);
1844 // If block is active, don't remove
1845 if(!force_delete && m_active_blocks.contains(blockpos_o))
1848 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1849 <<"deactivating object id="<<id<<" on inactive block "
1850 <<PP(blockpos_o)<<std::endl;
1852 // If known by some client, don't immediately delete.
1853 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1856 Update the static data
1859 if(obj->isStaticAllowed())
1861 // Create new static object
1862 std::string staticdata_new = obj->getStaticData();
1863 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1865 bool stays_in_same_block = false;
1866 bool data_changed = true;
1868 if(obj->m_static_exists){
1869 if(obj->m_static_block == blockpos_o)
1870 stays_in_same_block = true;
1872 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1874 std::map<u16, StaticObject>::iterator n =
1875 block->m_static_objects.m_active.find(id);
1876 if(n != block->m_static_objects.m_active.end()){
1877 StaticObject static_old = n->second;
1879 float save_movem = obj->getMinimumSavedMovement();
1881 if(static_old.data == staticdata_new &&
1882 (static_old.pos - objectpos).getLength() < save_movem)
1883 data_changed = false;
1885 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1886 <<"id="<<id<<" m_static_exists=true but "
1887 <<"static data doesn't actually exist in "
1888 <<PP(obj->m_static_block)<<std::endl;
1892 bool shall_be_written = (!stays_in_same_block || data_changed);
1894 // Delete old static object
1895 if(obj->m_static_exists)
1897 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1900 block->m_static_objects.remove(id);
1901 obj->m_static_exists = false;
1902 // Only mark block as modified if data changed considerably
1903 if(shall_be_written)
1904 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1905 MOD_REASON_STATIC_DATA_CHANGED);
1909 // Add to the block where the object is located in
1910 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1911 // Get or generate the block
1912 MapBlock *block = NULL;
1914 block = m_map->emergeBlock(blockpos);
1915 } catch(InvalidPositionException &e){
1916 // Handled via NULL pointer
1917 // NOTE: emergeBlock's failure is usually determined by it
1918 // actually returning NULL
1923 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1924 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1925 <<" statically but block "<<PP(blockpos)
1926 <<" already contains "
1927 <<block->m_static_objects.m_stored.size()
1929 <<" Forcing delete."<<std::endl;
1930 force_delete = true;
1932 // If static counterpart already exists in target block,
1934 // This shouldn't happen because the object is removed from
1935 // the previous block before this according to
1936 // obj->m_static_block, but happens rarely for some unknown
1937 // reason. Unsuccessful attempts have been made to find
1939 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1940 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1942 block->m_static_objects.remove(id);
1944 // Store static data
1945 u16 store_id = pending_delete ? id : 0;
1946 block->m_static_objects.insert(store_id, s_obj);
1948 // Only mark block as modified if data changed considerably
1949 if(shall_be_written)
1950 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1951 MOD_REASON_STATIC_DATA_CHANGED);
1953 obj->m_static_exists = true;
1954 obj->m_static_block = block->getPos();
1959 v3s16 p = floatToInt(objectpos, BS);
1960 errorstream<<"ServerEnv: Could not find or generate "
1961 <<"a block for storing id="<<obj->getId()
1962 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1969 If known by some client, set pending deactivation.
1970 Otherwise delete it immediately.
1973 if(pending_delete && !force_delete)
1975 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1976 <<"object id="<<id<<" is known by clients"
1977 <<"; not deleting yet"<<std::endl;
1979 obj->m_pending_deactivation = true;
1983 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1984 <<"object id="<<id<<" is not known by clients"
1985 <<"; deleting"<<std::endl;
1987 // Tell the object about removal
1988 obj->removingFromEnvironment();
1989 // Deregister in scripting api
1990 m_script->removeObjectReference(obj);
1992 // Delete active object
1993 if(obj->environmentDeletes())
1995 // Id to be removed from m_active_objects
1996 objects_to_remove.push_back(id);
1999 // Remove references from m_active_objects
2000 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2001 i != objects_to_remove.end(); ++i) {
2002 m_active_objects.erase(*i);
2009 #include "clientsimpleobject.h"
2015 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2016 ITextureSource *texturesource, IGameDef *gamedef,
2017 IrrlichtDevice *irr):
2020 m_texturesource(texturesource),
2025 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2028 ClientEnvironment::~ClientEnvironment()
2030 // delete active objects
2031 for(std::map<u16, ClientActiveObject*>::iterator
2032 i = m_active_objects.begin();
2033 i != m_active_objects.end(); ++i)
2038 for(std::vector<ClientSimpleObject*>::iterator
2039 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2047 Map & ClientEnvironment::getMap()
2052 ClientMap & ClientEnvironment::getClientMap()
2057 void ClientEnvironment::addPlayer(Player *player)
2059 DSTACK(__FUNCTION_NAME);
2061 It is a failure if player is local and there already is a local
2064 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2065 "Player is local but there is already a local player");
2067 Environment::addPlayer(player);
2070 LocalPlayer * ClientEnvironment::getLocalPlayer()
2072 for(std::vector<Player*>::iterator i = m_players.begin();
2073 i != m_players.end(); ++i) {
2074 Player *player = *i;
2075 if(player->isLocal())
2076 return (LocalPlayer*)player;
2081 void ClientEnvironment::step(float dtime)
2083 DSTACK(__FUNCTION_NAME);
2085 /* Step time of day */
2086 stepTimeOfDay(dtime);
2088 // Get some settings
2089 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2090 bool free_move = fly_allowed && g_settings->getBool("free_move");
2093 LocalPlayer *lplayer = getLocalPlayer();
2095 // collision info queue
2096 std::vector<CollisionInfo> player_collisions;
2099 Get the speed the player is going
2101 bool is_climbing = lplayer->is_climbing;
2103 f32 player_speed = lplayer->getSpeed().getLength();
2106 Maximum position increment
2108 //f32 position_max_increment = 0.05*BS;
2109 f32 position_max_increment = 0.1*BS;
2111 // Maximum time increment (for collision detection etc)
2112 // time = distance / speed
2113 f32 dtime_max_increment = 1;
2114 if(player_speed > 0.001)
2115 dtime_max_increment = position_max_increment / player_speed;
2117 // Maximum time increment is 10ms or lower
2118 if(dtime_max_increment > 0.01)
2119 dtime_max_increment = 0.01;
2121 // Don't allow overly huge dtime
2125 f32 dtime_downcount = dtime;
2128 Stuff that has a maximum time increment
2137 if(dtime_downcount > dtime_max_increment)
2139 dtime_part = dtime_max_increment;
2140 dtime_downcount -= dtime_part;
2144 dtime_part = dtime_downcount;
2146 Setting this to 0 (no -=dtime_part) disables an infinite loop
2147 when dtime_part is so small that dtime_downcount -= dtime_part
2150 dtime_downcount = 0;
2159 if(free_move == false && is_climbing == false)
2162 v3f speed = lplayer->getSpeed();
2163 if(lplayer->in_liquid == false)
2164 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2166 // Liquid floating / sinking
2167 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2168 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2170 // Liquid resistance
2171 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2173 // How much the node's viscosity blocks movement, ranges between 0 and 1
2174 // Should match the scale at which viscosity increase affects other liquid attributes
2175 const f32 viscosity_factor = 0.3;
2177 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2178 f32 dl = d_wanted.getLength();
2179 if(dl > lplayer->movement_liquid_fluidity_smooth)
2180 dl = lplayer->movement_liquid_fluidity_smooth;
2181 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2183 v3f d = d_wanted.normalize() * dl;
2187 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2188 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2189 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2190 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2191 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2192 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2196 lplayer->setSpeed(speed);
2201 This also does collision detection.
2203 lplayer->move(dtime_part, this, position_max_increment,
2204 &player_collisions);
2207 while(dtime_downcount > 0.001);
2209 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2211 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2212 i != player_collisions.end(); ++i) {
2213 CollisionInfo &info = *i;
2214 v3f speed_diff = info.new_speed - info.old_speed;;
2215 // Handle only fall damage
2216 // (because otherwise walking against something in fast_move kills you)
2217 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2219 // Get rid of other components
2222 f32 pre_factor = 1; // 1 hp per node/s
2223 f32 tolerance = BS*14; // 5 without damage
2224 f32 post_factor = 1; // 1 hp per node/s
2225 if(info.type == COLLISION_NODE)
2227 const ContentFeatures &f = m_gamedef->ndef()->
2228 get(m_map->getNodeNoEx(info.node_p));
2229 // Determine fall damage multiplier
2230 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2231 pre_factor = 1.0 + (float)addp/100.0;
2233 float speed = pre_factor * speed_diff.getLength();
2234 if(speed > tolerance)
2236 f32 damage_f = (speed - tolerance)/BS * post_factor;
2237 u16 damage = (u16)(damage_f+0.5);
2239 damageLocalPlayer(damage, true);
2240 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2241 m_gamedef->event()->put(e);
2247 A quick draft of lava damage
2249 if(m_lava_hurt_interval.step(dtime, 1.0))
2251 v3f pf = lplayer->getPosition();
2253 // Feet, middle and head
2254 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2255 MapNode n1 = m_map->getNodeNoEx(p1);
2256 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2257 MapNode n2 = m_map->getNodeNoEx(p2);
2258 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2259 MapNode n3 = m_map->getNodeNoEx(p3);
2261 u32 damage_per_second = 0;
2262 damage_per_second = MYMAX(damage_per_second,
2263 m_gamedef->ndef()->get(n1).damage_per_second);
2264 damage_per_second = MYMAX(damage_per_second,
2265 m_gamedef->ndef()->get(n2).damage_per_second);
2266 damage_per_second = MYMAX(damage_per_second,
2267 m_gamedef->ndef()->get(n3).damage_per_second);
2269 if(damage_per_second != 0)
2271 damageLocalPlayer(damage_per_second, true);
2278 if(m_drowning_interval.step(dtime, 2.0))
2280 v3f pf = lplayer->getPosition();
2283 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2284 MapNode n = m_map->getNodeNoEx(p);
2285 ContentFeatures c = m_gamedef->ndef()->get(n);
2286 u8 drowning_damage = c.drowning;
2287 if(drowning_damage > 0 && lplayer->hp > 0){
2288 u16 breath = lplayer->getBreath();
2295 lplayer->setBreath(breath);
2296 updateLocalPlayerBreath(breath);
2299 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2300 damageLocalPlayer(drowning_damage, true);
2303 if(m_breathing_interval.step(dtime, 0.5))
2305 v3f pf = lplayer->getPosition();
2308 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2309 MapNode n = m_map->getNodeNoEx(p);
2310 ContentFeatures c = m_gamedef->ndef()->get(n);
2312 lplayer->setBreath(11);
2314 else if(c.drowning == 0){
2315 u16 breath = lplayer->getBreath();
2318 lplayer->setBreath(breath);
2319 updateLocalPlayerBreath(breath);
2325 Stuff that can be done in an arbitarily large dtime
2327 for(std::vector<Player*>::iterator i = m_players.begin();
2328 i != m_players.end(); ++i) {
2329 Player *player = *i;
2332 Handle non-local players
2334 if(player->isLocal() == false) {
2336 player->move(dtime, this, 100*BS);
2341 // Update lighting on local player (used for wield item)
2342 u32 day_night_ratio = getDayNightRatio();
2346 // On InvalidPositionException, use this as default
2347 // (day: LIGHT_SUN, night: 0)
2348 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2350 v3s16 p = lplayer->getLightPosition();
2351 node_at_lplayer = m_map->getNodeNoEx(p);
2353 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2354 u8 day = light & 0xff;
2355 u8 night = (light >> 8) & 0xff;
2356 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2360 Step active objects and update lighting of them
2363 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2364 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2365 for(std::map<u16, ClientActiveObject*>::iterator
2366 i = m_active_objects.begin();
2367 i != m_active_objects.end(); ++i)
2369 ClientActiveObject* obj = i->second;
2371 obj->step(dtime, this);
2380 v3s16 p = obj->getLightPosition();
2381 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2383 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2385 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2387 obj->updateLight(light);
2392 Step and handle simple objects
2394 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2395 for(std::vector<ClientSimpleObject*>::iterator
2396 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2397 std::vector<ClientSimpleObject*>::iterator cur = i;
2398 ClientSimpleObject *simple = *cur;
2400 simple->step(dtime);
2401 if(simple->m_to_be_removed) {
2403 i = m_simple_objects.erase(cur);
2411 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2413 m_simple_objects.push_back(simple);
2416 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2418 ClientActiveObject *obj = getActiveObject(id);
2419 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2420 return (GenericCAO*) obj;
2425 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2427 std::map<u16, ClientActiveObject*>::iterator n;
2428 n = m_active_objects.find(id);
2429 if(n == m_active_objects.end())
2434 bool isFreeClientActiveObjectId(u16 id,
2435 std::map<u16, ClientActiveObject*> &objects)
2440 return objects.find(id) == objects.end();
2443 u16 getFreeClientActiveObjectId(
2444 std::map<u16, ClientActiveObject*> &objects)
2446 //try to reuse id's as late as possible
2447 static u16 last_used_id = 0;
2448 u16 startid = last_used_id;
2452 if(isFreeClientActiveObjectId(last_used_id, objects))
2453 return last_used_id;
2455 if(last_used_id == startid)
2460 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2462 assert(object); // Pre-condition
2463 if(object->getId() == 0)
2465 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2468 infostream<<"ClientEnvironment::addActiveObject(): "
2469 <<"no free ids available"<<std::endl;
2473 object->setId(new_id);
2475 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2477 infostream<<"ClientEnvironment::addActiveObject(): "
2478 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2482 infostream<<"ClientEnvironment::addActiveObject(): "
2483 <<"added (id="<<object->getId()<<")"<<std::endl;
2484 m_active_objects[object->getId()] = object;
2485 object->addToScene(m_smgr, m_texturesource, m_irr);
2486 { // Update lighting immediately
2491 v3s16 p = object->getLightPosition();
2492 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2494 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2496 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2498 object->updateLight(light);
2500 return object->getId();
2503 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2504 const std::string &init_data)
2506 ClientActiveObject* obj =
2507 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2510 infostream<<"ClientEnvironment::addActiveObject(): "
2511 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2520 obj->initialize(init_data);
2522 catch(SerializationError &e)
2524 errorstream<<"ClientEnvironment::addActiveObject():"
2525 <<" id="<<id<<" type="<<type
2526 <<": SerializationError in initialize(): "
2528 <<": init_data="<<serializeJsonString(init_data)
2532 addActiveObject(obj);
2535 void ClientEnvironment::removeActiveObject(u16 id)
2537 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2538 <<"id="<<id<<std::endl;
2539 ClientActiveObject* obj = getActiveObject(id);
2542 infostream<<"ClientEnvironment::removeActiveObject(): "
2543 <<"id="<<id<<" not found"<<std::endl;
2546 obj->removeFromScene(true);
2548 m_active_objects.erase(id);
2551 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2553 ClientActiveObject *obj = getActiveObject(id);
2555 infostream << "ClientEnvironment::processActiveObjectMessage():"
2556 << " got message for id=" << id << ", which doesn't exist."
2562 obj->processMessage(data);
2563 } catch (SerializationError &e) {
2564 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2565 << " id=" << id << " type=" << obj->getType()
2566 << " SerializationError in processMessage(): " << e.what()
2572 Callbacks for activeobjects
2575 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2577 LocalPlayer *lplayer = getLocalPlayer();
2581 if (lplayer->hp > damage)
2582 lplayer->hp -= damage;
2587 ClientEnvEvent event;
2588 event.type = CEE_PLAYER_DAMAGE;
2589 event.player_damage.amount = damage;
2590 event.player_damage.send_to_server = handle_hp;
2591 m_client_event_queue.push_back(event);
2594 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2596 ClientEnvEvent event;
2597 event.type = CEE_PLAYER_BREATH;
2598 event.player_breath.amount = breath;
2599 m_client_event_queue.push_back(event);
2603 Client likes to call these
2606 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2607 std::vector<DistanceSortedActiveObject> &dest)
2609 for(std::map<u16, ClientActiveObject*>::iterator
2610 i = m_active_objects.begin();
2611 i != m_active_objects.end(); ++i)
2613 ClientActiveObject* obj = i->second;
2615 f32 d = (obj->getPosition() - origin).getLength();
2620 DistanceSortedActiveObject dso(obj, d);
2622 dest.push_back(dso);
2626 ClientEnvEvent ClientEnvironment::getClientEvent()
2628 ClientEnvEvent event;
2629 if(m_client_event_queue.empty())
2630 event.type = CEE_NONE;
2632 event = m_client_event_queue.front();
2633 m_client_event_queue.pop_front();
2638 #endif // #ifndef SERVER