diff options
Diffstat (limited to 'src/server.cpp')
-rw-r--r-- | src/server.cpp | 512 |
1 files changed, 283 insertions, 229 deletions
diff --git a/src/server.cpp b/src/server.cpp index 0a8288324..d227474aa 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "craftdef.h" #include "mapgen.h" +#include "biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" #include "content_abm.h" @@ -80,7 +81,7 @@ public: *m_flag = false; } } - + private: bool *m_flag; }; @@ -105,7 +106,7 @@ public: *m_ignorevariable = VoxelArea(); } } - + private: VoxelArea *m_ignorevariable; }; @@ -129,7 +130,7 @@ void * ServerThread::Thread() //TimeTaker timer("AsyncRunStep()"); m_server->AsyncRunStep(); } - + //infostream<<"Running m_server->Receive()"<<std::endl; m_server->Receive(); } @@ -149,7 +150,7 @@ void * ServerThread::Thread() m_server->setAsyncFatalError(e.what()); } } - + END_DEBUG_EXCEPTION_HANDLER(errorstream) return NULL; @@ -168,7 +169,11 @@ void * EmergeThread::Thread() bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); v3s16 last_tried_pos(-32768,-32768,-32768); // For error output - + + ServerMap &map = ((ServerMap&)m_server->m_env->getMap()); + EmergeManager *emerge = m_server->m_emerge; + Mapgen *mapgen = emerge->getMapgen(); + /* Get block info from queue, emerge them and send them to clients. @@ -180,12 +185,12 @@ void * EmergeThread::Thread() QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop(); if(qptr == NULL) break; - + SharedPtr<QueuedBlockEmerge> q(qptr); v3s16 &p = q->pos; v2s16 p2d(p.X,p.Z); - + last_tried_pos = p; /* @@ -193,18 +198,18 @@ void * EmergeThread::Thread() */ if(blockpos_over_limit(p)) continue; - + //infostream<<"EmergeThread::Thread(): running"<<std::endl; //TimeTaker timer("block emerge"); - + /* Try to emerge it from somewhere. If it is only wanted as optional, only loading from disk will be allowed. */ - + /* Check if any peer wants it as non-optional. In that case it will be generated. @@ -224,17 +229,17 @@ void * EmergeThread::Thread() u8 flags = i.getNode()->getValue(); if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) only_from_disk = false; - + } } - + if(enable_mapgen_debug_info) infostream<<"EmergeThread: p=" <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") " <<"only_from_disk="<<only_from_disk<<std::endl; - - ServerMap &map = ((ServerMap&)m_server->m_env->getMap()); - + + + MapBlock *block = NULL; bool got_block = true; core::map<v3s16, MapBlock*> modified_blocks; @@ -243,17 +248,17 @@ void * EmergeThread::Thread() Try to fetch block from memory or disk. If not found and asked to generate, initialize generator. */ - + bool started_generate = false; - mapgen::BlockMakeData data; + BlockMakeData data; { JMutexAutoLock envlock(m_server->m_env_mutex); - + // Load sector if it isn't loaded if(map.getSectorNoGenerateNoEx(p2d) == NULL) map.loadSectorMeta(p2d); - + // Attempt to load block block = map.getBlockNoCreateNoEx(p); if(!block || block->isDummy() || !block->isGenerated()) @@ -264,7 +269,7 @@ void * EmergeThread::Thread() block = map.loadBlock(p); } - + // If could not load and allowed to generate, start generation // inside this same envlock if(only_from_disk == false && @@ -287,16 +292,17 @@ void * EmergeThread::Thread() SPT_AVG); TimeTaker t("mapgen::make_block()"); - mapgen::make_block(&data); + mapgen->makeChunk(&data); + //mapgen::make_block(&data); if(enable_mapgen_debug_info == false) t.stop(true); // Hide output } - + do{ // enable break // Lock environment again to access the map JMutexAutoLock envlock(m_server->m_env_mutex); - + ScopeProfiler sp(g_profiler, "EmergeThread: after " "mapgen::make_block (envlock)", SPT_AVG); @@ -306,7 +312,7 @@ void * EmergeThread::Thread() // Get central block block = map.getBlockNoCreateNoEx(p); - + // If block doesn't exist, don't try doing anything with it // This happens if the block is not in generation boundaries if(!block) @@ -319,7 +325,7 @@ void * EmergeThread::Thread() v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE; v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE + v3s16(1,1,1)*(MAP_BLOCKSIZE-1); - + /* Ignore map edit events, they will not need to be sent to anybody because the block hasn't been sent @@ -332,11 +338,11 @@ void * EmergeThread::Thread() { TimeTaker timer("on_generated"); scriptapi_environment_on_generated(m_server->m_lua, - minp, maxp, mapgen::get_blockseed(data.seed, minp)); + minp, maxp, emerge->getBlockSeed(minp)); /*int t = timer.stop(true); dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/ } - + if(enable_mapgen_debug_info) infostream<<"EmergeThread: ended up with: " <<analyze_block(block)<<std::endl; @@ -348,11 +354,11 @@ void * EmergeThread::Thread() if(block == NULL) got_block = false; - + /* Set sent status of modified blocks on clients */ - + // NOTE: Server's clients are also behind the connection mutex JMutexAutoLock lock(m_server->m_con_mutex); @@ -363,17 +369,17 @@ void * EmergeThread::Thread() { modified_blocks.insert(p, block); } - + /* Set the modified blocks unsent for all the clients */ - + for(core::map<u16, RemoteClient*>::Iterator i = m_server->m_clients.getIterator(); i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); - + if(modified_blocks.size() > 0) { // Remove block from sent history @@ -434,14 +440,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, core::array<PrioritySortedBlockTransfer> &dest) { DSTACK(__FUNCTION_NAME); - + /*u32 timer_result; TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/ - + // Increment timers m_nothing_to_send_pause_timer -= dtime; m_nearest_unsent_reset_timer += dtime; - + if(m_nothing_to_send_pause_timer >= 0) return; @@ -459,7 +465,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } //TimeTaker timer("RemoteClient::GetNextBlocks"); - + v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); v3f playerspeeddir(0,0,0); @@ -471,7 +477,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); - + // Camera position and direction v3f camera_pos = player->getEyePosition(); v3f camera_dir = v3f(0,0,1); @@ -484,7 +490,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* Get the starting value of the block finder radius. */ - + if(m_last_center != center) { m_nearest_unsent_d = 0; @@ -493,7 +499,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /*infostream<<"m_nearest_unsent_reset_timer=" <<m_nearest_unsent_reset_timer<<std::endl;*/ - + // Reset periodically to workaround for some bugs or stuff if(m_nearest_unsent_reset_timer > 20.0) { @@ -514,7 +520,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* Check the time from last addNode/removeNode. - + Decrease send rate if player is building stuff. */ m_time_from_building += dtime; @@ -524,12 +530,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, max_simul_sends_usually = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; } - + /* Number of blocks sending + number of blocks selected for sending */ u32 num_blocks_selected = m_blocks_sending.size(); - + /* next time d will be continued from the d from which the nearest unsent block was found this time. @@ -541,28 +547,28 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, s16 d_max = g_settings->getS16("max_block_send_distance"); s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); - + // Don't loop very much at a time s16 max_d_increment_at_time = 2; if(d_max > d_start + max_d_increment_at_time) d_max = d_start + max_d_increment_at_time; /*if(d_max_gen > d_start+2) d_max_gen = d_start+2;*/ - + //infostream<<"Starting from "<<d_start<<std::endl; s32 nearest_emerged_d = -1; s32 nearest_emergefull_d = -1; s32 nearest_sent_d = -1; bool queue_is_full = false; - + s16 d; for(d = d_start; d <= d_max; d++) { /*errorstream<<"checking d="<<d<<" for " <<server->getPlayerName(peer_id)<<std::endl;*/ //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl; - + /* If m_nearest_unsent_d was changed by the EmergeThread (it can change it to 0 through SetBlockNotSent), @@ -581,12 +587,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ core::list<v3s16> list; getFacePositions(list, d); - + core::list<v3s16>::Iterator li; for(li=list.begin(); li!=list.end(); li++) { v3s16 p = *li + center; - + /* Send throttling - Don't allow too many simultaneous transfers @@ -594,10 +600,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Also, don't send blocks that are already flying. */ - + // Start with the usual maximum u16 max_simul_dynamic = max_simul_sends_usually; - + // If block is very close, allow full maximum if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) max_simul_dynamic = max_simul_sends_setting; @@ -608,11 +614,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, queue_is_full = true; goto queue_full_break; } - + // Don't send blocks that are currently being transferred if(m_blocks_sending.find(p) != NULL) continue; - + /* Do not go over-limit */ @@ -623,10 +629,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) continue; - + // If this is true, inexistent block will be made from scratch bool generate = d <= d_max_gen; - + { /*// Limit the generating area vertically to 2/3 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) @@ -655,7 +661,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, v2s16 p2d_nodes_center( MAP_BLOCKSIZE*p.X, MAP_BLOCKSIZE*p.Z); - + // Get ground height in nodes s16 gh = server->m_env->getServerMap().findGroundLevel( p2d_nodes_center); @@ -697,7 +703,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Check if map has this block */ MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p); - + bool surely_not_found_on_disk = false; bool block_is_invalid = false; if(block != NULL) @@ -711,13 +717,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { surely_not_found_on_disk = true; } - + // Block is valid if lighting is up-to-date and data exists if(block->isValid() == false) { block_is_invalid = true; } - + /*if(block->isFullyGenerated() == false) { block_is_invalid = true; @@ -775,13 +781,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge) { //infostream<<"Adding block to emerge queue"<<std::endl; - + // Add it to the emerge queue and trigger the thread - + u8 flags = 0; if(generate == false) flags |= BLOCK_EMERGE_FLAG_FROMDISK; - + server->m_emerge_queue.addBlock(peer_id, p, flags); server->m_emergethread.trigger(); @@ -790,8 +796,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } else { if(nearest_emergefull_d == -1) nearest_emergefull_d = d; + goto queue_full_break; } - + // get next one. continue; } @@ -816,7 +823,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, queue_full_break: //infostream<<"Stopped at "<<d<<std::endl; - + // If nothing was found for sending and nothing was queued for // emerging, continue next time browsing from here if(nearest_emerged_d != -1){ @@ -871,7 +878,7 @@ void RemoteClient::SentBlock(v3s16 p) void RemoteClient::SetBlockNotSent(v3s16 p) { m_nearest_unsent_d = 0; - + if(m_blocks_sending.find(p) != NULL) m_blocks_sending.remove(p); if(m_blocks_sent.find(p) != NULL) @@ -881,7 +888,7 @@ void RemoteClient::SetBlockNotSent(v3s16 p) void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks) { m_nearest_unsent_d = 0; - + for(core::map<v3s16, MapBlock*>::Iterator i = blocks.getIterator(); i.atEnd()==false; i++) @@ -937,6 +944,8 @@ Server::Server( m_rollback(NULL), m_rollback_sink_enabled(true), m_enable_rollback_recording(false), + m_emerge(NULL), + m_biomedef(NULL), m_lua(NULL), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), @@ -955,7 +964,7 @@ Server::Server( m_objectdata_timer = 0.0; m_emergethread_trigger_timer = 0.0; m_savemap_timer = 0.0; - + m_env_mutex.Init(); m_con_mutex.Init(); m_step_dtime_mutex.Init(); @@ -963,10 +972,10 @@ Server::Server( if(path_world == "") throw ServerError("Supplied empty world path"); - + if(!gamespec.isValid()) throw ServerError("Supplied invalid gamespec"); - + infostream<<"Server created for gameid \""<<m_gamespec.id<<"\""; if(m_simple_singleplayer_mode) infostream<<" in simple singleplayer mode"<<std::endl; @@ -976,37 +985,72 @@ Server::Server( infostream<<"- config: "<<m_path_config<<std::endl; infostream<<"- game: "<<m_gamespec.path<<std::endl; + // Create biome definition manager + m_biomedef = new BiomeDefManager(this); + // Create rollback manager std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt"; m_rollback = createRollbackManager(rollback_path, this); - // Add world mod search path - m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods"); - // Add addon mod search path - for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin(); - i != m_gamespec.mods_paths.end(); i++) - m_modspaths.push_front((*i)); + // Create world if it doesn't exist + if(!initializeWorld(m_path_world, m_gamespec.id)) + throw ServerError("Failed to initialize world"); - // Print out mod search paths - for(core::list<std::string>::Iterator i = m_modspaths.begin(); - i != m_modspaths.end(); i++){ - std::string modspath = *i; - infostream<<"- mods: "<<modspath<<std::endl; + ModConfiguration modconf(m_path_world); + m_mods = modconf.getMods(); + // complain about mods with unsatisfied dependencies + if(!modconf.isConsistent()) + { + errorstream << "The following mods have unsatisfied dependencies: "; + std::list<ModSpec> modlist = modconf.getUnsatisfiedMods(); + for(std::list<ModSpec>::iterator it = modlist.begin(); + it != modlist.end(); ++it) + { + errorstream << (*it).name << " "; + } + errorstream << std::endl; + } + + Settings worldmt_settings; + std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; + worldmt_settings.readConfigFile(worldmt.c_str()); + std::vector<std::string> names = worldmt_settings.getNames(); + std::set<std::string> exclude_mod_names; + std::set<std::string> load_mod_names; + for(std::vector<std::string>::iterator it = names.begin(); + it != names.end(); ++it) + { + std::string name = *it; + if (name.compare(0,9,"load_mod_")==0) + { + if(worldmt_settings.getBool(name)) + load_mod_names.insert(name.substr(9)); + else + exclude_mod_names.insert(name.substr(9)); + } + } + // complain about mods declared to be loaded, but not found + for(std::vector<ModSpec>::iterator it = m_mods.begin(); + it != m_mods.end(); ++it) + load_mod_names.erase((*it).name); + if(!load_mod_names.empty()) + { + errorstream << "The following mods could not be found: "; + for(std::set<std::string>::iterator it = load_mod_names.begin(); + it != load_mod_names.end(); ++it) + errorstream << (*it) << " "; + errorstream << std::endl; } - + // Path to builtin.lua std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua"; - // Create world if it doesn't exist - if(!initializeWorld(m_path_world, m_gamespec.id)) - throw ServerError("Failed to initialize world"); - // Lock environment JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); // Initialize scripting - + infostream<<"Server: Initializing Lua"<<std::endl; m_lua = script_init(); assert(m_lua); @@ -1021,18 +1065,16 @@ Server::Server( <<builtinpath<<std::endl; throw ModError("Failed to load and run "+builtinpath); } - // Find mods in mod search paths - m_mods = getMods(m_modspaths); // Print 'em infostream<<"Server: Loading mods: "; - for(core::list<ModSpec>::Iterator i = m_mods.begin(); + for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++){ const ModSpec &mod = *i; infostream<<mod.name<<" "; } infostream<<std::endl; // Load and run "mod" scripts - for(core::list<ModSpec>::Iterator i = m_mods.begin(); + for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++){ const ModSpec &mod = *i; std::string scriptpath = mod.path + DIR_DELIM + "init.lua"; @@ -1045,23 +1087,31 @@ Server::Server( throw ModError("Failed to load and run "+scriptpath); } } - + // Read Textures and calculate sha1 sums fillMediaCache(); // Apply item aliases in the node definition manager m_nodedef->updateAliases(m_itemdef); + // Add default biomes after nodedef had its aliases added + m_biomedef->addDefaultBiomes(); + // Initialize Environment - - m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua, - this, this); - + ServerMap *servermap = new ServerMap(path_world, this); + m_env = new ServerEnvironment(servermap, m_lua, this, this); + + // Create emerge manager + m_emerge = new EmergeManager(this, m_biomedef, servermap->getMapgenParams()); + + // Give map pointer to the emerge manager + servermap->setEmerge(m_emerge); + // Give environment reference to scripting api scriptapi_add_environment(m_lua, m_env); - + // Register us to receive map edit events - m_env->getMap().addEventReceiver(this); + servermap->addEventReceiver(this); // If file exists, load environment metadata if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt")) @@ -1089,7 +1139,7 @@ Server::~Server() */ { JMutexAutoLock conlock(m_con_mutex); - + std::wstring line = L"*** Server shutting down"; /* @@ -1138,12 +1188,12 @@ Server::~Server() infostream<<"Server: Saving environment metadata"<<std::endl; m_env->saveMeta(m_path_world); } - + /* Stop threads */ stop(); - + /* Delete clients */ @@ -1154,19 +1204,21 @@ Server::~Server() i = m_clients.getIterator(); i.atEnd() == false; i++) { + // Delete client delete i.getNode()->getValue(); } } - + // Delete things in the reverse order of creation delete m_env; delete m_rollback; + delete m_emerge; delete m_event; delete m_itemdef; delete m_nodedef; delete m_craftdef; - + // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<<std::endl; script_deinit(m_lua); @@ -1188,7 +1240,7 @@ void Server::start(unsigned short port) // Stop thread if already running m_thread.stop(); - + // Initialize connection m_con.SetTimeoutMs(30); m_con.Serve(port); @@ -1196,7 +1248,7 @@ void Server::start(unsigned short port) // Start thread m_thread.setRun(true); m_thread.Start(); - + // ASCII art for the win! actionstream <<" .__ __ __ "<<std::endl @@ -1213,7 +1265,7 @@ void Server::start(unsigned short port) void Server::stop() { DSTACK(__FUNCTION_NAME); - + infostream<<"Server: Stopping and waiting threads"<<std::endl; // Stop threads (set run=false first so both start stopping) @@ -1221,7 +1273,7 @@ void Server::stop() m_emergethread.setRun(false); m_thread.stop(); m_emergethread.stop(); - + infostream<<"Server: Threads stopped"<<std::endl; } @@ -1245,28 +1297,28 @@ void Server::step(float dtime) void Server::AsyncRunStep() { DSTACK(__FUNCTION_NAME); - + g_profiler->add("Server::AsyncRunStep (num)", 1); - + float dtime; { JMutexAutoLock lock1(m_step_dtime_mutex); dtime = m_step_dtime; } - + { // Send blocks to clients SendBlocks(dtime); } - + if(dtime < 0.001) return; - + g_profiler->add("Server::AsyncRunStep with dtime (num)", 1); //infostream<<"Server steps "<<dtime<<std::endl; //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl; - + { JMutexAutoLock lock1(m_step_dtime_mutex); m_step_dtime -= dtime; @@ -1278,14 +1330,14 @@ void Server::AsyncRunStep() { m_uptime.set(m_uptime.get() + dtime); } - + { // Process connection's timeouts JMutexAutoLock lock2(m_con_mutex); ScopeProfiler sp(g_profiler, "Server: connection timeout processing"); m_con.RunTimeouts(dtime); } - + { // This has to be called so that the client list gets synced // with the peer list of the connection @@ -1332,7 +1384,7 @@ void Server::AsyncRunStep() ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG); m_env->step(dtime); } - + const float map_timer_and_unload_dtime = 2.92; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { @@ -1342,7 +1394,7 @@ void Server::AsyncRunStep() m_env->getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("server_unload_unused_data_timeout")); } - + /* Do background stuff */ @@ -1389,27 +1441,27 @@ void Server::AsyncRunStep() } } } - + /* Transform liquids */ m_liquid_transform_timer += dtime; if(m_liquid_transform_timer >= 1.00) { m_liquid_transform_timer -= 1.00; - + JMutexAutoLock lock(m_env_mutex); ScopeProfiler sp(g_profiler, "Server: liquid transform"); core::map<v3s16, MapBlock*> modified_blocks; m_env->getMap().transformLiquids(modified_blocks); -#if 0 +#if 0 /* Update lighting */ core::map<v3s16, MapBlock*> lighting_modified_blocks; ServerMap &map = ((ServerMap&)m_env->getMap()); map.updateLighting(modified_blocks, lighting_modified_blocks); - + // Add blocks modified by lighting to modified_blocks for(core::map<v3s16, MapBlock*>::Iterator i = lighting_modified_blocks.getIterator(); @@ -1422,7 +1474,7 @@ void Server::AsyncRunStep() /* Set the modified blocks unsent for all the clients */ - + JMutexAutoLock lock2(m_con_mutex); for(core::map<u16, RemoteClient*>::Iterator @@ -1430,7 +1482,7 @@ void Server::AsyncRunStep() i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); - + if(modified_blocks.size() > 0) { // Remove block from sent history @@ -1448,7 +1500,7 @@ void Server::AsyncRunStep() counter = 0.0; JMutexAutoLock lock2(m_con_mutex); - + if(m_clients.size() != 0) infostream<<"Players:"<<std::endl; for(core::map<u16, RemoteClient*>::Iterator @@ -1511,18 +1563,18 @@ void Server::AsyncRunStep() client->m_known_objects, removed_objects); m_env->getAddedActiveObjects(pos, radius, client->m_known_objects, added_objects); - + // Ignore if nothing happened if(removed_objects.size() == 0 && added_objects.size() == 0) { //infostream<<"active objects: none changed"<<std::endl; continue; } - + std::string data_buffer; char buf[4]; - + // Handle removed objects writeU16((u8*)buf, removed_objects.size()); data_buffer.append(buf, 2); @@ -1537,7 +1589,7 @@ void Server::AsyncRunStep() // Add to data buffer for sending writeU16((u8*)buf, i.getNode()->getKey()); data_buffer.append(buf, 2); - + // Remove from known objects client->m_known_objects.remove(i.getNode()->getKey()); @@ -1555,7 +1607,7 @@ void Server::AsyncRunStep() // Get object u16 id = i.getNode()->getKey(); ServerActiveObject* obj = m_env->getActiveObject(id); - + // Get object type u8 type = ACTIVEOBJECT_TYPE_INVALID; if(obj == NULL) @@ -1569,7 +1621,7 @@ void Server::AsyncRunStep() data_buffer.append(buf, 2); writeU8((u8*)buf, type); data_buffer.append(buf, 1); - + if(obj) data_buffer.append(serializeLongString( obj->getClientInitializationData(client->net_proto_version))); @@ -1619,7 +1671,7 @@ void Server::AsyncRunStep() all_known_objects[id] = true; } } - + m_env->setKnownActiveObjects(whatever); #endif @@ -1644,7 +1696,7 @@ void Server::AsyncRunStep() ActiveObjectMessage aom = m_env->getActiveObjectMessage(); if(aom.id == 0) break; - + core::list<ActiveObjectMessage>* message_list = NULL; core::map<u16, core::list<ActiveObjectMessage>* >::Node *n; n = buffered_messages.find(aom.id); @@ -1659,7 +1711,7 @@ void Server::AsyncRunStep() } message_list->push_back(aom); } - + // Route data to every client for(core::map<u16, RemoteClient*>::Iterator i = m_clients.getIterator(); @@ -1766,7 +1818,7 @@ void Server::AsyncRunStep() while(m_unsent_map_edit_queue.size() != 0) { MapEditEvent* event = m_unsent_map_edit_queue.pop_front(); - + // Players far away from the change are stored here. // Instead of sending the changes, MapBlocks are set not sent // for them. @@ -1818,7 +1870,7 @@ void Server::AsyncRunStep() infostream<<"WARNING: Server: Unknown MapEditEvent " <<((u32)event->type)<<std::endl; } - + /* Set blocks not sent to far players */ @@ -1862,7 +1914,7 @@ void Server::AsyncRunStep() verbosestream<<"Server: MapEditEvents:"<<std::endl; prof.print(verbosestream); } - + } /* @@ -1875,7 +1927,7 @@ void Server::AsyncRunStep() if(counter >= 2.0) { counter = 0.0; - + m_emergethread.trigger(); // Update m_enable_rollback_recording here too @@ -1898,13 +1950,13 @@ void Server::AsyncRunStep() //Ban stuff if(m_banmanager.isModified()) m_banmanager.save(); - + // Save changed parts of map m_env->getMap().save(MOD_STATE_WRITE_NEEDED); // Save players m_env->serializePlayers(m_path_world); - + // Save environment metadata m_env->saveMeta(m_path_world); } @@ -1938,7 +1990,7 @@ void Server::Receive() catch(con::PeerNotFoundException &e) { //NOTE: This is not needed anymore - + // The peer has been disconnected. // Find the associated player and remove it. @@ -1958,9 +2010,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Environment is locked first. JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + ScopeProfiler sp(g_profiler, "Server::ProcessData"); - + try{ Address address = m_con.GetPeerAddress(peer_id); std::string addr_s = address.serializeString(); @@ -1984,7 +2036,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<peer_id<<" not found"<<std::endl; return; } - + std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString(); u8 peer_ser_ver = getClient(peer_id)->serialization_version; @@ -1996,7 +2048,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; ToServerCommand command = (ToServerCommand)readU16(&data[0]); - + if(command == TOSERVER_INIT) { // [0] u16 TOSERVER_INIT @@ -2022,7 +2074,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) //peer->serialization_version = deployed; getClient(peer_id)->pending_serialization_version = deployed; - + if(deployed == SER_FMT_VER_INVALID) { actionstream<<"Server: A mismatched client tried to connect from " @@ -2037,7 +2089,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ); return; } - + /* Read and check network protocol version */ @@ -2093,7 +2145,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ); return; } - + if(g_settings->getBool("strict_protocol_version_checking")) { if(net_proto_version != LATEST_PROTOCOL_VERSION) @@ -2118,7 +2170,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Set up player */ - + // Get player name char playername[PLAYERNAME_SIZE]; for(u32 i=0; i<PLAYERNAME_SIZE-1; i++) @@ -2126,7 +2178,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) playername[i] = data[3+i]; } playername[PLAYERNAME_SIZE-1] = 0; - + if(playername[0]=='\0') { actionstream<<"Server: Player with an empty name " @@ -2170,10 +2222,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendAccessDenied(m_con, peer_id, L"Invalid password hash"); return; } - + std::string checkpwd; // Password hash to check against bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); - + // If no authentication info exists for user, create it if(!has_auth){ if(!isSingleplayer() && @@ -2194,7 +2246,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) scriptapi_create_auth(m_lua, playername, initial_password); } - + has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); if(!has_auth){ @@ -2219,7 +2271,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) L"Running in simple singleplayer mode."); return; } - + // Enforce user limit. // Don't enforce for users that have some admin right if(m_clients.size() >= g_settings->getU16("max_users") && @@ -2257,7 +2309,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS)); writeU64(&reply[2+1+6], m_env->getServerMap().getSeed()); writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step")); - + // Send as reliable m_con.Send(peer_id, 0, reply, true); } @@ -2295,16 +2347,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send item definitions SendItemDef(m_con, peer_id, m_itemdef); - + // Send node definitions SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); - + // Send media announcement sendMediaAnnouncement(peer_id); - + // Send privileges SendPlayerPrivileges(peer_id); - + // Send inventory formspec SendPlayerInventoryFormspec(peer_id); @@ -2315,10 +2367,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send HP if(g_settings->getBool("enable_damage")) SendPlayerHP(peer_id); - + // Send detached inventories sendDetachedInventories(peer_id); - + // Show death screen if necessary if(player->hp == 0) SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); @@ -2329,20 +2381,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_env->getTimeOfDay(), g_settings->getFloat("time_speed")); m_con.Send(peer_id, 0, data, true); } - + // Note things in chat if not in simple singleplayer mode if(!m_simple_singleplayer_mode) { // Send information about server to player in chat SendChatMessage(peer_id, getStatusString()); - + // Send information about joining in chat { std::wstring name = L"unknown"; Player *player = m_env->getPlayer(peer_id); if(player != NULL) name = narrow_to_wide(player->getName()); - + std::wstring message; message += L"*** "; message += name; @@ -2350,7 +2402,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) BroadcastChatMessage(message); } } - + // Warnings about protocol version can be issued here if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION) { @@ -2393,7 +2445,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) " Skipping incoming command="<<command<<std::endl; return; } - + Player *player = m_env->getPlayer(peer_id); if(player == NULL){ infostream<<"Server::ProcessData(): Cancelling: " @@ -2414,7 +2466,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { if(datasize < 2+12+12+4+4) return; - + u32 start = 0; v3s32 ps = readV3S32(&data[start+2]); v3s32 ss = readV3S32(&data[start+2+12]); @@ -2442,7 +2494,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) player->control.sneak = (bool)(keyPressed&64); player->control.LMB = (bool)(keyPressed&128); player->control.RMB = (bool)(keyPressed&256); - + /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to " <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")" <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/ @@ -2451,7 +2503,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { if(datasize < 2+1) return; - + /* [0] u16 command [2] u8 count @@ -2477,7 +2529,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { if(datasize < 2+1) return; - + /* [0] u16 command [2] u8 count @@ -2658,7 +2710,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } } - + // Do the action a->apply(this, playersao, this); // Eat the action @@ -2674,11 +2726,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) u8 buf[6]; std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - + // Read stuff is.read((char*)buf, 2); u16 len = readU16(buf); - + std::wstring message; for(u16 i=0; i<len; i++) { @@ -2692,21 +2744,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Get player name of this client std::wstring name = narrow_to_wide(player->getName()); - + // Run script hook bool ate = scriptapi_on_chat_message(m_lua, player->getName(), wide_to_narrow(message)); // If script ate the message, don't proceed if(ate) return; - + // Line to send to players std::wstring line; // Whether to send to the player that sent the line bool send_to_sender = false; // Whether to send to other players bool send_to_others = false; - + // Commands are implemented in Lua, so only catch invalid // commands that were not "eaten" and send an error back if(message[0] == L'/') @@ -2731,7 +2783,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) send_to_sender = true; } } - + if(line != L"") { if(send_to_others) @@ -2858,9 +2910,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { if(player->hp != 0 || !g_settings->getBool("enable_damage")) return; - + RespawnPlayer(peer_id); - + actionstream<<player->getName()<<" respawns at " <<PP(player->getPosition()/BS)<<std::endl; @@ -2870,7 +2922,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else if(command == TOSERVER_REQUEST_MEDIA) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - + core::list<MediaRequest> tosend; u16 numfiles = readU16(is); @@ -3164,7 +3216,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } } } // action == 2 - + /* 3: place block or right-click object */ @@ -3200,7 +3252,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Apply returned ItemStack playersao->setWieldedItem(item); } - + // If item has node placement prediction, always send the above // node to make sure the client knows what exactly happened if(item.getDefinition(m_itemdef).node_placement_prediction != ""){ @@ -3261,7 +3313,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - + v3s16 p = readV3S16(is); std::string formname = deSerializeString(is); int num = readU16(is); @@ -3294,7 +3346,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - + std::string formname = deSerializeString(is); int num = readU16(is); std::map<std::string, std::string> fields; @@ -3311,7 +3363,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"Server::ProcessData(): Ignoring " "unknown command "<<command<<std::endl; } - + } //try catch(SendFailedException &e) { @@ -3397,7 +3449,7 @@ void Server::setInventoryModified(const InventoryLocation &loc) MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); if(block) block->raiseModified(MOD_STATE_WRITE_NEEDED); - + setBlockNotSent(blockpos); } break; @@ -3416,11 +3468,11 @@ core::list<PlayerInfo> Server::getPlayerInfo() DSTACK(__FUNCTION_NAME); JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + core::list<PlayerInfo> list; core::list<Player*> players = m_env->getPlayers(); - + core::list<Player*>::Iterator i; for(i = players.begin(); i != players.end(); i++) @@ -3458,7 +3510,7 @@ void Server::peerAdded(con::Peer *peer) DSTACK(__FUNCTION_NAME); verbosestream<<"Server::peerAdded(): peer->id=" <<peer->id<<std::endl; - + PeerChange c; c.type = PEER_ADDED; c.peer_id = peer->id; @@ -3471,7 +3523,7 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) DSTACK(__FUNCTION_NAME); verbosestream<<"Server::deletingPeer(): peer->id=" <<peer->id<<", timeout="<<timeout<<std::endl; - + PeerChange c; c.type = PEER_REMOVED; c.peer_id = peer->id; @@ -3592,7 +3644,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id, void Server::SendInventory(u16 peer_id) { DSTACK(__FUNCTION_NAME); - + PlayerSAO *playersao = getPlayerSAO(peer_id); assert(playersao); @@ -3606,11 +3658,11 @@ void Server::SendInventory(u16 peer_id) playersao->getInventory()->serialize(os); std::string s = os.str(); - + SharedBuffer<u8> data(s.size()+2); writeU16(&data[0], TOCLIENT_INVENTORY); memcpy(&data[2], s.c_str(), s.size()); - + // Send as reliable m_con.Send(peer_id, 0, data, true); } @@ -3618,18 +3670,18 @@ void Server::SendInventory(u16 peer_id) void Server::SendChatMessage(u16 peer_id, const std::wstring &message) { DSTACK(__FUNCTION_NAME); - + std::ostringstream os(std::ios_base::binary); u8 buf[12]; - + // Write command writeU16(buf, TOCLIENT_CHAT_MESSAGE); os.write((char*)buf, 2); - + // Write length writeU16(buf, message.size()); os.write((char*)buf, 2); - + // Write string for(u32 i=0; i<message.size(); i++) { @@ -3637,7 +3689,7 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message) writeU16(buf, w); os.write((char*)buf, 2); } - + // Make data buffer std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); @@ -3700,7 +3752,7 @@ void Server::SendMovePlayer(u16 peer_id) writeV3F1000(os, player->getPosition()); writeF1000(os, player->getPitch()); writeF1000(os, player->getYaw()); - + { v3f pos = player->getPosition(); f32 pitch = player->getPitch(); @@ -3728,7 +3780,7 @@ void Server::SendPlayerPrivileges(u16 peer_id) std::set<std::string> privs; scriptapi_get_auth(m_lua, player->getName(), NULL, &privs); - + std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PRIVILEGES); writeU16(os, privs.size()); @@ -3890,7 +3942,7 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, // Don't send if it's the same one if(client->peer_id == ignore_id) continue; - + if(far_players) { // Get player @@ -3978,7 +4030,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) DSTACK(__FUNCTION_NAME); v3s16 p = block->getPos(); - + #if 0 // Analyze it a bit bool completely_air = true; @@ -4003,7 +4055,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) /* Create a packet with the block in the right format */ - + std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false); std::string s = os.str(); @@ -4019,7 +4071,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")" <<": \tpacket size: "<<replysize<<std::endl;*/ - + /* Send packet */ @@ -4038,7 +4090,7 @@ void Server::SendBlocks(float dtime) core::array<PrioritySortedBlockTransfer> queue; s32 total_sending = 0; - + { ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); @@ -4055,10 +4107,10 @@ void Server::SendBlocks(float dtime) continue; total_sending += client->SendingCount(); - + if(client->serialization_version == SER_FMT_VER_INVALID) continue; - + client->GetNextBlocks(this, dtime, queue); } } @@ -4074,7 +4126,7 @@ void Server::SendBlocks(float dtime) if(total_sending >= g_settings->getS32 ("max_simultaneous_block_sends_server_total")) break; - + PrioritySortedBlockTransfer q = queue[i]; MapBlock *block = NULL; @@ -4102,10 +4154,10 @@ void Server::fillMediaCache() DSTACK(__FUNCTION_NAME); infostream<<"Server: Calculating media file checksums"<<std::endl; - + // Collect all media file paths std::list<std::string> paths; - for(core::list<ModSpec>::Iterator i = m_mods.begin(); + for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++){ const ModSpec &mod = *i; paths.push_back(mod.path + DIR_DELIM + "textures"); @@ -4115,7 +4167,7 @@ void Server::fillMediaCache() } std::string path_all = "textures"; paths.push_back(path_all + DIR_DELIM + "all"); - + // Collect media file information from paths into cache for(std::list<std::string>::iterator i = paths.begin(); i != paths.end(); i++) @@ -4235,7 +4287,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) string sha1_digest } */ - + writeU16(os, TOCLIENT_ANNOUNCE_MEDIA); writeU16(os, file_announcements.size()); @@ -4436,7 +4488,7 @@ void Server::sendDetachedInventories(u16 peer_id) void Server::DiePlayer(u16 peer_id) { DSTACK(__FUNCTION_NAME); - + PlayerSAO *playersao = getPlayerSAO(peer_id); assert(playersao); @@ -4476,7 +4528,7 @@ void Server::RespawnPlayer(u16 peer_id) void Server::UpdateCrafting(u16 peer_id) { DSTACK(__FUNCTION_NAME); - + Player* player = m_env->getPlayer(peer_id); assert(player); @@ -4672,7 +4724,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions, ServerMap *map = (ServerMap*)(&m_env->getMap()); // Disable rollback report sink while reverting BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false); - + // Fail if no actions to handle if(actions.empty()){ log->push_back("Nothing to do."); @@ -4681,7 +4733,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions, int num_tried = 0; int num_failed = 0; - + for(std::list<RollbackAction>::const_iterator i = actions.begin(); i != actions.end(); i++) @@ -4704,7 +4756,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions, log->push_back(os.str()); } } - + infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried <<" failed"<<std::endl; @@ -4770,7 +4822,7 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager() const ModSpec* Server::getModSpec(const std::string &modname) { - for(core::list<ModSpec>::Iterator i = m_mods.begin(); + for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++){ const ModSpec &mod = *i; if(mod.name == modname) @@ -4780,7 +4832,7 @@ const ModSpec* Server::getModSpec(const std::string &modname) } void Server::getModNames(core::list<std::string> &modlist) { - for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++) + for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++) { modlist.push_back((*i).name); } @@ -4795,13 +4847,15 @@ v3f findSpawnPos(ServerMap &map) //return v3f(50,50,50)*BS; v3s16 nodepos; - + #if 0 nodepos = v2s16(0,0); groundheight = 20; #endif #if 1 + s16 water_level = map.m_mgparams->water_level; + // Try to find a good place a few times for(s32 i=0; i<1000; i++) { @@ -4813,18 +4867,18 @@ v3f findSpawnPos(ServerMap &map) // Get ground height at point (fallbacks to heightmap function) s16 groundheight = map.findGroundLevel(nodepos2d); // Don't go underwater - if(groundheight < WATER_LEVEL) + if(groundheight <= water_level) { //infostream<<"-> Underwater"<<std::endl; continue; } // Don't go to high places - if(groundheight > WATER_LEVEL + 4) + if(groundheight > water_level + 6) { //infostream<<"-> Underwater"<<std::endl; continue; } - + nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y); bool is_good = false; s32 air_count = 0; @@ -4849,7 +4903,7 @@ v3f findSpawnPos(ServerMap &map) } } #endif - + return intToFloat(nodepos, BS); } @@ -4922,7 +4976,7 @@ void Server::handlePeerChange(PeerChange &c) { JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + if(c.type == PEER_ADDED) { /* @@ -4952,7 +5006,7 @@ void Server::handlePeerChange(PeerChange &c) n = m_clients.find(c.peer_id); // The client should exist assert(n != NULL); - + /* Mark objects to be not known by the client */ @@ -4965,7 +5019,7 @@ void Server::handlePeerChange(PeerChange &c) // Get object u16 id = i.getNode()->getKey(); ServerActiveObject* obj = m_env->getActiveObject(id); - + if(obj && obj->m_known_by_count > 0) obj->m_known_by_count--; } @@ -5000,7 +5054,7 @@ void Server::handlePeerChange(PeerChange &c) message += L" (timed out)"; } } - + /* Run scripts and remove from environment */ { if(player != NULL) @@ -5043,18 +5097,18 @@ void Server::handlePeerChange(PeerChange &c) <<os.str()<<std::endl; } } - + // Delete client delete m_clients[c.peer_id]; m_clients.remove(c.peer_id); // Send player info to all remaining clients //SendPlayerInfos(); - + // Send leave chat message to all remaining clients if(message.length() != 0) BroadcastChatMessage(message); - + } // PEER_REMOVED else { @@ -5079,7 +5133,7 @@ void Server::handlePeerChanges() void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - + verbosestream<<"dedicated_server_loop()"<<std::endl; IntervalLimiter m_profiler_interval; |