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.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
45 #include "jthread/jmutexautolock.h"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
54 m_enable_day_night_ratio_override(false),
55 m_day_night_ratio_override(0.0f)
59 Environment::~Environment()
62 for(std::list<Player*>::iterator i = m_players.begin();
63 i != m_players.end(); ++i)
69 void Environment::addPlayer(Player *player)
71 DSTACK(__FUNCTION_NAME);
73 Check that peer_ids are unique.
74 Also check that names are unique.
75 Exception: there can be multiple players with peer_id=0
77 // If peer id is non-zero, it has to be unique.
78 if(player->peer_id != 0)
79 assert(getPlayer(player->peer_id) == NULL);
80 // Name has to be unique.
81 assert(getPlayer(player->getName()) == NULL);
83 m_players.push_back(player);
86 void Environment::removePlayer(u16 peer_id)
88 DSTACK(__FUNCTION_NAME);
90 for(std::list<Player*>::iterator i = m_players.begin();
91 i != m_players.end();)
94 if(player->peer_id == peer_id) {
96 i = m_players.erase(i);
103 Player * Environment::getPlayer(u16 peer_id)
105 for(std::list<Player*>::iterator i = m_players.begin();
106 i != m_players.end(); ++i)
109 if(player->peer_id == peer_id)
115 Player * Environment::getPlayer(const char *name)
117 for(std::list<Player*>::iterator i = m_players.begin();
118 i != m_players.end(); ++i)
121 if(strcmp(player->getName(), name) == 0)
127 Player * Environment::getRandomConnectedPlayer()
129 std::list<Player*> connected_players = getPlayers(true);
130 u32 chosen_one = myrand() % connected_players.size();
132 for(std::list<Player*>::iterator
133 i = connected_players.begin();
134 i != connected_players.end(); ++i)
146 Player * Environment::getNearestConnectedPlayer(v3f pos)
148 std::list<Player*> connected_players = getPlayers(true);
150 Player *nearest_player = NULL;
151 for(std::list<Player*>::iterator
152 i = connected_players.begin();
153 i != connected_players.end(); ++i)
156 f32 d = player->getPosition().getDistanceFrom(pos);
157 if(d < nearest_d || nearest_player == NULL)
160 nearest_player = player;
163 return nearest_player;
166 std::list<Player*> Environment::getPlayers()
171 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
173 std::list<Player*> newlist;
174 for(std::list<Player*>::iterator
175 i = m_players.begin();
176 i != m_players.end(); ++i)
180 if(ignore_disconnected)
182 // Ignore disconnected players
183 if(player->peer_id == 0)
187 newlist.push_back(player);
192 u32 Environment::getDayNightRatio()
194 if(m_enable_day_night_ratio_override)
195 return m_day_night_ratio_override;
196 bool smooth = g_settings->getBool("enable_shaders");
197 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
200 void Environment::setTimeOfDaySpeed(float speed)
202 JMutexAutoLock(this->m_lock);
203 m_time_of_day_speed = speed;
206 float Environment::getTimeOfDaySpeed()
208 JMutexAutoLock(this->m_lock);
209 float retval = m_time_of_day_speed;
213 void Environment::stepTimeOfDay(float dtime)
217 JMutexAutoLock(this->m_lock);
218 day_speed = m_time_of_day_speed;
221 m_time_counter += dtime;
222 f32 speed = day_speed * 24000./(24.*3600);
223 u32 units = (u32)(m_time_counter*speed);
227 if(m_time_of_day + units >= 24000)
229 m_time_of_day = (m_time_of_day + units) % 24000;
231 m_time_of_day_f = (float)m_time_of_day / 24000.0;
234 m_time_counter -= (f32)units / speed;
237 m_time_of_day_f += day_speed/24/3600*dtime;
238 if(m_time_of_day_f > 1.0)
239 m_time_of_day_f -= 1.0;
240 if(m_time_of_day_f < 0.0)
241 m_time_of_day_f += 1.0;
249 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
253 // Initialize timer to random value to spread processing
254 float itv = abm->getTriggerInterval();
255 itv = MYMAX(0.001, itv); // No less than 1ms
256 int minval = MYMAX(-0.51*itv, -60); // Clamp to
257 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
258 timer = myrand_range(minval, maxval);
265 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
268 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
269 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
270 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
277 void ActiveBlockList::update(std::list<v3s16> &active_positions,
279 std::set<v3s16> &blocks_removed,
280 std::set<v3s16> &blocks_added)
285 std::set<v3s16> newlist = m_forceloaded_list;
286 for(std::list<v3s16>::iterator i = active_positions.begin();
287 i != active_positions.end(); ++i)
289 fillRadiusBlock(*i, radius, newlist);
293 Find out which blocks on the old list are not on the new list
295 // Go through old list
296 for(std::set<v3s16>::iterator i = m_list.begin();
297 i != m_list.end(); ++i)
300 // If not on new list, it's been removed
301 if(newlist.find(p) == newlist.end())
302 blocks_removed.insert(p);
306 Find out which blocks on the new list are not on the old list
308 // Go through new list
309 for(std::set<v3s16>::iterator i = newlist.begin();
310 i != newlist.end(); ++i)
313 // If not on old list, it's been added
314 if(m_list.find(p) == m_list.end())
315 blocks_added.insert(p);
322 for(std::set<v3s16>::iterator i = newlist.begin();
323 i != newlist.end(); ++i)
334 ServerEnvironment::ServerEnvironment(ServerMap *map,
335 GameScripting *scriptIface, IGameDef *gamedef):
337 m_script(scriptIface),
339 m_send_recommended_timer(0),
340 m_active_block_interval_overload_skip(0),
342 m_game_time_fraction_counter(0),
343 m_recommended_send_interval(0.1),
344 m_max_lag_estimate(0.1)
348 ServerEnvironment::~ServerEnvironment()
350 // Clear active block list.
351 // This makes the next one delete all active objects.
352 m_active_blocks.clear();
354 // Convert all objects to static and delete the active objects
355 deactivateFarObjects(true);
360 // Delete ActiveBlockModifiers
361 for(std::list<ABMWithState>::iterator
362 i = m_abms.begin(); i != m_abms.end(); ++i){
367 Map & ServerEnvironment::getMap()
372 ServerMap & ServerEnvironment::getServerMap()
377 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
379 float distance = pos1.getDistanceFrom(pos2);
381 //calculate normalized direction vector
382 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
383 (pos2.Y - pos1.Y)/distance,
384 (pos2.Z - pos1.Z)/distance);
386 //find out if there's a node on path between pos1 and pos2
387 for (float i = 1; i < distance; i += stepsize) {
388 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
389 normalized_vector.Y * i,
390 normalized_vector.Z * i) +pos1,BS);
392 MapNode n = getMap().getNodeNoEx(pos);
394 if(n.param0 != CONTENT_AIR) {
404 void ServerEnvironment::serializePlayers(const std::string &savedir)
406 std::string players_path = savedir + "/players";
407 fs::CreateDir(players_path);
409 std::set<Player*> saved_players;
411 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
412 for(u32 i=0; i<player_files.size(); i++)
414 if(player_files[i].dir || player_files[i].name[0] == '.')
417 // Full path to this file
418 std::string path = players_path + "/" + player_files[i].name;
420 //infostream<<"Checking player file "<<path<<std::endl;
422 // Load player to see what is its name
423 RemotePlayer testplayer(m_gamedef);
425 // Open file and deserialize
426 std::ifstream is(path.c_str(), std::ios_base::binary);
427 if(is.good() == false)
429 infostream<<"Failed to read "<<path<<std::endl;
432 testplayer.deSerialize(is, player_files[i].name);
435 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
437 // Search for the player
438 std::string playername = testplayer.getName();
439 Player *player = getPlayer(playername.c_str());
442 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
446 //infostream<<"Found matching player, overwriting."<<std::endl;
448 // OK, found. Save player there.
449 if(player->checkModified())
451 // Open file and serialize
452 std::ostringstream ss(std::ios_base::binary);
453 player->serialize(ss);
454 if(!fs::safeWriteToFile(path, ss.str()))
456 infostream<<"Failed to write "<<path<<std::endl;
459 saved_players.insert(player);
461 saved_players.insert(player);
465 for(std::list<Player*>::iterator i = m_players.begin();
466 i != m_players.end(); ++i)
469 if(saved_players.find(player) != saved_players.end())
471 /*infostream<<"Player "<<player->getName()
472 <<" was already saved."<<std::endl;*/
475 std::string playername = player->getName();
476 // Don't save unnamed player
479 //infostream<<"Not saving unnamed player."<<std::endl;
485 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
486 playername = "player";
487 std::string path = players_path + "/" + playername;
489 for(u32 i=0; i<1000; i++)
491 if(fs::PathExists(path) == false)
496 path = players_path + "/" + playername + itos(i);
500 infostream<<"Didn't find free file for player"<<std::endl;
505 /*infostream<<"Saving player "<<player->getName()<<" to "
507 // Open file and serialize
508 std::ostringstream ss(std::ios_base::binary);
509 player->serialize(ss);
510 if(!fs::safeWriteToFile(path, ss.str()))
512 infostream<<"Failed to write "<<path<<std::endl;
515 saved_players.insert(player);
519 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
522 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
524 std::string players_path = savedir + "/players";
526 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
527 for(u32 i=0; i<player_files.size(); i++)
529 if(player_files[i].dir)
532 // Full path to this file
533 std::string path = players_path + "/" + player_files[i].name;
535 //infostream<<"Checking player file "<<path<<std::endl;
537 // Load player to see what is its name
538 RemotePlayer testplayer(m_gamedef);
540 // Open file and deserialize
541 std::ifstream is(path.c_str(), std::ios_base::binary);
542 if(is.good() == false)
544 infostream<<"Failed to read "<<path<<std::endl;
547 testplayer.deSerialize(is, player_files[i].name);
550 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
552 infostream<<"Not loading player with invalid name: "
553 <<testplayer.getName()<<std::endl;
556 /*infostream<<"Loaded test player with name "<<testplayer.getName()
559 // Search for the player
560 std::string playername = testplayer.getName();
561 Player *player = getPlayer(playername.c_str());
562 bool newplayer = false;
565 //infostream<<"Is a new player"<<std::endl;
566 player = new RemotePlayer(m_gamedef);
572 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
574 // Open file and deserialize
575 std::ifstream is(path.c_str(), std::ios_base::binary);
576 if(is.good() == false)
578 infostream<<"Failed to read "<<path<<std::endl;
581 player->deSerialize(is, player_files[i].name);
591 void ServerEnvironment::saveMeta(const std::string &savedir)
593 std::string path = savedir + "/env_meta.txt";
595 // Open file and serialize
596 std::ostringstream ss(std::ios_base::binary);
599 args.setU64("game_time", m_game_time);
600 args.setU64("time_of_day", getTimeOfDay());
604 if(!fs::safeWriteToFile(path, ss.str()))
606 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
608 throw SerializationError("Couldn't save env meta");
612 void ServerEnvironment::loadMeta(const std::string &savedir)
614 std::string path = savedir + "/env_meta.txt";
616 // Open file and deserialize
617 std::ifstream is(path.c_str(), std::ios_base::binary);
618 if(is.good() == false)
620 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
622 throw SerializationError("Couldn't load env meta");
630 throw SerializationError
631 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
633 std::getline(is, line);
634 std::string trimmedline = trim(line);
635 if(trimmedline == "EnvArgsEnd")
637 args.parseConfigLine(line);
641 m_game_time = args.getU64("game_time");
642 }catch(SettingNotFoundException &e){
643 // Getting this is crucial, otherwise timestamps are useless
644 throw SerializationError("Couldn't load env meta game_time");
648 m_time_of_day = args.getU64("time_of_day");
649 }catch(SettingNotFoundException &e){
650 // This is not as important
651 m_time_of_day = 9000;
657 ActiveBlockModifier *abm;
659 std::set<content_t> required_neighbors;
665 ServerEnvironment *m_env;
666 std::map<content_t, std::list<ActiveABM> > m_aabms;
668 ABMHandler(std::list<ABMWithState> &abms,
669 float dtime_s, ServerEnvironment *env,
675 INodeDefManager *ndef = env->getGameDef()->ndef();
676 for(std::list<ABMWithState>::iterator
677 i = abms.begin(); i != abms.end(); ++i){
678 ActiveBlockModifier *abm = i->abm;
679 float trigger_interval = abm->getTriggerInterval();
680 if(trigger_interval < 0.001)
681 trigger_interval = 0.001;
682 float actual_interval = dtime_s;
685 if(i->timer < trigger_interval)
687 i->timer -= trigger_interval;
688 actual_interval = trigger_interval;
690 float intervals = actual_interval / trigger_interval;
693 float chance = abm->getTriggerChance();
698 aabm.chance = chance / intervals;
702 std::set<std::string> required_neighbors_s
703 = abm->getRequiredNeighbors();
704 for(std::set<std::string>::iterator
705 i = required_neighbors_s.begin();
706 i != required_neighbors_s.end(); i++)
708 ndef->getIds(*i, aabm.required_neighbors);
711 std::set<std::string> contents_s = abm->getTriggerContents();
712 for(std::set<std::string>::iterator
713 i = contents_s.begin(); i != contents_s.end(); i++)
715 std::set<content_t> ids;
716 ndef->getIds(*i, ids);
717 for(std::set<content_t>::const_iterator k = ids.begin();
721 std::map<content_t, std::list<ActiveABM> >::iterator j;
723 if(j == m_aabms.end()){
724 std::list<ActiveABM> aabmlist;
725 m_aabms[c] = aabmlist;
728 j->second.push_back(aabm);
733 // Find out how many objects the given block and its neighbours contain.
734 // Returns the number of objects in the block, and also in 'wider' the
735 // number of objects in the block and all its neighbours. The latter
736 // may an estimate if any neighbours are unloaded.
737 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
740 u32 wider_unknown_count = 0;
741 for(s16 x=-1; x<=1; x++)
742 for(s16 y=-1; y<=1; y++)
743 for(s16 z=-1; z<=1; z++)
745 MapBlock *block2 = map->getBlockNoCreateNoEx(
746 block->getPos() + v3s16(x,y,z));
748 wider_unknown_count++;
751 wider += block2->m_static_objects.m_active.size()
752 + block2->m_static_objects.m_stored.size();
755 u32 active_object_count = block->m_static_objects.m_active.size();
756 u32 wider_known_count = 3*3*3 - wider_unknown_count;
757 wider += wider_unknown_count * wider / wider_known_count;
758 return active_object_count;
761 void apply(MapBlock *block)
766 ServerMap *map = &m_env->getServerMap();
768 u32 active_object_count_wider;
769 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
770 m_env->m_added_objects = 0;
773 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
774 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
775 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
777 MapNode n = block->getNodeNoEx(p0);
778 content_t c = n.getContent();
779 v3s16 p = p0 + block->getPosRelative();
781 std::map<content_t, std::list<ActiveABM> >::iterator j;
783 if(j == m_aabms.end())
786 for(std::list<ActiveABM>::iterator
787 i = j->second.begin(); i != j->second.end(); i++)
789 if(myrand() % i->chance != 0)
793 if(!i->required_neighbors.empty())
796 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
797 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
798 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
802 MapNode n = map->getNodeNoEx(p1);
803 content_t c = n.getContent();
804 std::set<content_t>::const_iterator k;
805 k = i->required_neighbors.find(c);
806 if(k != i->required_neighbors.end()){
810 // No required neighbor found
815 // Call all the trigger variations
816 i->abm->trigger(m_env, p, n);
817 i->abm->trigger(m_env, p, n,
818 active_object_count, active_object_count_wider);
820 // Count surrounding objects again if the abms added any
821 if(m_env->m_added_objects > 0) {
822 active_object_count = countObjects(block, map, active_object_count_wider);
823 m_env->m_added_objects = 0;
830 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
832 // Reset usage timer immediately, otherwise a block that becomes active
833 // again at around the same time as it would normally be unloaded will
834 // get unloaded incorrectly. (I think this still leaves a small possibility
835 // of a race condition between this and server::AsyncRunStep, which only
836 // some kind of synchronisation will fix, but it at least reduces the window
837 // of opportunity for it to break from seconds to nanoseconds)
838 block->resetUsageTimer();
840 // Get time difference
842 u32 stamp = block->getTimestamp();
843 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
844 dtime_s = m_game_time - block->getTimestamp();
845 dtime_s += additional_dtime;
847 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
848 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
850 // Set current time as timestamp
851 block->setTimestampNoChangedFlag(m_game_time);
853 /*infostream<<"ServerEnvironment::activateBlock(): block is "
854 <<dtime_s<<" seconds old."<<std::endl;*/
856 // Activate stored objects
857 activateObjects(block, dtime_s);
860 std::map<v3s16, NodeTimer> elapsed_timers =
861 block->m_node_timers.step((float)dtime_s);
862 if(!elapsed_timers.empty()){
864 for(std::map<v3s16, NodeTimer>::iterator
865 i = elapsed_timers.begin();
866 i != elapsed_timers.end(); i++){
867 n = block->getNodeNoEx(i->first);
868 v3s16 p = i->first + block->getPosRelative();
869 if(m_script->node_on_timer(p,n,i->second.elapsed))
870 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
874 /* Handle ActiveBlockModifiers */
875 ABMHandler abmhandler(m_abms, dtime_s, this, false);
876 abmhandler.apply(block);
879 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
881 m_abms.push_back(ABMWithState(abm));
884 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
886 INodeDefManager *ndef = m_gamedef->ndef();
887 MapNode n_old = m_map->getNodeNoEx(p);
889 if(ndef->get(n_old).has_on_destruct)
890 m_script->node_on_destruct(p, n_old);
892 bool succeeded = m_map->addNodeWithEvent(p, n);
895 // Call post-destructor
896 if(ndef->get(n_old).has_after_destruct)
897 m_script->node_after_destruct(p, n_old);
899 if(ndef->get(n).has_on_construct)
900 m_script->node_on_construct(p, n);
904 bool ServerEnvironment::removeNode(v3s16 p)
906 INodeDefManager *ndef = m_gamedef->ndef();
907 MapNode n_old = m_map->getNodeNoEx(p);
909 if(ndef->get(n_old).has_on_destruct)
910 m_script->node_on_destruct(p, n_old);
912 // This is slightly optimized compared to addNodeWithEvent(air)
913 bool succeeded = m_map->removeNodeWithEvent(p);
916 // Call post-destructor
917 if(ndef->get(n_old).has_after_destruct)
918 m_script->node_after_destruct(p, n_old);
919 // Air doesn't require constructor
923 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
925 return m_map->addNodeWithEvent(p, n, false);
928 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
930 std::set<u16> objects;
931 for(std::map<u16, ServerActiveObject*>::iterator
932 i = m_active_objects.begin();
933 i != m_active_objects.end(); ++i)
935 ServerActiveObject* obj = i->second;
937 v3f objectpos = obj->getBasePosition();
938 if(objectpos.getDistanceFrom(pos) > radius)
945 void ServerEnvironment::clearAllObjects()
947 infostream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Removing all active objects"<<std::endl;
949 std::list<u16> objects_to_remove;
950 for(std::map<u16, ServerActiveObject*>::iterator
951 i = m_active_objects.begin();
952 i != m_active_objects.end(); ++i)
954 ServerActiveObject* obj = i->second;
955 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
958 // Delete static object if block is loaded
959 if(obj->m_static_exists){
960 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
962 block->m_static_objects.remove(id);
963 block->raiseModified(MOD_STATE_WRITE_NEEDED,
965 obj->m_static_exists = false;
968 // If known by some client, don't delete immediately
969 if(obj->m_known_by_count > 0){
970 obj->m_pending_deactivation = true;
971 obj->m_removed = true;
975 // Tell the object about removal
976 obj->removingFromEnvironment();
977 // Deregister in scripting api
978 m_script->removeObjectReference(obj);
980 // Delete active object
981 if(obj->environmentDeletes())
983 // Id to be removed from m_active_objects
984 objects_to_remove.push_back(id);
986 // Remove references from m_active_objects
987 for(std::list<u16>::iterator i = objects_to_remove.begin();
988 i != objects_to_remove.end(); ++i)
990 m_active_objects.erase(*i);
993 // Get list of loaded blocks
994 std::list<v3s16> loaded_blocks;
995 infostream<<"ServerEnvironment::clearAllObjects(): "
996 <<"Listing all loaded blocks"<<std::endl;
997 m_map->listAllLoadedBlocks(loaded_blocks);
998 infostream<<"ServerEnvironment::clearAllObjects(): "
999 <<"Done listing all loaded blocks: "
1000 <<loaded_blocks.size()<<std::endl;
1002 // Get list of loadable blocks
1003 std::list<v3s16> loadable_blocks;
1004 infostream<<"ServerEnvironment::clearAllObjects(): "
1005 <<"Listing all loadable blocks"<<std::endl;
1006 m_map->listAllLoadableBlocks(loadable_blocks);
1007 infostream<<"ServerEnvironment::clearAllObjects(): "
1008 <<"Done listing all loadable blocks: "
1009 <<loadable_blocks.size()
1010 <<", now clearing"<<std::endl;
1012 // Grab a reference on each loaded block to avoid unloading it
1013 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1014 i != loaded_blocks.end(); ++i)
1017 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1022 // Remove objects in all loadable blocks
1023 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1024 unload_interval = MYMAX(unload_interval, 1);
1025 u32 report_interval = loadable_blocks.size() / 10;
1026 u32 num_blocks_checked = 0;
1027 u32 num_blocks_cleared = 0;
1028 u32 num_objs_cleared = 0;
1029 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
1030 i != loadable_blocks.end(); ++i)
1033 MapBlock *block = m_map->emergeBlock(p, false);
1035 errorstream<<"ServerEnvironment::clearAllObjects(): "
1036 <<"Failed to emerge block "<<PP(p)<<std::endl;
1039 u32 num_stored = block->m_static_objects.m_stored.size();
1040 u32 num_active = block->m_static_objects.m_active.size();
1041 if(num_stored != 0 || num_active != 0){
1042 block->m_static_objects.m_stored.clear();
1043 block->m_static_objects.m_active.clear();
1044 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1046 num_objs_cleared += num_stored + num_active;
1047 num_blocks_cleared++;
1049 num_blocks_checked++;
1051 if(report_interval != 0 &&
1052 num_blocks_checked % report_interval == 0){
1053 float percent = 100.0 * (float)num_blocks_checked /
1054 loadable_blocks.size();
1055 infostream<<"ServerEnvironment::clearAllObjects(): "
1056 <<"Cleared "<<num_objs_cleared<<" objects"
1057 <<" in "<<num_blocks_cleared<<" blocks ("
1058 <<percent<<"%)"<<std::endl;
1060 if(num_blocks_checked % unload_interval == 0){
1061 m_map->unloadUnreferencedBlocks();
1064 m_map->unloadUnreferencedBlocks();
1066 // Drop references that were added above
1067 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1068 i != loaded_blocks.end(); ++i)
1071 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1076 infostream<<"ServerEnvironment::clearAllObjects(): "
1077 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1078 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1081 void ServerEnvironment::step(float dtime)
1083 DSTACK(__FUNCTION_NAME);
1085 //TimeTaker timer("ServerEnv step");
1087 /* Step time of day */
1088 stepTimeOfDay(dtime);
1091 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1092 // really matter that much.
1093 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1099 m_game_time_fraction_counter += dtime;
1100 u32 inc_i = (u32)m_game_time_fraction_counter;
1101 m_game_time += inc_i;
1102 m_game_time_fraction_counter -= (float)inc_i;
1109 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1110 for(std::list<Player*>::iterator i = m_players.begin();
1111 i != m_players.end(); ++i)
1113 Player *player = *i;
1115 // Ignore disconnected players
1116 if(player->peer_id == 0)
1120 player->move(dtime, this, 100*BS);
1125 Manage active block list
1127 if(m_active_blocks_management_interval.step(dtime, 2.0))
1129 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1131 Get player block positions
1133 std::list<v3s16> players_blockpos;
1134 for(std::list<Player*>::iterator
1135 i = m_players.begin();
1136 i != m_players.end(); ++i)
1138 Player *player = *i;
1139 // Ignore disconnected players
1140 if(player->peer_id == 0)
1142 v3s16 blockpos = getNodeBlockPos(
1143 floatToInt(player->getPosition(), BS));
1144 players_blockpos.push_back(blockpos);
1148 Update list of active blocks, collecting changes
1150 const s16 active_block_range = g_settings->getS16("active_block_range");
1151 std::set<v3s16> blocks_removed;
1152 std::set<v3s16> blocks_added;
1153 m_active_blocks.update(players_blockpos, active_block_range,
1154 blocks_removed, blocks_added);
1157 Handle removed blocks
1160 // Convert active objects that are no more in active blocks to static
1161 deactivateFarObjects(false);
1163 for(std::set<v3s16>::iterator
1164 i = blocks_removed.begin();
1165 i != blocks_removed.end(); ++i)
1169 /* infostream<<"Server: Block " << PP(p)
1170 << " became inactive"<<std::endl; */
1172 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1176 // Set current time as timestamp (and let it set ChangedFlag)
1177 block->setTimestamp(m_game_time);
1184 for(std::set<v3s16>::iterator
1185 i = blocks_added.begin();
1186 i != blocks_added.end(); ++i)
1190 MapBlock *block = m_map->getBlockOrEmerge(p);
1192 m_active_blocks.m_list.erase(p);
1196 activateBlock(block);
1197 /* infostream<<"Server: Block " << PP(p)
1198 << " became active"<<std::endl; */
1203 Mess around in active blocks
1205 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1207 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1211 for(std::set<v3s16>::iterator
1212 i = m_active_blocks.m_list.begin();
1213 i != m_active_blocks.m_list.end(); ++i)
1217 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1218 <<") being handled"<<std::endl;*/
1220 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1224 // Reset block usage timer
1225 block->resetUsageTimer();
1227 // Set current time as timestamp
1228 block->setTimestampNoChangedFlag(m_game_time);
1229 // If time has changed much from the one on disk,
1230 // set block to be saved when it is unloaded
1231 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1232 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1233 "Timestamp older than 60s (step)");
1236 std::map<v3s16, NodeTimer> elapsed_timers =
1237 block->m_node_timers.step((float)dtime);
1238 if(!elapsed_timers.empty()){
1240 for(std::map<v3s16, NodeTimer>::iterator
1241 i = elapsed_timers.begin();
1242 i != elapsed_timers.end(); i++){
1243 n = block->getNodeNoEx(i->first);
1244 p = i->first + block->getPosRelative();
1245 if(m_script->node_on_timer(p,n,i->second.elapsed))
1246 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1252 const float abm_interval = 1.0;
1253 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1255 if(m_active_block_interval_overload_skip > 0){
1256 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1257 m_active_block_interval_overload_skip--;
1260 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1261 TimeTaker timer("modify in active blocks");
1263 // Initialize handling of ActiveBlockModifiers
1264 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1266 for(std::set<v3s16>::iterator
1267 i = m_active_blocks.m_list.begin();
1268 i != m_active_blocks.m_list.end(); ++i)
1272 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1273 <<") being handled"<<std::endl;*/
1275 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1279 // Set current time as timestamp
1280 block->setTimestampNoChangedFlag(m_game_time);
1282 /* Handle ActiveBlockModifiers */
1283 abmhandler.apply(block);
1286 u32 time_ms = timer.stop(true);
1287 u32 max_time_ms = 200;
1288 if(time_ms > max_time_ms){
1289 infostream<<"WARNING: active block modifiers took "
1290 <<time_ms<<"ms (longer than "
1291 <<max_time_ms<<"ms)"<<std::endl;
1292 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1297 Step script environment (run global on_step())
1299 m_script->environment_Step(dtime);
1305 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1306 //TimeTaker timer("Step active objects");
1308 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1310 // This helps the objects to send data at the same time
1311 bool send_recommended = false;
1312 m_send_recommended_timer += dtime;
1313 if(m_send_recommended_timer > getSendRecommendedInterval())
1315 m_send_recommended_timer -= getSendRecommendedInterval();
1316 send_recommended = true;
1319 for(std::map<u16, ServerActiveObject*>::iterator
1320 i = m_active_objects.begin();
1321 i != m_active_objects.end(); ++i)
1323 ServerActiveObject* obj = i->second;
1324 // Remove non-peaceful mobs on peaceful mode
1325 if(g_settings->getBool("only_peaceful_mobs")){
1326 if(!obj->isPeaceful())
1327 obj->m_removed = true;
1329 // Don't step if is to be removed or stored statically
1330 if(obj->m_removed || obj->m_pending_deactivation)
1333 obj->step(dtime, send_recommended);
1334 // Read messages from object
1335 while(!obj->m_messages_out.empty())
1337 m_active_object_messages.push_back(
1338 obj->m_messages_out.pop_front());
1344 Manage active objects
1346 if(m_object_management_interval.step(dtime, 0.5))
1348 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1350 Remove objects that satisfy (m_removed && m_known_by_count==0)
1352 removeRemovedObjects();
1356 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1358 std::map<u16, ServerActiveObject*>::iterator n;
1359 n = m_active_objects.find(id);
1360 if(n == m_active_objects.end())
1365 bool isFreeServerActiveObjectId(u16 id,
1366 std::map<u16, ServerActiveObject*> &objects)
1371 return objects.find(id) == objects.end();
1374 u16 getFreeServerActiveObjectId(
1375 std::map<u16, ServerActiveObject*> &objects)
1377 //try to reuse id's as late as possible
1378 static u16 last_used_id = 0;
1379 u16 startid = last_used_id;
1383 if(isFreeServerActiveObjectId(last_used_id, objects))
1384 return last_used_id;
1386 if(last_used_id == startid)
1391 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1395 u16 id = addActiveObjectRaw(object, true, 0);
1400 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1404 v3f objectpos = obj->getBasePosition();
1406 // The block in which the object resides in
1407 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1410 Update the static data
1413 // Create new static object
1414 std::string staticdata = obj->getStaticData();
1415 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1416 // Add to the block where the object is located in
1417 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1418 // Get or generate the block
1419 MapBlock *block = m_map->emergeBlock(blockpos);
1421 bool succeeded = false;
1425 block->m_static_objects.insert(0, s_obj);
1426 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1427 "addActiveObjectAsStatic");
1431 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1432 <<"Could not find or generate "
1433 <<"a block for storing static object"<<std::endl;
1437 if(obj->environmentDeletes())
1445 Finds out what new objects have been added to
1446 inside a radius around a position
1448 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1449 std::set<u16> ¤t_objects,
1450 std::set<u16> &added_objects)
1452 v3f pos_f = intToFloat(pos, BS);
1453 f32 radius_f = radius * BS;
1455 Go through the object list,
1456 - discard m_removed objects,
1457 - discard objects that are too far away,
1458 - discard objects that are found in current_objects.
1459 - add remaining objects to added_objects
1461 for(std::map<u16, ServerActiveObject*>::iterator
1462 i = m_active_objects.begin();
1463 i != m_active_objects.end(); ++i)
1467 ServerActiveObject *object = i->second;
1470 // Discard if removed or deactivating
1471 if(object->m_removed || object->m_pending_deactivation)
1473 if(object->unlimitedTransferDistance() == false){
1474 // Discard if too far
1475 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1476 if(distance_f > radius_f)
1479 // Discard if already on current_objects
1480 std::set<u16>::iterator n;
1481 n = current_objects.find(id);
1482 if(n != current_objects.end())
1484 // Add to added_objects
1485 added_objects.insert(id);
1490 Finds out what objects have been removed from
1491 inside a radius around a position
1493 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1494 std::set<u16> ¤t_objects,
1495 std::set<u16> &removed_objects)
1497 v3f pos_f = intToFloat(pos, BS);
1498 f32 radius_f = radius * BS;
1500 Go through current_objects; object is removed if:
1501 - object is not found in m_active_objects (this is actually an
1502 error condition; objects should be set m_removed=true and removed
1503 only after all clients have been informed about removal), or
1504 - object has m_removed=true, or
1505 - object is too far away
1507 for(std::set<u16>::iterator
1508 i = current_objects.begin();
1509 i != current_objects.end(); ++i)
1512 ServerActiveObject *object = getActiveObject(id);
1515 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1516 <<" object in current_objects is NULL"<<std::endl;
1517 removed_objects.insert(id);
1521 if(object->m_removed || object->m_pending_deactivation)
1523 removed_objects.insert(id);
1527 // If transfer distance is unlimited, don't remove
1528 if(object->unlimitedTransferDistance())
1531 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1533 if(distance_f >= radius_f)
1535 removed_objects.insert(id);
1543 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1545 if(m_active_object_messages.empty())
1546 return ActiveObjectMessage(0);
1548 ActiveObjectMessage message = m_active_object_messages.front();
1549 m_active_object_messages.pop_front();
1554 ************ Private methods *************
1557 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1558 bool set_changed, u32 dtime_s)
1561 if(object->getId() == 0){
1562 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1565 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1566 <<"no free ids available"<<std::endl;
1567 if(object->environmentDeletes())
1571 object->setId(new_id);
1574 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1575 <<"supplied with id "<<object->getId()<<std::endl;
1577 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1579 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1580 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1581 if(object->environmentDeletes())
1585 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1586 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1588 m_active_objects[object->getId()] = object;
1590 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1591 <<"Added id="<<object->getId()<<"; there are now "
1592 <<m_active_objects.size()<<" active objects."
1595 // Register reference in scripting api (must be done before post-init)
1596 m_script->addObjectReference(object);
1597 // Post-initialize object
1598 object->addedToEnvironment(dtime_s);
1600 // Add static data to block
1601 if(object->isStaticAllowed())
1603 // Add static object to active static list of the block
1604 v3f objectpos = object->getBasePosition();
1605 std::string staticdata = object->getStaticData();
1606 StaticObject s_obj(object->getType(), objectpos, staticdata);
1607 // Add to the block where the object is located in
1608 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1609 MapBlock *block = m_map->emergeBlock(blockpos);
1611 block->m_static_objects.m_active[object->getId()] = s_obj;
1612 object->m_static_exists = true;
1613 object->m_static_block = blockpos;
1616 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1617 "addActiveObjectRaw");
1619 v3s16 p = floatToInt(objectpos, BS);
1620 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1621 <<"could not emerge block for storing id="<<object->getId()
1622 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1626 return object->getId();
1630 Remove objects that satisfy (m_removed && m_known_by_count==0)
1632 void ServerEnvironment::removeRemovedObjects()
1634 std::list<u16> objects_to_remove;
1635 for(std::map<u16, ServerActiveObject*>::iterator
1636 i = m_active_objects.begin();
1637 i != m_active_objects.end(); ++i)
1640 ServerActiveObject* obj = i->second;
1641 // This shouldn't happen but check it
1644 infostream<<"NULL object found in ServerEnvironment"
1645 <<" while finding removed objects. id="<<id<<std::endl;
1646 // Id to be removed from m_active_objects
1647 objects_to_remove.push_back(id);
1652 We will delete objects that are marked as removed or thatare
1653 waiting for deletion after deactivation
1655 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1659 Delete static data from block if is marked as removed
1661 if(obj->m_static_exists && obj->m_removed)
1663 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1665 block->m_static_objects.remove(id);
1666 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1667 "removeRemovedObjects/remove");
1668 obj->m_static_exists = false;
1670 infostream<<"Failed to emerge block from which an object to "
1671 <<"be removed was loaded from. id="<<id<<std::endl;
1675 // If m_known_by_count > 0, don't actually remove. On some future
1676 // invocation this will be 0, which is when removal will continue.
1677 if(obj->m_known_by_count > 0)
1681 Move static data from active to stored if not marked as removed
1683 if(obj->m_static_exists && !obj->m_removed){
1684 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1686 std::map<u16, StaticObject>::iterator i =
1687 block->m_static_objects.m_active.find(id);
1688 if(i != block->m_static_objects.m_active.end()){
1689 block->m_static_objects.m_stored.push_back(i->second);
1690 block->m_static_objects.m_active.erase(id);
1691 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1692 "removeRemovedObjects/deactivate");
1695 infostream<<"Failed to emerge block from which an object to "
1696 <<"be deactivated was loaded from. id="<<id<<std::endl;
1700 // Tell the object about removal
1701 obj->removingFromEnvironment();
1702 // Deregister in scripting api
1703 m_script->removeObjectReference(obj);
1706 if(obj->environmentDeletes())
1708 // Id to be removed from m_active_objects
1709 objects_to_remove.push_back(id);
1711 // Remove references from m_active_objects
1712 for(std::list<u16>::iterator i = objects_to_remove.begin();
1713 i != objects_to_remove.end(); ++i)
1715 m_active_objects.erase(*i);
1719 static void print_hexdump(std::ostream &o, const std::string &data)
1721 const int linelength = 16;
1722 for(int l=0; ; l++){
1723 int i0 = linelength * l;
1724 bool at_end = false;
1725 int thislinelength = linelength;
1726 if(i0 + thislinelength > (int)data.size()){
1727 thislinelength = data.size() - i0;
1730 for(int di=0; di<linelength; di++){
1733 if(di<thislinelength)
1734 snprintf(buf, 4, "%.2x ", data[i]);
1736 snprintf(buf, 4, " ");
1740 for(int di=0; di<thislinelength; di++){
1754 Convert stored objects from blocks near the players to active.
1756 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1760 // Ignore if no stored objects (to not set changed flag)
1761 if(block->m_static_objects.m_stored.size() == 0)
1763 verbosestream<<"ServerEnvironment::activateObjects(): "
1764 <<"activating objects of block "<<PP(block->getPos())
1765 <<" ("<<block->m_static_objects.m_stored.size()
1766 <<" objects)"<<std::endl;
1767 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1769 errorstream<<"suspiciously large amount of objects detected: "
1770 <<block->m_static_objects.m_stored.size()<<" in "
1771 <<PP(block->getPos())
1772 <<"; removing all of them."<<std::endl;
1773 // Clear stored list
1774 block->m_static_objects.m_stored.clear();
1775 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1776 "stored list cleared in activateObjects due to "
1777 "large amount of objects");
1781 // Activate stored objects
1782 std::list<StaticObject> new_stored;
1783 for(std::list<StaticObject>::iterator
1784 i = block->m_static_objects.m_stored.begin();
1785 i != block->m_static_objects.m_stored.end(); ++i)
1787 /*infostream<<"Server: Creating an active object from "
1788 <<"static data"<<std::endl;*/
1789 StaticObject &s_obj = *i;
1790 // Create an active object from the data
1791 ServerActiveObject *obj = ServerActiveObject::create
1792 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1793 // If couldn't create object, store static data back.
1796 errorstream<<"ServerEnvironment::activateObjects(): "
1797 <<"failed to create active object from static object "
1798 <<"in block "<<PP(s_obj.pos/BS)
1799 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1800 print_hexdump(verbosestream, s_obj.data);
1802 new_stored.push_back(s_obj);
1805 verbosestream<<"ServerEnvironment::activateObjects(): "
1806 <<"activated static object pos="<<PP(s_obj.pos/BS)
1807 <<" type="<<(int)s_obj.type<<std::endl;
1808 // This will also add the object to the active static list
1809 addActiveObjectRaw(obj, false, dtime_s);
1811 // Clear stored list
1812 block->m_static_objects.m_stored.clear();
1813 // Add leftover failed stuff to stored list
1814 for(std::list<StaticObject>::iterator
1815 i = new_stored.begin();
1816 i != new_stored.end(); ++i)
1818 StaticObject &s_obj = *i;
1819 block->m_static_objects.m_stored.push_back(s_obj);
1822 // Turn the active counterparts of activated objects not pending for
1824 for(std::map<u16, StaticObject>::iterator
1825 i = block->m_static_objects.m_active.begin();
1826 i != block->m_static_objects.m_active.end(); ++i)
1829 ServerActiveObject *object = getActiveObject(id);
1831 object->m_pending_deactivation = false;
1835 Note: Block hasn't really been modified here.
1836 The objects have just been activated and moved from the stored
1837 static list to the active static list.
1838 As such, the block is essentially the same.
1839 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1840 Otherwise there would be a huge amount of unnecessary I/O.
1845 Convert objects that are not standing inside active blocks to static.
1847 If m_known_by_count != 0, active object is not deleted, but static
1848 data is still updated.
1850 If force_delete is set, active object is deleted nevertheless. It
1851 shall only be set so in the destructor of the environment.
1853 If block wasn't generated (not in memory or on disk),
1855 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1857 std::list<u16> objects_to_remove;
1858 for(std::map<u16, ServerActiveObject*>::iterator
1859 i = m_active_objects.begin();
1860 i != m_active_objects.end(); ++i)
1862 ServerActiveObject* obj = i->second;
1865 // Do not deactivate if static data creation not allowed
1866 if(!force_delete && !obj->isStaticAllowed())
1869 // If pending deactivation, let removeRemovedObjects() do it
1870 if(!force_delete && obj->m_pending_deactivation)
1874 v3f objectpos = obj->getBasePosition();
1876 // The block in which the object resides in
1877 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1879 // If object's static data is stored in a deactivated block and object
1880 // is actually located in an active block, re-save to the block in
1881 // which the object is actually located in.
1883 obj->m_static_exists &&
1884 !m_active_blocks.contains(obj->m_static_block) &&
1885 m_active_blocks.contains(blockpos_o))
1887 v3s16 old_static_block = obj->m_static_block;
1889 // Save to block where object is located
1890 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1892 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1893 <<"Could not save object id="<<id
1894 <<" to it's current block "<<PP(blockpos_o)
1898 std::string staticdata_new = obj->getStaticData();
1899 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1900 block->m_static_objects.insert(id, s_obj);
1901 obj->m_static_block = blockpos_o;
1902 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1903 "deactivateFarObjects: Static data moved in");
1905 // Delete from block where object was located
1906 block = m_map->emergeBlock(old_static_block, false);
1908 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1909 <<"Could not delete object id="<<id
1910 <<" from it's previous block "<<PP(old_static_block)
1914 block->m_static_objects.remove(id);
1915 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1916 "deactivateFarObjects: Static data moved out");
1920 // If block is active, don't remove
1921 if(!force_delete && m_active_blocks.contains(blockpos_o))
1924 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1925 <<"deactivating object id="<<id<<" on inactive block "
1926 <<PP(blockpos_o)<<std::endl;
1928 // If known by some client, don't immediately delete.
1929 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1932 Update the static data
1935 if(obj->isStaticAllowed())
1937 // Create new static object
1938 std::string staticdata_new = obj->getStaticData();
1939 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1941 bool stays_in_same_block = false;
1942 bool data_changed = true;
1944 if(obj->m_static_exists){
1945 if(obj->m_static_block == blockpos_o)
1946 stays_in_same_block = true;
1948 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1950 std::map<u16, StaticObject>::iterator n =
1951 block->m_static_objects.m_active.find(id);
1952 if(n != block->m_static_objects.m_active.end()){
1953 StaticObject static_old = n->second;
1955 float save_movem = obj->getMinimumSavedMovement();
1957 if(static_old.data == staticdata_new &&
1958 (static_old.pos - objectpos).getLength() < save_movem)
1959 data_changed = false;
1961 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1962 <<"id="<<id<<" m_static_exists=true but "
1963 <<"static data doesn't actually exist in "
1964 <<PP(obj->m_static_block)<<std::endl;
1968 bool shall_be_written = (!stays_in_same_block || data_changed);
1970 // Delete old static object
1971 if(obj->m_static_exists)
1973 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1976 block->m_static_objects.remove(id);
1977 obj->m_static_exists = false;
1978 // Only mark block as modified if data changed considerably
1979 if(shall_be_written)
1980 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1981 "deactivateFarObjects: Static data "
1982 "changed considerably");
1986 // Add to the block where the object is located in
1987 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1988 // Get or generate the block
1989 MapBlock *block = NULL;
1991 block = m_map->emergeBlock(blockpos);
1992 } catch(InvalidPositionException &e){
1993 // Handled via NULL pointer
1994 // NOTE: emergeBlock's failure is usually determined by it
1995 // actually returning NULL
2000 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2001 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2002 <<" statically but block "<<PP(blockpos)
2003 <<" already contains "
2004 <<block->m_static_objects.m_stored.size()
2006 <<" Forcing delete."<<std::endl;
2007 force_delete = true;
2009 // If static counterpart already exists in target block,
2011 // This shouldn't happen because the object is removed from
2012 // the previous block before this according to
2013 // obj->m_static_block, but happens rarely for some unknown
2014 // reason. Unsuccessful attempts have been made to find
2016 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2017 infostream<<"ServerEnv: WARNING: Performing hack #83274"
2019 block->m_static_objects.remove(id);
2021 // Store static data
2022 u16 store_id = pending_delete ? id : 0;
2023 block->m_static_objects.insert(store_id, s_obj);
2025 // Only mark block as modified if data changed considerably
2026 if(shall_be_written)
2027 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2028 "deactivateFarObjects: Static data "
2029 "changed considerably");
2031 obj->m_static_exists = true;
2032 obj->m_static_block = block->getPos();
2037 v3s16 p = floatToInt(objectpos, BS);
2038 errorstream<<"ServerEnv: Could not find or generate "
2039 <<"a block for storing id="<<obj->getId()
2040 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2047 If known by some client, set pending deactivation.
2048 Otherwise delete it immediately.
2051 if(pending_delete && !force_delete)
2053 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2054 <<"object id="<<id<<" is known by clients"
2055 <<"; not deleting yet"<<std::endl;
2057 obj->m_pending_deactivation = true;
2061 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2062 <<"object id="<<id<<" is not known by clients"
2063 <<"; deleting"<<std::endl;
2065 // Tell the object about removal
2066 obj->removingFromEnvironment();
2067 // Deregister in scripting api
2068 m_script->removeObjectReference(obj);
2070 // Delete active object
2071 if(obj->environmentDeletes())
2073 // Id to be removed from m_active_objects
2074 objects_to_remove.push_back(id);
2077 // Remove references from m_active_objects
2078 for(std::list<u16>::iterator i = objects_to_remove.begin();
2079 i != objects_to_remove.end(); ++i)
2081 m_active_objects.erase(*i);
2088 #include "clientsimpleobject.h"
2094 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2095 ITextureSource *texturesource, IGameDef *gamedef,
2096 IrrlichtDevice *irr):
2099 m_texturesource(texturesource),
2104 memset(m_attachements, zero, sizeof(m_attachements));
2107 ClientEnvironment::~ClientEnvironment()
2109 // delete active objects
2110 for(std::map<u16, ClientActiveObject*>::iterator
2111 i = m_active_objects.begin();
2112 i != m_active_objects.end(); ++i)
2117 for(std::list<ClientSimpleObject*>::iterator
2118 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2127 Map & ClientEnvironment::getMap()
2132 ClientMap & ClientEnvironment::getClientMap()
2137 void ClientEnvironment::addPlayer(Player *player)
2139 DSTACK(__FUNCTION_NAME);
2141 It is a failure if player is local and there already is a local
2144 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2146 Environment::addPlayer(player);
2149 LocalPlayer * ClientEnvironment::getLocalPlayer()
2151 for(std::list<Player*>::iterator i = m_players.begin();
2152 i != m_players.end(); ++i)
2154 Player *player = *i;
2155 if(player->isLocal())
2156 return (LocalPlayer*)player;
2161 void ClientEnvironment::step(float dtime)
2163 DSTACK(__FUNCTION_NAME);
2165 /* Step time of day */
2166 stepTimeOfDay(dtime);
2168 // Get some settings
2169 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2170 bool free_move = fly_allowed && g_settings->getBool("free_move");
2173 LocalPlayer *lplayer = getLocalPlayer();
2175 // collision info queue
2176 std::list<CollisionInfo> player_collisions;
2179 Get the speed the player is going
2181 bool is_climbing = lplayer->is_climbing;
2183 f32 player_speed = lplayer->getSpeed().getLength();
2186 Maximum position increment
2188 //f32 position_max_increment = 0.05*BS;
2189 f32 position_max_increment = 0.1*BS;
2191 // Maximum time increment (for collision detection etc)
2192 // time = distance / speed
2193 f32 dtime_max_increment = 1;
2194 if(player_speed > 0.001)
2195 dtime_max_increment = position_max_increment / player_speed;
2197 // Maximum time increment is 10ms or lower
2198 if(dtime_max_increment > 0.01)
2199 dtime_max_increment = 0.01;
2201 // Don't allow overly huge dtime
2205 f32 dtime_downcount = dtime;
2208 Stuff that has a maximum time increment
2217 if(dtime_downcount > dtime_max_increment)
2219 dtime_part = dtime_max_increment;
2220 dtime_downcount -= dtime_part;
2224 dtime_part = dtime_downcount;
2226 Setting this to 0 (no -=dtime_part) disables an infinite loop
2227 when dtime_part is so small that dtime_downcount -= dtime_part
2230 dtime_downcount = 0;
2239 if(free_move == false && is_climbing == false)
2242 v3f speed = lplayer->getSpeed();
2243 if(lplayer->in_liquid == false)
2244 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2246 // Liquid floating / sinking
2247 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2248 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2250 // Liquid resistance
2251 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2253 // How much the node's viscosity blocks movement, ranges between 0 and 1
2254 // Should match the scale at which viscosity increase affects other liquid attributes
2255 const f32 viscosity_factor = 0.3;
2257 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2258 f32 dl = d_wanted.getLength();
2259 if(dl > lplayer->movement_liquid_fluidity_smooth)
2260 dl = lplayer->movement_liquid_fluidity_smooth;
2261 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2263 v3f d = d_wanted.normalize() * dl;
2267 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2268 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2269 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2270 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2271 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2272 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2276 lplayer->setSpeed(speed);
2281 This also does collision detection.
2283 lplayer->move(dtime_part, this, position_max_increment,
2284 &player_collisions);
2287 while(dtime_downcount > 0.001);
2289 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2291 for(std::list<CollisionInfo>::iterator
2292 i = player_collisions.begin();
2293 i != player_collisions.end(); ++i)
2295 CollisionInfo &info = *i;
2296 v3f speed_diff = info.new_speed - info.old_speed;;
2297 // Handle only fall damage
2298 // (because otherwise walking against something in fast_move kills you)
2299 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2301 // Get rid of other components
2304 f32 pre_factor = 1; // 1 hp per node/s
2305 f32 tolerance = BS*14; // 5 without damage
2306 f32 post_factor = 1; // 1 hp per node/s
2307 if(info.type == COLLISION_NODE)
2309 const ContentFeatures &f = m_gamedef->ndef()->
2310 get(m_map->getNodeNoEx(info.node_p));
2311 // Determine fall damage multiplier
2312 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2313 pre_factor = 1.0 + (float)addp/100.0;
2315 float speed = pre_factor * speed_diff.getLength();
2316 if(speed > tolerance)
2318 f32 damage_f = (speed - tolerance)/BS * post_factor;
2319 u16 damage = (u16)(damage_f+0.5);
2321 damageLocalPlayer(damage, true);
2322 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2323 m_gamedef->event()->put(e);
2329 A quick draft of lava damage
2331 if(m_lava_hurt_interval.step(dtime, 1.0))
2333 v3f pf = lplayer->getPosition();
2335 // Feet, middle and head
2336 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2337 MapNode n1 = m_map->getNodeNoEx(p1);
2338 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2339 MapNode n2 = m_map->getNodeNoEx(p2);
2340 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2341 MapNode n3 = m_map->getNodeNoEx(p3);
2343 u32 damage_per_second = 0;
2344 damage_per_second = MYMAX(damage_per_second,
2345 m_gamedef->ndef()->get(n1).damage_per_second);
2346 damage_per_second = MYMAX(damage_per_second,
2347 m_gamedef->ndef()->get(n2).damage_per_second);
2348 damage_per_second = MYMAX(damage_per_second,
2349 m_gamedef->ndef()->get(n3).damage_per_second);
2351 if(damage_per_second != 0)
2353 damageLocalPlayer(damage_per_second, true);
2360 if(m_drowning_interval.step(dtime, 2.0))
2362 v3f pf = lplayer->getPosition();
2365 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2366 MapNode n = m_map->getNodeNoEx(p);
2367 ContentFeatures c = m_gamedef->ndef()->get(n);
2368 u8 drowning_damage = c.drowning;
2369 if(drowning_damage > 0 && lplayer->hp > 0){
2370 u16 breath = lplayer->getBreath();
2377 lplayer->setBreath(breath);
2378 updateLocalPlayerBreath(breath);
2381 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2382 damageLocalPlayer(drowning_damage, true);
2385 if(m_breathing_interval.step(dtime, 0.5))
2387 v3f pf = lplayer->getPosition();
2390 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2391 MapNode n = m_map->getNodeNoEx(p);
2392 ContentFeatures c = m_gamedef->ndef()->get(n);
2394 lplayer->setBreath(11);
2396 else if(c.drowning == 0){
2397 u16 breath = lplayer->getBreath();
2400 lplayer->setBreath(breath);
2401 updateLocalPlayerBreath(breath);
2407 Stuff that can be done in an arbitarily large dtime
2409 for(std::list<Player*>::iterator i = m_players.begin();
2410 i != m_players.end(); ++i)
2412 Player *player = *i;
2415 Handle non-local players
2417 if(player->isLocal() == false)
2420 player->move(dtime, this, 100*BS);
2424 // Update lighting on all players on client
2428 v3s16 p = player->getLightPosition();
2429 MapNode n = m_map->getNode(p);
2430 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2432 catch(InvalidPositionException &e){
2433 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2435 player->light = light;
2439 Step active objects and update lighting of them
2442 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2443 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2444 for(std::map<u16, ClientActiveObject*>::iterator
2445 i = m_active_objects.begin();
2446 i != m_active_objects.end(); ++i)
2448 ClientActiveObject* obj = i->second;
2450 obj->step(dtime, this);
2458 v3s16 p = obj->getLightPosition();
2459 MapNode n = m_map->getNode(p);
2460 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2462 catch(InvalidPositionException &e){
2463 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2465 obj->updateLight(light);
2470 Step and handle simple objects
2472 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2473 for(std::list<ClientSimpleObject*>::iterator
2474 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2476 ClientSimpleObject *simple = *i;
2477 std::list<ClientSimpleObject*>::iterator cur = i;
2479 simple->step(dtime);
2480 if(simple->m_to_be_removed){
2482 m_simple_objects.erase(cur);
2487 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2489 m_simple_objects.push_back(simple);
2492 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2494 std::map<u16, ClientActiveObject*>::iterator n;
2495 n = m_active_objects.find(id);
2496 if(n == m_active_objects.end())
2501 bool isFreeClientActiveObjectId(u16 id,
2502 std::map<u16, ClientActiveObject*> &objects)
2507 return objects.find(id) == objects.end();
2510 u16 getFreeClientActiveObjectId(
2511 std::map<u16, ClientActiveObject*> &objects)
2513 //try to reuse id's as late as possible
2514 static u16 last_used_id = 0;
2515 u16 startid = last_used_id;
2519 if(isFreeClientActiveObjectId(last_used_id, objects))
2520 return last_used_id;
2522 if(last_used_id == startid)
2527 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2530 if(object->getId() == 0)
2532 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2535 infostream<<"ClientEnvironment::addActiveObject(): "
2536 <<"no free ids available"<<std::endl;
2540 object->setId(new_id);
2542 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2544 infostream<<"ClientEnvironment::addActiveObject(): "
2545 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2549 infostream<<"ClientEnvironment::addActiveObject(): "
2550 <<"added (id="<<object->getId()<<")"<<std::endl;
2551 m_active_objects[object->getId()] = object;
2552 object->addToScene(m_smgr, m_texturesource, m_irr);
2553 { // Update lighting immediately
2557 v3s16 p = object->getLightPosition();
2558 MapNode n = m_map->getNode(p);
2559 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2561 catch(InvalidPositionException &e){
2562 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2564 object->updateLight(light);
2566 return object->getId();
2569 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2570 const std::string &init_data)
2572 ClientActiveObject* obj =
2573 ClientActiveObject::create(type, m_gamedef, this);
2576 infostream<<"ClientEnvironment::addActiveObject(): "
2577 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2586 obj->initialize(init_data);
2588 catch(SerializationError &e)
2590 errorstream<<"ClientEnvironment::addActiveObject():"
2591 <<" id="<<id<<" type="<<type
2592 <<": SerializationError in initialize(): "
2594 <<": init_data="<<serializeJsonString(init_data)
2598 addActiveObject(obj);
2601 void ClientEnvironment::removeActiveObject(u16 id)
2603 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2604 <<"id="<<id<<std::endl;
2605 ClientActiveObject* obj = getActiveObject(id);
2608 infostream<<"ClientEnvironment::removeActiveObject(): "
2609 <<"id="<<id<<" not found"<<std::endl;
2612 obj->removeFromScene(true);
2614 m_active_objects.erase(id);
2617 void ClientEnvironment::processActiveObjectMessage(u16 id,
2618 const std::string &data)
2620 ClientActiveObject* obj = getActiveObject(id);
2623 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2624 <<" got message for id="<<id<<", which doesn't exist."
2630 obj->processMessage(data);
2632 catch(SerializationError &e)
2634 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2635 <<" id="<<id<<" type="<<obj->getType()
2636 <<" SerializationError in processMessage(),"
2637 <<" message="<<serializeJsonString(data)
2643 Callbacks for activeobjects
2646 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2648 LocalPlayer *lplayer = getLocalPlayer();
2652 if(lplayer->hp > damage)
2653 lplayer->hp -= damage;
2658 ClientEnvEvent event;
2659 event.type = CEE_PLAYER_DAMAGE;
2660 event.player_damage.amount = damage;
2661 event.player_damage.send_to_server = handle_hp;
2662 m_client_event_queue.push_back(event);
2665 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2667 ClientEnvEvent event;
2668 event.type = CEE_PLAYER_BREATH;
2669 event.player_breath.amount = breath;
2670 m_client_event_queue.push_back(event);
2674 Client likes to call these
2677 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2678 std::vector<DistanceSortedActiveObject> &dest)
2680 for(std::map<u16, ClientActiveObject*>::iterator
2681 i = m_active_objects.begin();
2682 i != m_active_objects.end(); ++i)
2684 ClientActiveObject* obj = i->second;
2686 f32 d = (obj->getPosition() - origin).getLength();
2691 DistanceSortedActiveObject dso(obj, d);
2693 dest.push_back(dso);
2697 ClientEnvEvent ClientEnvironment::getClientEvent()
2699 ClientEnvEvent event;
2700 if(m_client_event_queue.empty())
2701 event.type = CEE_NONE;
2703 event = m_client_event_queue.front();
2704 m_client_event_queue.pop_front();
2709 #endif // #ifndef SERVER