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 "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 Environment::Environment():
52 m_time_of_day_speed(0),
54 m_time_of_day_f(9000./24000),
55 m_time_conversion_skew(0.0f),
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(Player* player)
90 for (std::vector<Player*>::iterator it = m_players.begin();
91 it != m_players.end(); ++it) {
92 if ((*it) == player) {
100 Player * Environment::getPlayer(u16 peer_id)
102 for(std::vector<Player*>::iterator i = m_players.begin();
103 i != m_players.end(); ++i) {
105 if(player->peer_id == peer_id)
111 Player * Environment::getPlayer(const char *name)
113 for(std::vector<Player*>::iterator i = m_players.begin();
114 i != m_players.end(); ++i) {
116 if(strcmp(player->getName(), name) == 0)
122 Player * Environment::getRandomConnectedPlayer()
124 std::vector<Player*> connected_players = getPlayers(true);
125 u32 chosen_one = myrand() % connected_players.size();
127 for(std::vector<Player*>::iterator
128 i = connected_players.begin();
129 i != connected_players.end(); ++i) {
130 if(j == chosen_one) {
139 Player * Environment::getNearestConnectedPlayer(v3f pos)
141 std::vector<Player*> connected_players = getPlayers(true);
143 Player *nearest_player = NULL;
144 for(std::vector<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i) {
148 f32 d = player->getPosition().getDistanceFrom(pos);
149 if(d < nearest_d || nearest_player == NULL) {
151 nearest_player = player;
154 return nearest_player;
157 std::vector<Player*> Environment::getPlayers()
162 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
164 std::vector<Player*> newlist;
165 for(std::vector<Player*>::iterator
166 i = m_players.begin();
167 i != m_players.end(); ++i) {
170 if(ignore_disconnected) {
171 // Ignore disconnected players
172 if(player->peer_id == 0)
176 newlist.push_back(player);
181 u32 Environment::getDayNightRatio()
183 MutexAutoLock lock(this->m_time_lock);
184 if (m_enable_day_night_ratio_override)
185 return m_day_night_ratio_override;
186 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
189 void Environment::setTimeOfDaySpeed(float speed)
191 m_time_of_day_speed = speed;
194 float Environment::getTimeOfDaySpeed()
196 return m_time_of_day_speed;
199 void Environment::setDayNightRatioOverride(bool enable, u32 value)
201 MutexAutoLock lock(this->m_time_lock);
202 m_enable_day_night_ratio_override = enable;
203 m_day_night_ratio_override = value;
206 void Environment::setTimeOfDay(u32 time)
208 MutexAutoLock lock(this->m_time_lock);
209 m_time_of_day = time;
210 m_time_of_day_f = (float)time / 24000.0;
213 u32 Environment::getTimeOfDay()
215 MutexAutoLock lock(this->m_time_lock);
216 return m_time_of_day;
219 float Environment::getTimeOfDayF()
221 MutexAutoLock lock(this->m_time_lock);
222 return m_time_of_day_f;
225 void Environment::stepTimeOfDay(float dtime)
227 MutexAutoLock lock(this->m_time_lock);
229 // Cached in order to prevent the two reads we do to give
230 // different results (can be written by code not under the lock)
231 f32 cached_time_of_day_speed = m_time_of_day_speed;
233 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
234 m_time_conversion_skew += dtime;
235 u32 units = (u32)(m_time_conversion_skew * speed);
239 if (m_time_of_day + units >= 24000)
241 m_time_of_day = (m_time_of_day + units) % 24000;
243 m_time_of_day_f = (float)m_time_of_day / 24000.0;
246 m_time_conversion_skew -= (f32)units / speed;
249 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
250 if (m_time_of_day_f > 1.0)
251 m_time_of_day_f -= 1.0;
252 if (m_time_of_day_f < 0.0)
253 m_time_of_day_f += 1.0;
261 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
265 // Initialize timer to random value to spread processing
266 float itv = abm->getTriggerInterval();
267 itv = MYMAX(0.001, itv); // No less than 1ms
268 int minval = MYMAX(-0.51*itv, -60); // Clamp to
269 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
270 timer = myrand_range(minval, maxval);
277 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
280 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
281 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
282 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
289 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
291 std::set<v3s16> &blocks_removed,
292 std::set<v3s16> &blocks_added)
297 std::set<v3s16> newlist = m_forceloaded_list;
298 for(std::vector<v3s16>::iterator i = active_positions.begin();
299 i != active_positions.end(); ++i)
301 fillRadiusBlock(*i, radius, newlist);
305 Find out which blocks on the old list are not on the new list
307 // Go through old list
308 for(std::set<v3s16>::iterator i = m_list.begin();
309 i != m_list.end(); ++i)
312 // If not on new list, it's been removed
313 if(newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for(std::set<v3s16>::iterator i = newlist.begin();
322 i != newlist.end(); ++i)
325 // If not on old list, it's been added
326 if(m_list.find(p) == m_list.end())
327 blocks_added.insert(p);
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
346 ServerEnvironment::ServerEnvironment(ServerMap *map,
347 GameScripting *scriptIface, IGameDef *gamedef,
348 const std::string &path_world) :
350 m_script(scriptIface),
352 m_path_world(path_world),
353 m_send_recommended_timer(0),
354 m_active_block_interval_overload_skip(0),
356 m_game_time_fraction_counter(0),
357 m_last_clear_objects_time(0),
358 m_recommended_send_interval(0.1),
359 m_max_lag_estimate(0.1)
363 ServerEnvironment::~ServerEnvironment()
365 // Clear active block list.
366 // This makes the next one delete all active objects.
367 m_active_blocks.clear();
369 // Convert all objects to static and delete the active objects
370 deactivateFarObjects(true);
375 // Delete ActiveBlockModifiers
376 for(std::vector<ABMWithState>::iterator
377 i = m_abms.begin(); i != m_abms.end(); ++i){
382 Map & ServerEnvironment::getMap()
387 ServerMap & ServerEnvironment::getServerMap()
392 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
394 float distance = pos1.getDistanceFrom(pos2);
396 //calculate normalized direction vector
397 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
398 (pos2.Y - pos1.Y)/distance,
399 (pos2.Z - pos1.Z)/distance);
401 //find out if there's a node on path between pos1 and pos2
402 for (float i = 1; i < distance; i += stepsize) {
403 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
404 normalized_vector.Y * i,
405 normalized_vector.Z * i) +pos1,BS);
407 MapNode n = getMap().getNodeNoEx(pos);
409 if(n.param0 != CONTENT_AIR) {
419 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
420 const std::string &str_reason, bool reconnect)
422 for (std::vector<Player*>::iterator it = m_players.begin();
423 it != m_players.end();
425 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
426 (*it)->protocol_version, (AccessDeniedCode)reason,
427 str_reason, reconnect);
431 void ServerEnvironment::saveLoadedPlayers()
433 std::string players_path = m_path_world + DIR_DELIM "players";
434 fs::CreateDir(players_path);
436 for (std::vector<Player*>::iterator it = m_players.begin();
437 it != m_players.end();
439 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
440 if (player->checkModified()) {
441 player->save(players_path);
446 void ServerEnvironment::savePlayer(RemotePlayer *player)
448 std::string players_path = m_path_world + DIR_DELIM "players";
449 fs::CreateDir(players_path);
451 player->save(players_path);
454 Player *ServerEnvironment::loadPlayer(const std::string &playername)
456 bool newplayer = false;
458 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
459 std::string path = players_path + playername;
461 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
463 player = new RemotePlayer(m_gamedef, "");
467 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
468 //// Open file and deserialize
469 std::ifstream is(path.c_str(), std::ios_base::binary);
472 player->deSerialize(is, path);
475 if (player->getName() == playername) {
480 path = players_path + playername + itos(i);
484 infostream << "Player file for player " << playername
485 << " not found" << std::endl;
493 player->setModified(false);
497 void ServerEnvironment::saveMeta()
499 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
501 // Open file and serialize
502 std::ostringstream ss(std::ios_base::binary);
505 args.setU64("game_time", m_game_time);
506 args.setU64("time_of_day", getTimeOfDay());
507 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
511 if(!fs::safeWriteToFile(path, ss.str()))
513 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
515 throw SerializationError("Couldn't save env meta");
519 void ServerEnvironment::loadMeta()
521 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
523 // Open file and deserialize
524 std::ifstream is(path.c_str(), std::ios_base::binary);
526 infostream << "ServerEnvironment::loadMeta(): Failed to open "
527 << path << std::endl;
528 throw SerializationError("Couldn't load env meta");
533 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
534 throw SerializationError("ServerEnvironment::loadMeta(): "
535 "EnvArgsEnd not found!");
539 m_game_time = args.getU64("game_time");
540 } catch (SettingNotFoundException &e) {
541 // Getting this is crucial, otherwise timestamps are useless
542 throw SerializationError("Couldn't load env meta game_time");
546 setTimeOfDay(args.getU64("time_of_day"));
547 } catch (SettingNotFoundException &e) {
548 // This is not as important
553 m_last_clear_objects_time = args.getU64("last_clear_objects_time");
554 } catch (SettingNotFoundException &e) {
555 // If missing, do as if clearObjects was never called
556 m_last_clear_objects_time = 0;
562 ActiveBlockModifier *abm;
564 std::set<content_t> required_neighbors;
570 ServerEnvironment *m_env;
571 std::map<content_t, std::vector<ActiveABM> > m_aabms;
573 ABMHandler(std::vector<ABMWithState> &abms,
574 float dtime_s, ServerEnvironment *env,
580 INodeDefManager *ndef = env->getGameDef()->ndef();
581 for(std::vector<ABMWithState>::iterator
582 i = abms.begin(); i != abms.end(); ++i) {
583 ActiveBlockModifier *abm = i->abm;
584 float trigger_interval = abm->getTriggerInterval();
585 if(trigger_interval < 0.001)
586 trigger_interval = 0.001;
587 float actual_interval = dtime_s;
590 if(i->timer < trigger_interval)
592 i->timer -= trigger_interval;
593 actual_interval = trigger_interval;
595 float chance = abm->getTriggerChance();
600 if(abm->getSimpleCatchUp()) {
601 float intervals = actual_interval / trigger_interval;
604 aabm.chance = chance / intervals;
608 aabm.chance = chance;
611 std::set<std::string> required_neighbors_s
612 = abm->getRequiredNeighbors();
613 for(std::set<std::string>::iterator
614 i = required_neighbors_s.begin();
615 i != required_neighbors_s.end(); ++i)
617 ndef->getIds(*i, aabm.required_neighbors);
620 std::set<std::string> contents_s = abm->getTriggerContents();
621 for(std::set<std::string>::iterator
622 i = contents_s.begin(); i != contents_s.end(); ++i)
624 std::set<content_t> ids;
625 ndef->getIds(*i, ids);
626 for(std::set<content_t>::const_iterator k = ids.begin();
630 std::map<content_t, std::vector<ActiveABM> >::iterator j;
632 if(j == m_aabms.end()){
633 std::vector<ActiveABM> aabmlist;
634 m_aabms[c] = aabmlist;
637 j->second.push_back(aabm);
642 // Find out how many objects the given block and its neighbours contain.
643 // Returns the number of objects in the block, and also in 'wider' the
644 // number of objects in the block and all its neighbours. The latter
645 // may an estimate if any neighbours are unloaded.
646 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
649 u32 wider_unknown_count = 0;
650 for(s16 x=-1; x<=1; x++)
651 for(s16 y=-1; y<=1; y++)
652 for(s16 z=-1; z<=1; z++)
654 MapBlock *block2 = map->getBlockNoCreateNoEx(
655 block->getPos() + v3s16(x,y,z));
657 wider_unknown_count++;
660 wider += block2->m_static_objects.m_active.size()
661 + block2->m_static_objects.m_stored.size();
664 u32 active_object_count = block->m_static_objects.m_active.size();
665 u32 wider_known_count = 3*3*3 - wider_unknown_count;
666 wider += wider_unknown_count * wider / wider_known_count;
667 return active_object_count;
670 void apply(MapBlock *block)
675 ServerMap *map = &m_env->getServerMap();
677 u32 active_object_count_wider;
678 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
679 m_env->m_added_objects = 0;
682 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
683 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
684 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
686 MapNode n = block->getNodeNoEx(p0);
687 content_t c = n.getContent();
688 v3s16 p = p0 + block->getPosRelative();
690 std::map<content_t, std::vector<ActiveABM> >::iterator j;
692 if(j == m_aabms.end())
695 for(std::vector<ActiveABM>::iterator
696 i = j->second.begin(); i != j->second.end(); ++i) {
697 if(myrand() % i->chance != 0)
701 if(!i->required_neighbors.empty())
704 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
705 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
706 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
710 MapNode n = map->getNodeNoEx(p1);
711 content_t c = n.getContent();
712 std::set<content_t>::const_iterator k;
713 k = i->required_neighbors.find(c);
714 if(k != i->required_neighbors.end()){
718 // No required neighbor found
723 // Call all the trigger variations
724 i->abm->trigger(m_env, p, n);
725 i->abm->trigger(m_env, p, n,
726 active_object_count, active_object_count_wider);
728 // Count surrounding objects again if the abms added any
729 if(m_env->m_added_objects > 0) {
730 active_object_count = countObjects(block, map, active_object_count_wider);
731 m_env->m_added_objects = 0;
738 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
740 // Reset usage timer immediately, otherwise a block that becomes active
741 // again at around the same time as it would normally be unloaded will
742 // get unloaded incorrectly. (I think this still leaves a small possibility
743 // of a race condition between this and server::AsyncRunStep, which only
744 // some kind of synchronisation will fix, but it at least reduces the window
745 // of opportunity for it to break from seconds to nanoseconds)
746 block->resetUsageTimer();
748 // Get time difference
750 u32 stamp = block->getTimestamp();
751 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
752 dtime_s = m_game_time - stamp;
753 dtime_s += additional_dtime;
755 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
756 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
758 // Remove stored static objects if clearObjects was called since block's timestamp
759 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
760 block->m_static_objects.m_stored.clear();
761 // do not set changed flag to avoid unnecessary mapblock writes
764 // Set current time as timestamp
765 block->setTimestampNoChangedFlag(m_game_time);
767 /*infostream<<"ServerEnvironment::activateBlock(): block is "
768 <<dtime_s<<" seconds old."<<std::endl;*/
770 // Activate stored objects
771 activateObjects(block, dtime_s);
774 std::map<v3s16, NodeTimer> elapsed_timers =
775 block->m_node_timers.step((float)dtime_s);
776 if(!elapsed_timers.empty()){
778 for(std::map<v3s16, NodeTimer>::iterator
779 i = elapsed_timers.begin();
780 i != elapsed_timers.end(); ++i){
781 n = block->getNodeNoEx(i->first);
782 v3s16 p = i->first + block->getPosRelative();
783 if(m_script->node_on_timer(p,n,i->second.elapsed))
784 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
788 /* Handle ActiveBlockModifiers */
789 ABMHandler abmhandler(m_abms, dtime_s, this, false);
790 abmhandler.apply(block);
793 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
795 m_abms.push_back(ABMWithState(abm));
798 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
800 INodeDefManager *ndef = m_gamedef->ndef();
801 MapNode n_old = m_map->getNodeNoEx(p);
804 if (ndef->get(n_old).has_on_destruct)
805 m_script->node_on_destruct(p, n_old);
808 if (!m_map->addNodeWithEvent(p, n))
811 // Update active VoxelManipulator if a mapgen thread
812 m_map->updateVManip(p);
814 // Call post-destructor
815 if (ndef->get(n_old).has_after_destruct)
816 m_script->node_after_destruct(p, n_old);
819 if (ndef->get(n).has_on_construct)
820 m_script->node_on_construct(p, n);
825 bool ServerEnvironment::removeNode(v3s16 p)
827 INodeDefManager *ndef = m_gamedef->ndef();
828 MapNode n_old = m_map->getNodeNoEx(p);
831 if (ndef->get(n_old).has_on_destruct)
832 m_script->node_on_destruct(p, n_old);
835 // This is slightly optimized compared to addNodeWithEvent(air)
836 if (!m_map->removeNodeWithEvent(p))
839 // Update active VoxelManipulator if a mapgen thread
840 m_map->updateVManip(p);
842 // Call post-destructor
843 if (ndef->get(n_old).has_after_destruct)
844 m_script->node_after_destruct(p, n_old);
846 // Air doesn't require constructor
850 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
852 if (!m_map->addNodeWithEvent(p, n, false))
855 // Update active VoxelManipulator if a mapgen thread
856 m_map->updateVManip(p);
861 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
863 for(std::map<u16, ServerActiveObject*>::iterator
864 i = m_active_objects.begin();
865 i != m_active_objects.end(); ++i)
867 ServerActiveObject* obj = i->second;
869 v3f objectpos = obj->getBasePosition();
870 if(objectpos.getDistanceFrom(pos) > radius)
872 objects.push_back(id);
876 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
878 infostream << "ServerEnvironment::clearObjects(): "
879 << "Removing all active objects" << std::endl;
880 std::vector<u16> objects_to_remove;
881 for (std::map<u16, ServerActiveObject*>::iterator
882 i = m_active_objects.begin();
883 i != m_active_objects.end(); ++i) {
884 ServerActiveObject* obj = i->second;
885 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
888 // Delete static object if block is loaded
889 if (obj->m_static_exists) {
890 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
892 block->m_static_objects.remove(id);
893 block->raiseModified(MOD_STATE_WRITE_NEEDED,
894 MOD_REASON_CLEAR_ALL_OBJECTS);
895 obj->m_static_exists = false;
898 // If known by some client, don't delete immediately
899 if (obj->m_known_by_count > 0) {
900 obj->m_pending_deactivation = true;
901 obj->m_removed = true;
905 // Tell the object about removal
906 obj->removingFromEnvironment();
907 // Deregister in scripting api
908 m_script->removeObjectReference(obj);
910 // Delete active object
911 if (obj->environmentDeletes())
913 // Id to be removed from m_active_objects
914 objects_to_remove.push_back(id);
917 // Remove references from m_active_objects
918 for (std::vector<u16>::iterator i = objects_to_remove.begin();
919 i != objects_to_remove.end(); ++i) {
920 m_active_objects.erase(*i);
923 // Get list of loaded blocks
924 std::vector<v3s16> loaded_blocks;
925 infostream << "ServerEnvironment::clearObjects(): "
926 << "Listing all loaded blocks" << std::endl;
927 m_map->listAllLoadedBlocks(loaded_blocks);
928 infostream << "ServerEnvironment::clearObjects(): "
929 << "Done listing all loaded blocks: "
930 << loaded_blocks.size()<<std::endl;
932 // Get list of loadable blocks
933 std::vector<v3s16> loadable_blocks;
934 if (mode == CLEAR_OBJECTS_MODE_FULL) {
935 infostream << "ServerEnvironment::clearObjects(): "
936 << "Listing all loadable blocks" << std::endl;
937 m_map->listAllLoadableBlocks(loadable_blocks);
938 infostream << "ServerEnvironment::clearObjects(): "
939 << "Done listing all loadable blocks: "
940 << loadable_blocks.size() << std::endl;
942 loadable_blocks = loaded_blocks;
945 infostream << "ServerEnvironment::clearObjects(): "
946 << "Now clearing objects in " << loadable_blocks.size()
947 << " blocks" << std::endl;
949 // Grab a reference on each loaded block to avoid unloading it
950 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
951 i != loaded_blocks.end(); ++i) {
953 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
954 assert(block != NULL);
958 // Remove objects in all loadable blocks
959 u32 unload_interval = U32_MAX;
960 if (mode == CLEAR_OBJECTS_MODE_FULL) {
961 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
962 unload_interval = MYMAX(unload_interval, 1);
964 u32 report_interval = loadable_blocks.size() / 10;
965 u32 num_blocks_checked = 0;
966 u32 num_blocks_cleared = 0;
967 u32 num_objs_cleared = 0;
968 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
969 i != loadable_blocks.end(); ++i) {
971 MapBlock *block = m_map->emergeBlock(p, false);
973 errorstream << "ServerEnvironment::clearObjects(): "
974 << "Failed to emerge block " << PP(p) << std::endl;
977 u32 num_stored = block->m_static_objects.m_stored.size();
978 u32 num_active = block->m_static_objects.m_active.size();
979 if (num_stored != 0 || num_active != 0) {
980 block->m_static_objects.m_stored.clear();
981 block->m_static_objects.m_active.clear();
982 block->raiseModified(MOD_STATE_WRITE_NEEDED,
983 MOD_REASON_CLEAR_ALL_OBJECTS);
984 num_objs_cleared += num_stored + num_active;
985 num_blocks_cleared++;
987 num_blocks_checked++;
989 if (report_interval != 0 &&
990 num_blocks_checked % report_interval == 0) {
991 float percent = 100.0 * (float)num_blocks_checked /
992 loadable_blocks.size();
993 infostream << "ServerEnvironment::clearObjects(): "
994 << "Cleared " << num_objs_cleared << " objects"
995 << " in " << num_blocks_cleared << " blocks ("
996 << percent << "%)" << std::endl;
998 if (num_blocks_checked % unload_interval == 0) {
999 m_map->unloadUnreferencedBlocks();
1002 m_map->unloadUnreferencedBlocks();
1004 // Drop references that were added above
1005 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1006 i != loaded_blocks.end(); ++i) {
1008 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1013 m_last_clear_objects_time = m_game_time;
1015 infostream << "ServerEnvironment::clearObjects(): "
1016 << "Finished: Cleared " << num_objs_cleared << " objects"
1017 << " in " << num_blocks_cleared << " blocks" << std::endl;
1020 void ServerEnvironment::step(float dtime)
1022 DSTACK(FUNCTION_NAME);
1024 //TimeTaker timer("ServerEnv step");
1026 /* Step time of day */
1027 stepTimeOfDay(dtime);
1030 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1031 // really matter that much.
1032 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1038 m_game_time_fraction_counter += dtime;
1039 u32 inc_i = (u32)m_game_time_fraction_counter;
1040 m_game_time += inc_i;
1041 m_game_time_fraction_counter -= (float)inc_i;
1048 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1049 for(std::vector<Player*>::iterator i = m_players.begin();
1050 i != m_players.end(); ++i)
1052 Player *player = *i;
1054 // Ignore disconnected players
1055 if(player->peer_id == 0)
1059 player->move(dtime, this, 100*BS);
1064 Manage active block list
1066 if(m_active_blocks_management_interval.step(dtime, 2.0))
1068 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1070 Get player block positions
1072 std::vector<v3s16> players_blockpos;
1073 for(std::vector<Player*>::iterator
1074 i = m_players.begin();
1075 i != m_players.end(); ++i) {
1076 Player *player = *i;
1077 // Ignore disconnected players
1078 if(player->peer_id == 0)
1081 v3s16 blockpos = getNodeBlockPos(
1082 floatToInt(player->getPosition(), BS));
1083 players_blockpos.push_back(blockpos);
1087 Update list of active blocks, collecting changes
1089 const s16 active_block_range = g_settings->getS16("active_block_range");
1090 std::set<v3s16> blocks_removed;
1091 std::set<v3s16> blocks_added;
1092 m_active_blocks.update(players_blockpos, active_block_range,
1093 blocks_removed, blocks_added);
1096 Handle removed blocks
1099 // Convert active objects that are no more in active blocks to static
1100 deactivateFarObjects(false);
1102 for(std::set<v3s16>::iterator
1103 i = blocks_removed.begin();
1104 i != blocks_removed.end(); ++i)
1108 /* infostream<<"Server: Block " << PP(p)
1109 << " became inactive"<<std::endl; */
1111 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1115 // Set current time as timestamp (and let it set ChangedFlag)
1116 block->setTimestamp(m_game_time);
1123 for(std::set<v3s16>::iterator
1124 i = blocks_added.begin();
1125 i != blocks_added.end(); ++i)
1129 MapBlock *block = m_map->getBlockOrEmerge(p);
1131 m_active_blocks.m_list.erase(p);
1135 activateBlock(block);
1136 /* infostream<<"Server: Block " << PP(p)
1137 << " became active"<<std::endl; */
1142 Mess around in active blocks
1144 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1146 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1150 for(std::set<v3s16>::iterator
1151 i = m_active_blocks.m_list.begin();
1152 i != m_active_blocks.m_list.end(); ++i)
1156 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1157 <<") being handled"<<std::endl;*/
1159 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1163 // Reset block usage timer
1164 block->resetUsageTimer();
1166 // Set current time as timestamp
1167 block->setTimestampNoChangedFlag(m_game_time);
1168 // If time has changed much from the one on disk,
1169 // set block to be saved when it is unloaded
1170 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1171 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1172 MOD_REASON_BLOCK_EXPIRED);
1175 std::map<v3s16, NodeTimer> elapsed_timers =
1176 block->m_node_timers.step((float)dtime);
1177 if(!elapsed_timers.empty()){
1179 for(std::map<v3s16, NodeTimer>::iterator
1180 i = elapsed_timers.begin();
1181 i != elapsed_timers.end(); ++i){
1182 n = block->getNodeNoEx(i->first);
1183 p = i->first + block->getPosRelative();
1184 if(m_script->node_on_timer(p,n,i->second.elapsed))
1185 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1191 const float abm_interval = 1.0;
1192 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1194 if(m_active_block_interval_overload_skip > 0){
1195 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1196 m_active_block_interval_overload_skip--;
1199 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1200 TimeTaker timer("modify in active blocks");
1202 // Initialize handling of ActiveBlockModifiers
1203 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1205 for(std::set<v3s16>::iterator
1206 i = m_active_blocks.m_list.begin();
1207 i != m_active_blocks.m_list.end(); ++i)
1211 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1212 <<") being handled"<<std::endl;*/
1214 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1218 // Set current time as timestamp
1219 block->setTimestampNoChangedFlag(m_game_time);
1221 /* Handle ActiveBlockModifiers */
1222 abmhandler.apply(block);
1225 u32 time_ms = timer.stop(true);
1226 u32 max_time_ms = 200;
1227 if(time_ms > max_time_ms){
1228 warningstream<<"active block modifiers took "
1229 <<time_ms<<"ms (longer than "
1230 <<max_time_ms<<"ms)"<<std::endl;
1231 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1236 Step script environment (run global on_step())
1238 m_script->environment_Step(dtime);
1244 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1245 //TimeTaker timer("Step active objects");
1247 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1249 // This helps the objects to send data at the same time
1250 bool send_recommended = false;
1251 m_send_recommended_timer += dtime;
1252 if(m_send_recommended_timer > getSendRecommendedInterval())
1254 m_send_recommended_timer -= getSendRecommendedInterval();
1255 send_recommended = true;
1258 for(std::map<u16, ServerActiveObject*>::iterator
1259 i = m_active_objects.begin();
1260 i != m_active_objects.end(); ++i)
1262 ServerActiveObject* obj = i->second;
1263 // Don't step if is to be removed or stored statically
1264 if(obj->m_removed || obj->m_pending_deactivation)
1267 obj->step(dtime, send_recommended);
1268 // Read messages from object
1269 while(!obj->m_messages_out.empty())
1271 m_active_object_messages.push(
1272 obj->m_messages_out.front());
1273 obj->m_messages_out.pop();
1279 Manage active objects
1281 if(m_object_management_interval.step(dtime, 0.5))
1283 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1285 Remove objects that satisfy (m_removed && m_known_by_count==0)
1287 removeRemovedObjects();
1291 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1293 std::map<u16, ServerActiveObject*>::iterator n;
1294 n = m_active_objects.find(id);
1295 if(n == m_active_objects.end())
1300 bool isFreeServerActiveObjectId(u16 id,
1301 std::map<u16, ServerActiveObject*> &objects)
1306 return objects.find(id) == objects.end();
1309 u16 getFreeServerActiveObjectId(
1310 std::map<u16, ServerActiveObject*> &objects)
1312 //try to reuse id's as late as possible
1313 static u16 last_used_id = 0;
1314 u16 startid = last_used_id;
1318 if(isFreeServerActiveObjectId(last_used_id, objects))
1319 return last_used_id;
1321 if(last_used_id == startid)
1326 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1328 assert(object); // Pre-condition
1330 u16 id = addActiveObjectRaw(object, true, 0);
1335 Finds out what new objects have been added to
1336 inside a radius around a position
1338 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1340 std::set<u16> ¤t_objects,
1341 std::queue<u16> &added_objects)
1343 f32 radius_f = radius * BS;
1344 f32 player_radius_f = player_radius * BS;
1346 if (player_radius_f < 0)
1347 player_radius_f = 0;
1350 Go through the object list,
1351 - discard m_removed objects,
1352 - discard objects that are too far away,
1353 - discard objects that are found in current_objects.
1354 - add remaining objects to added_objects
1356 for(std::map<u16, ServerActiveObject*>::iterator
1357 i = m_active_objects.begin();
1358 i != m_active_objects.end(); ++i) {
1362 ServerActiveObject *object = i->second;
1366 // Discard if removed or deactivating
1367 if(object->m_removed || object->m_pending_deactivation)
1370 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1371 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1372 // Discard if too far
1373 if (distance_f > player_radius_f && player_radius_f != 0)
1375 } else if (distance_f > radius_f)
1378 // Discard if already on current_objects
1379 std::set<u16>::iterator n;
1380 n = current_objects.find(id);
1381 if(n != current_objects.end())
1383 // Add to added_objects
1384 added_objects.push(id);
1389 Finds out what objects have been removed from
1390 inside a radius around a position
1392 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1394 std::set<u16> ¤t_objects,
1395 std::queue<u16> &removed_objects)
1397 f32 radius_f = radius * BS;
1398 f32 player_radius_f = player_radius * BS;
1400 if (player_radius_f < 0)
1401 player_radius_f = 0;
1404 Go through current_objects; object is removed if:
1405 - object is not found in m_active_objects (this is actually an
1406 error condition; objects should be set m_removed=true and removed
1407 only after all clients have been informed about removal), or
1408 - object has m_removed=true, or
1409 - object is too far away
1411 for(std::set<u16>::iterator
1412 i = current_objects.begin();
1413 i != current_objects.end(); ++i)
1416 ServerActiveObject *object = getActiveObject(id);
1418 if (object == NULL) {
1419 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1420 << " object in current_objects is NULL" << std::endl;
1421 removed_objects.push(id);
1425 if (object->m_removed || object->m_pending_deactivation) {
1426 removed_objects.push(id);
1430 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1431 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1432 if (distance_f <= player_radius_f || player_radius_f == 0)
1434 } else if (distance_f <= radius_f)
1437 // Object is no longer visible
1438 removed_objects.push(id);
1442 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1443 v3s16 blockpos, bool static_exists, v3s16 static_block)
1445 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1449 for (std::map<u16, StaticObject>::iterator
1450 so_it = block->m_static_objects.m_active.begin();
1451 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1452 // Get the ServerActiveObject counterpart to this StaticObject
1453 std::map<u16, ServerActiveObject *>::iterator ao_it;
1454 ao_it = m_active_objects.find(so_it->first);
1455 if (ao_it == m_active_objects.end()) {
1456 // If this ever happens, there must be some kind of nasty bug.
1457 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1458 "Object from MapBlock::m_static_objects::m_active not found "
1459 "in m_active_objects";
1463 ServerActiveObject *sao = ao_it->second;
1464 sao->m_static_exists = static_exists;
1465 sao->m_static_block = static_block;
1469 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1471 if(m_active_object_messages.empty())
1472 return ActiveObjectMessage(0);
1474 ActiveObjectMessage message = m_active_object_messages.front();
1475 m_active_object_messages.pop();
1480 ************ Private methods *************
1483 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1484 bool set_changed, u32 dtime_s)
1486 assert(object); // Pre-condition
1487 if(object->getId() == 0){
1488 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1491 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1492 <<"no free ids available"<<std::endl;
1493 if(object->environmentDeletes())
1497 object->setId(new_id);
1500 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1501 <<"supplied with id "<<object->getId()<<std::endl;
1503 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1505 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1506 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1507 if(object->environmentDeletes())
1512 if (objectpos_over_limit(object->getBasePosition())) {
1513 v3f p = object->getBasePosition();
1514 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1515 << "object position (" << p.X << "," << p.Y << "," << p.Z
1516 << ") outside maximum range" << std::endl;
1517 if (object->environmentDeletes())
1522 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1523 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1525 m_active_objects[object->getId()] = object;
1527 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1528 <<"Added id="<<object->getId()<<"; there are now "
1529 <<m_active_objects.size()<<" active objects."
1532 // Register reference in scripting api (must be done before post-init)
1533 m_script->addObjectReference(object);
1534 // Post-initialize object
1535 object->addedToEnvironment(dtime_s);
1537 // Add static data to block
1538 if(object->isStaticAllowed())
1540 // Add static object to active static list of the block
1541 v3f objectpos = object->getBasePosition();
1542 std::string staticdata = object->getStaticData();
1543 StaticObject s_obj(object->getType(), objectpos, staticdata);
1544 // Add to the block where the object is located in
1545 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1546 MapBlock *block = m_map->emergeBlock(blockpos);
1548 block->m_static_objects.m_active[object->getId()] = s_obj;
1549 object->m_static_exists = true;
1550 object->m_static_block = blockpos;
1553 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1554 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1556 v3s16 p = floatToInt(objectpos, BS);
1557 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1558 <<"could not emerge block for storing id="<<object->getId()
1559 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1563 return object->getId();
1567 Remove objects that satisfy (m_removed && m_known_by_count==0)
1569 void ServerEnvironment::removeRemovedObjects()
1571 std::vector<u16> objects_to_remove;
1572 for(std::map<u16, ServerActiveObject*>::iterator
1573 i = m_active_objects.begin();
1574 i != m_active_objects.end(); ++i) {
1576 ServerActiveObject* obj = i->second;
1577 // This shouldn't happen but check it
1580 infostream<<"NULL object found in ServerEnvironment"
1581 <<" while finding removed objects. id="<<id<<std::endl;
1582 // Id to be removed from m_active_objects
1583 objects_to_remove.push_back(id);
1588 We will delete objects that are marked as removed or thatare
1589 waiting for deletion after deactivation
1591 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1595 Delete static data from block if is marked as removed
1597 if(obj->m_static_exists && obj->m_removed)
1599 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1601 block->m_static_objects.remove(id);
1602 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1603 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1604 obj->m_static_exists = false;
1606 infostream<<"Failed to emerge block from which an object to "
1607 <<"be removed was loaded from. id="<<id<<std::endl;
1611 // If m_known_by_count > 0, don't actually remove. On some future
1612 // invocation this will be 0, which is when removal will continue.
1613 if(obj->m_known_by_count > 0)
1617 Move static data from active to stored if not marked as removed
1619 if(obj->m_static_exists && !obj->m_removed){
1620 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1622 std::map<u16, StaticObject>::iterator i =
1623 block->m_static_objects.m_active.find(id);
1624 if(i != block->m_static_objects.m_active.end()){
1625 block->m_static_objects.m_stored.push_back(i->second);
1626 block->m_static_objects.m_active.erase(id);
1627 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1628 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1631 infostream<<"Failed to emerge block from which an object to "
1632 <<"be deactivated was loaded from. id="<<id<<std::endl;
1636 // Tell the object about removal
1637 obj->removingFromEnvironment();
1638 // Deregister in scripting api
1639 m_script->removeObjectReference(obj);
1642 if(obj->environmentDeletes())
1645 // Id to be removed from m_active_objects
1646 objects_to_remove.push_back(id);
1648 // Remove references from m_active_objects
1649 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1650 i != objects_to_remove.end(); ++i) {
1651 m_active_objects.erase(*i);
1655 static void print_hexdump(std::ostream &o, const std::string &data)
1657 const int linelength = 16;
1658 for(int l=0; ; l++){
1659 int i0 = linelength * l;
1660 bool at_end = false;
1661 int thislinelength = linelength;
1662 if(i0 + thislinelength > (int)data.size()){
1663 thislinelength = data.size() - i0;
1666 for(int di=0; di<linelength; di++){
1669 if(di<thislinelength)
1670 snprintf(buf, 4, "%.2x ", data[i]);
1672 snprintf(buf, 4, " ");
1676 for(int di=0; di<thislinelength; di++){
1690 Convert stored objects from blocks near the players to active.
1692 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1697 // Ignore if no stored objects (to not set changed flag)
1698 if(block->m_static_objects.m_stored.empty())
1701 verbosestream<<"ServerEnvironment::activateObjects(): "
1702 <<"activating objects of block "<<PP(block->getPos())
1703 <<" ("<<block->m_static_objects.m_stored.size()
1704 <<" objects)"<<std::endl;
1705 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1707 errorstream<<"suspiciously large amount of objects detected: "
1708 <<block->m_static_objects.m_stored.size()<<" in "
1709 <<PP(block->getPos())
1710 <<"; removing all of them."<<std::endl;
1711 // Clear stored list
1712 block->m_static_objects.m_stored.clear();
1713 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1714 MOD_REASON_TOO_MANY_OBJECTS);
1718 // Activate stored objects
1719 std::vector<StaticObject> new_stored;
1720 for (std::vector<StaticObject>::iterator
1721 i = block->m_static_objects.m_stored.begin();
1722 i != block->m_static_objects.m_stored.end(); ++i) {
1723 StaticObject &s_obj = *i;
1725 // Create an active object from the data
1726 ServerActiveObject *obj = ServerActiveObject::create
1727 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1728 // If couldn't create object, store static data back.
1730 errorstream<<"ServerEnvironment::activateObjects(): "
1731 <<"failed to create active object from static object "
1732 <<"in block "<<PP(s_obj.pos/BS)
1733 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1734 print_hexdump(verbosestream, s_obj.data);
1736 new_stored.push_back(s_obj);
1739 verbosestream<<"ServerEnvironment::activateObjects(): "
1740 <<"activated static object pos="<<PP(s_obj.pos/BS)
1741 <<" type="<<(int)s_obj.type<<std::endl;
1742 // This will also add the object to the active static list
1743 addActiveObjectRaw(obj, false, dtime_s);
1745 // Clear stored list
1746 block->m_static_objects.m_stored.clear();
1747 // Add leftover failed stuff to stored list
1748 for(std::vector<StaticObject>::iterator
1749 i = new_stored.begin();
1750 i != new_stored.end(); ++i) {
1751 StaticObject &s_obj = *i;
1752 block->m_static_objects.m_stored.push_back(s_obj);
1755 // Turn the active counterparts of activated objects not pending for
1757 for(std::map<u16, StaticObject>::iterator
1758 i = block->m_static_objects.m_active.begin();
1759 i != block->m_static_objects.m_active.end(); ++i)
1762 ServerActiveObject *object = getActiveObject(id);
1764 object->m_pending_deactivation = false;
1768 Note: Block hasn't really been modified here.
1769 The objects have just been activated and moved from the stored
1770 static list to the active static list.
1771 As such, the block is essentially the same.
1772 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1773 Otherwise there would be a huge amount of unnecessary I/O.
1778 Convert objects that are not standing inside active blocks to static.
1780 If m_known_by_count != 0, active object is not deleted, but static
1781 data is still updated.
1783 If force_delete is set, active object is deleted nevertheless. It
1784 shall only be set so in the destructor of the environment.
1786 If block wasn't generated (not in memory or on disk),
1788 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1790 std::vector<u16> objects_to_remove;
1791 for(std::map<u16, ServerActiveObject*>::iterator
1792 i = m_active_objects.begin();
1793 i != m_active_objects.end(); ++i) {
1794 ServerActiveObject* obj = i->second;
1797 // Do not deactivate if static data creation not allowed
1798 if(!force_delete && !obj->isStaticAllowed())
1801 // If pending deactivation, let removeRemovedObjects() do it
1802 if(!force_delete && obj->m_pending_deactivation)
1806 v3f objectpos = obj->getBasePosition();
1808 // The block in which the object resides in
1809 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1811 // If object's static data is stored in a deactivated block and object
1812 // is actually located in an active block, re-save to the block in
1813 // which the object is actually located in.
1815 obj->m_static_exists &&
1816 !m_active_blocks.contains(obj->m_static_block) &&
1817 m_active_blocks.contains(blockpos_o))
1819 v3s16 old_static_block = obj->m_static_block;
1821 // Save to block where object is located
1822 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1824 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1825 <<"Could not save object id="<<id
1826 <<" to it's current block "<<PP(blockpos_o)
1830 std::string staticdata_new = obj->getStaticData();
1831 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1832 block->m_static_objects.insert(id, s_obj);
1833 obj->m_static_block = blockpos_o;
1834 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1835 MOD_REASON_STATIC_DATA_ADDED);
1837 // Delete from block where object was located
1838 block = m_map->emergeBlock(old_static_block, false);
1840 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1841 <<"Could not delete object id="<<id
1842 <<" from it's previous block "<<PP(old_static_block)
1846 block->m_static_objects.remove(id);
1847 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1848 MOD_REASON_STATIC_DATA_REMOVED);
1852 // If block is active, don't remove
1853 if(!force_delete && m_active_blocks.contains(blockpos_o))
1856 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1857 <<"deactivating object id="<<id<<" on inactive block "
1858 <<PP(blockpos_o)<<std::endl;
1860 // If known by some client, don't immediately delete.
1861 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1864 Update the static data
1867 if(obj->isStaticAllowed())
1869 // Create new static object
1870 std::string staticdata_new = obj->getStaticData();
1871 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1873 bool stays_in_same_block = false;
1874 bool data_changed = true;
1876 if (obj->m_static_exists) {
1877 if (obj->m_static_block == blockpos_o)
1878 stays_in_same_block = true;
1880 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1883 std::map<u16, StaticObject>::iterator n =
1884 block->m_static_objects.m_active.find(id);
1885 if (n != block->m_static_objects.m_active.end()) {
1886 StaticObject static_old = n->second;
1888 float save_movem = obj->getMinimumSavedMovement();
1890 if (static_old.data == staticdata_new &&
1891 (static_old.pos - objectpos).getLength() < save_movem)
1892 data_changed = false;
1894 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1895 <<"id="<<id<<" m_static_exists=true but "
1896 <<"static data doesn't actually exist in "
1897 <<PP(obj->m_static_block)<<std::endl;
1902 bool shall_be_written = (!stays_in_same_block || data_changed);
1904 // Delete old static object
1905 if(obj->m_static_exists)
1907 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1910 block->m_static_objects.remove(id);
1911 obj->m_static_exists = false;
1912 // Only mark block as modified if data changed considerably
1913 if(shall_be_written)
1914 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1915 MOD_REASON_STATIC_DATA_CHANGED);
1919 // Add to the block where the object is located in
1920 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1921 // Get or generate the block
1922 MapBlock *block = NULL;
1924 block = m_map->emergeBlock(blockpos);
1925 } catch(InvalidPositionException &e){
1926 // Handled via NULL pointer
1927 // NOTE: emergeBlock's failure is usually determined by it
1928 // actually returning NULL
1933 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1934 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1935 <<" statically but block "<<PP(blockpos)
1936 <<" already contains "
1937 <<block->m_static_objects.m_stored.size()
1939 <<" Forcing delete."<<std::endl;
1940 force_delete = true;
1942 // If static counterpart already exists in target block,
1944 // This shouldn't happen because the object is removed from
1945 // the previous block before this according to
1946 // obj->m_static_block, but happens rarely for some unknown
1947 // reason. Unsuccessful attempts have been made to find
1949 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1950 warningstream<<"ServerEnv: Performing hack #83274"
1952 block->m_static_objects.remove(id);
1954 // Store static data
1955 u16 store_id = pending_delete ? id : 0;
1956 block->m_static_objects.insert(store_id, s_obj);
1958 // Only mark block as modified if data changed considerably
1959 if(shall_be_written)
1960 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1961 MOD_REASON_STATIC_DATA_CHANGED);
1963 obj->m_static_exists = true;
1964 obj->m_static_block = block->getPos();
1969 v3s16 p = floatToInt(objectpos, BS);
1970 errorstream<<"ServerEnv: Could not find or generate "
1971 <<"a block for storing id="<<obj->getId()
1972 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1979 If known by some client, set pending deactivation.
1980 Otherwise delete it immediately.
1983 if(pending_delete && !force_delete)
1985 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1986 <<"object id="<<id<<" is known by clients"
1987 <<"; not deleting yet"<<std::endl;
1989 obj->m_pending_deactivation = true;
1993 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1994 <<"object id="<<id<<" is not known by clients"
1995 <<"; deleting"<<std::endl;
1997 // Tell the object about removal
1998 obj->removingFromEnvironment();
1999 // Deregister in scripting api
2000 m_script->removeObjectReference(obj);
2002 // Delete active object
2003 if(obj->environmentDeletes())
2005 // Id to be removed from m_active_objects
2006 objects_to_remove.push_back(id);
2009 // Remove references from m_active_objects
2010 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2011 i != objects_to_remove.end(); ++i) {
2012 m_active_objects.erase(*i);
2018 #include "clientsimpleobject.h"
2024 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2025 ITextureSource *texturesource, IGameDef *gamedef,
2026 IrrlichtDevice *irr):
2029 m_texturesource(texturesource),
2034 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2037 ClientEnvironment::~ClientEnvironment()
2039 // delete active objects
2040 for(std::map<u16, ClientActiveObject*>::iterator
2041 i = m_active_objects.begin();
2042 i != m_active_objects.end(); ++i)
2047 for(std::vector<ClientSimpleObject*>::iterator
2048 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2056 Map & ClientEnvironment::getMap()
2061 ClientMap & ClientEnvironment::getClientMap()
2066 void ClientEnvironment::addPlayer(Player *player)
2068 DSTACK(FUNCTION_NAME);
2070 It is a failure if player is local and there already is a local
2073 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2074 "Player is local but there is already a local player");
2076 Environment::addPlayer(player);
2079 LocalPlayer * ClientEnvironment::getLocalPlayer()
2081 for(std::vector<Player*>::iterator i = m_players.begin();
2082 i != m_players.end(); ++i) {
2083 Player *player = *i;
2084 if(player->isLocal())
2085 return (LocalPlayer*)player;
2090 void ClientEnvironment::step(float dtime)
2092 DSTACK(FUNCTION_NAME);
2094 /* Step time of day */
2095 stepTimeOfDay(dtime);
2097 // Get some settings
2098 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2099 bool free_move = fly_allowed && g_settings->getBool("free_move");
2102 LocalPlayer *lplayer = getLocalPlayer();
2104 // collision info queue
2105 std::vector<CollisionInfo> player_collisions;
2108 Get the speed the player is going
2110 bool is_climbing = lplayer->is_climbing;
2112 f32 player_speed = lplayer->getSpeed().getLength();
2115 Maximum position increment
2117 //f32 position_max_increment = 0.05*BS;
2118 f32 position_max_increment = 0.1*BS;
2120 // Maximum time increment (for collision detection etc)
2121 // time = distance / speed
2122 f32 dtime_max_increment = 1;
2123 if(player_speed > 0.001)
2124 dtime_max_increment = position_max_increment / player_speed;
2126 // Maximum time increment is 10ms or lower
2127 if(dtime_max_increment > 0.01)
2128 dtime_max_increment = 0.01;
2130 // Don't allow overly huge dtime
2134 f32 dtime_downcount = dtime;
2137 Stuff that has a maximum time increment
2146 if(dtime_downcount > dtime_max_increment)
2148 dtime_part = dtime_max_increment;
2149 dtime_downcount -= dtime_part;
2153 dtime_part = dtime_downcount;
2155 Setting this to 0 (no -=dtime_part) disables an infinite loop
2156 when dtime_part is so small that dtime_downcount -= dtime_part
2159 dtime_downcount = 0;
2168 if(free_move == false && is_climbing == false)
2171 v3f speed = lplayer->getSpeed();
2172 if(lplayer->in_liquid == false)
2173 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2175 // Liquid floating / sinking
2176 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2177 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2179 // Liquid resistance
2180 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2182 // How much the node's viscosity blocks movement, ranges between 0 and 1
2183 // Should match the scale at which viscosity increase affects other liquid attributes
2184 const f32 viscosity_factor = 0.3;
2186 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2187 f32 dl = d_wanted.getLength();
2188 if(dl > lplayer->movement_liquid_fluidity_smooth)
2189 dl = lplayer->movement_liquid_fluidity_smooth;
2190 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2192 v3f d = d_wanted.normalize() * dl;
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(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(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();
2638 #endif // #ifndef SERVER