X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fserver.cpp;h=1e644ce6ce3a672a5f4d414f63409a66940e0fad;hb=37b7f094e3ea502339794f64e8bad22444c6fb54;hp=60bf422f7fbba4d8422f98bc9fccb429326809a3;hpb=320a00e7c65c43c2aa64d1da6e855428101c4aa6;p=oweals%2Fminetest.git diff --git a/src/server.cpp b/src/server.cpp index 60bf422f7..1e644ce6c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -22,19 +22,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "clientserver.h" +#include "ban.h" +#include "environment.h" #include "map.h" -#include "jmutexautolock.h" +#include "jthread/jmutexautolock.h" #include "main.h" #include "constants.h" #include "voxel.h" #include "config.h" +#include "version.h" #include "filesys.h" #include "mapblock.h" #include "serverobject.h" +#include "genericobject.h" #include "settings.h" #include "profiler.h" #include "log.h" -#include "script/cpp_api/scriptapi.h" +#include "scripting_game.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -58,30 +62,54 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" +#include "util/thread.h" #include "defaultsettings.h" -void * ServerThread::Thread() +class ClientNotFoundException : public BaseException { - ThreadStarted(); +public: + ClientNotFoundException(const char *s): + BaseException(s) + {} +}; + +class ServerThread : public JThread +{ + Server *m_server; + +public: + + ServerThread(Server *server): + JThread(), + m_server(server) + { + } + void * Thread(); +}; + +void * ServerThread::Thread() +{ log_register_thread("ServerThread"); DSTACK(__FUNCTION_NAME); - BEGIN_DEBUG_EXCEPTION_HANDLER - while(getRun()) + m_server->AsyncRunStep(true); + + ThreadStarted(); + + porting::setThreadName("ServerThread"); + + while(!StopRequested()) { try{ //TimeTaker timer("AsyncRunStep() + Receive()"); - { - //TimeTaker timer("AsyncRunStep()"); - m_server->AsyncRunStep(); - } + m_server->AsyncRunStep(); - //infostream<<"Running m_server->Receive()"<Receive(); + } catch(con::NoIncomingDataException &e) { @@ -90,6 +118,9 @@ void * ServerThread::Thread() { infostream<<"Server: PeerNotFoundException"<setAsyncFatalError(e.what()); @@ -126,502 +157,7 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const return v3f(0,0,0); } -void RemoteClient::GetNextBlocks(Server *server, float dtime, - std::vector &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; - - Player *player = server->m_env->getPlayer(peer_id); - // This can happen sometimes; clients and players are not in perfect sync. - if(player == NULL) - return; - - // Won't send anything if already sending - if(m_blocks_sending.size() >= g_settings->getU16 - ("max_simultaneous_block_sends_per_client")) - { - //infostream<<"Not sending any blocks, Queue full."<getPosition(); - v3f playerspeed = player->getSpeed(); - v3f playerspeeddir(0,0,0); - if(playerspeed.getLength() > 1.0*BS) - playerspeeddir = playerspeed / playerspeed.getLength(); - // Predict to next block - v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - - 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); - camera_dir.rotateYZBy(player->getPitch()); - camera_dir.rotateXZBy(player->getYaw()); - - /*infostream<<"camera_dir=("<getPlayerName(peer_id)<getFloat( - "full_block_send_enable_min_time_from_building")) - { - 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. - - This is because not necessarily any of the blocks found this - time are actually sent. - */ - s32 new_nearest_unsent_d = -1; - - 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 "<getPlayerName(peer_id)< list; - getFacePositions(list, d); - - std::list::iterator li; - for(li=list.begin(); li!=list.end(); ++li) - { - v3s16 p = *li + center; - - /* - Send throttling - - Don't allow too many simultaneous transfers - - EXCEPT when the blocks are very close - - 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; - - // Don't select too many blocks for sending - if(num_blocks_selected >= max_simul_dynamic) - { - queue_is_full = true; - goto queue_full_break; - } - - // Don't send blocks that are currently being transferred - if(m_blocks_sending.find(p) != m_blocks_sending.end()) - continue; - - /* - Do not go over-limit - */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || 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) - generate = false;*/ - - // Limit the send area vertically to 1/2 - if(abs(p.Y - center.Y) > d_max / 2) - continue; - } - -#if 0 - /* - If block is far away, don't generate it unless it is - near ground level. - */ - if(d >= 4) - { - #if 1 - // Block center y in nodes - f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2); - // Don't generate if it's very high or very low - if(y < -64 || y > 64) - generate = false; - #endif - #if 0 - 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); - - // If differs a lot, don't generate - if(fabs(gh - y) > MAP_BLOCKSIZE*2) - generate = false; - // Actually, don't even send it - //continue; - #endif - } -#endif - - //infostream<<"d="<m_env->getMap().getBlockNoCreateNoEx(p); - - bool surely_not_found_on_disk = false; - bool block_is_invalid = false; - if(block != NULL) - { - // Reset usage timer, this block will be of use in the future. - block->resetUsageTimer(); - - // Block is dummy if data doesn't exist. - // It means it has been not found from disk and not generated - if(block->isDummy()) - { - 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; - }*/ - -#if 0 - v2s16 p2d(p.X, p.Z); - ServerMap *map = (ServerMap*)(&server->m_env->getMap()); - v2s16 chunkpos = map->sector_to_chunk(p2d); - if(map->chunkNonVolatile(chunkpos) == false) - block_is_invalid = true; -#endif - if(block->isGenerated() == false) - block_is_invalid = true; -#if 1 - /* - If block is not close, don't send it unless it is near - ground level. - - Block is near ground level if night-time mesh - differs from day-time mesh. - */ - if(d >= 4) - { - if(block->getDayNightDiff() == false) - continue; - } -#endif - } - - /* - If block has been marked to not exist on disk (dummy) - and generating new ones is not wanted, skip block. - */ - if(generate == false && surely_not_found_on_disk == true) - { - // get next one. - continue; - } - /* - Add inexistent block to emerge queue. - */ - if(block == NULL || surely_not_found_on_disk || block_is_invalid) - { - /* //TODO: Get value from somewhere - // Allow only one block in emerge queue - //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) - // Allow two blocks in queue per client - //if(server->m_emerge_queue.peerItemCount(peer_id) < 2) - u32 max_emerge = 5; - // Make it more responsive when needing to generate stuff - if(surely_not_found_on_disk) - max_emerge = 1; - if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge) - { - //infostream<<"Adding block to emerge queue"<m_emerge_queue.addBlock(peer_id, p, flags); - server->m_emergethread.trigger(); - - if(nearest_emerged_d == -1) - nearest_emerged_d = d; - } else { - if(nearest_emergefull_d == -1) - nearest_emergefull_d = d; - goto queue_full_break; - } - */ - - if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) { - if (nearest_emerged_d == -1) - nearest_emerged_d = d; - } else { - if (nearest_emergefull_d == -1) - nearest_emergefull_d = d; - goto queue_full_break; - } - - // get next one. - continue; - } - - if(nearest_sent_d == -1) - nearest_sent_d = d; - - /* - Add block to send queue - */ - - /*errorstream<<"sending from d="<getPlayerName(peer_id)< g_settings->getS16("max_block_send_distance")){ - new_nearest_unsent_d = 0; - m_nothing_to_send_pause_timer = 2.0; - /*infostream<<"GetNextBlocks(): d wrapped around for " - <getPlayerName(peer_id) - <<"; setting to 0 and pausing"< &blocks) -{ - m_nearest_unsent_d = 0; - - for(std::map::iterator - i = blocks.begin(); - i != blocks.end(); ++i) - { - v3s16 p = i->first; - - if(m_blocks_sending.find(p) != m_blocks_sending.end()) - m_blocks_sending.erase(p); - if(m_blocks_sent.find(p) != m_blocks_sent.end()) - m_blocks_sent.erase(p); - } -} - -/* - PlayerInfo -*/ - -PlayerInfo::PlayerInfo() -{ - name[0] = 0; - avg_rtt = 0; -} - -void PlayerInfo::PrintLine(std::ostream *s) -{ - (*s)<getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this), - m_banmanager(path_world+DIR_DELIM+"ipban.txt"), + m_con(PROTOCOL_ID, + 512, + CONNECTION_TIMEOUT, + ipv6, + this), + m_banmanager(NULL), m_rollback(NULL), m_rollback_sink_enabled(true), m_enable_rollback_recording(false), @@ -651,12 +189,14 @@ Server::Server( m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), m_event(new EventManager()), - m_thread(this), + m_thread(NULL), m_time_of_day_send_timer(0), m_uptime(0), + m_clients(&m_con), m_shutdown_requested(false), m_ignore_map_edit_events(false), m_ignore_map_edit_events_peer_id(0) + { m_liquid_transform_timer = 0.0; m_liquid_transform_every = 1.0; @@ -665,12 +205,9 @@ Server::Server( m_objectdata_timer = 0.0; m_emergethread_trigger_timer = 0.0; m_savemap_timer = 0.0; - m_clients_number = 0; - m_env_mutex.Init(); - m_con_mutex.Init(); - m_step_dtime_mutex.Init(); m_step_dtime = 0.0; + m_lag = g_settings->getFloat("dedicated_server_step"); if(path_world == "") throw ServerError("Supplied empty world path"); @@ -684,7 +221,6 @@ Server::Server( else infostream< unsatisfied_mods = modconf.getUnsatisfiedMods(); // complain about mods with unsatisfied dependencies - if(!modconf.isConsistent()) + if(!modconf.isConsistent()) { for(std::vector::iterator it = unsatisfied_mods.begin(); it != unsatisfied_mods.end(); ++it) @@ -728,10 +271,10 @@ Server::Server( worldmt_settings.readConfigFile(worldmt.c_str()); std::vector names = worldmt_settings.getNames(); std::set load_mod_names; - for(std::vector::iterator it = names.begin(); + for(std::vector::iterator it = names.begin(); it != names.end(); ++it) - { - std::string name = *it; + { + std::string name = *it; if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name)) load_mod_names.insert(name.substr(9)); } @@ -743,7 +286,7 @@ Server::Server( it != unsatisfied_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::iterator it = load_mod_names.begin(); it != load_mod_names.end(); ++it) @@ -751,29 +294,21 @@ Server::Server( errorstream << std::endl; } - // Path to builtin.lua - std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua"; - // Lock environment JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); // Initialize scripting - infostream<<"Server: Initializing Lua"<loadMod(builtinpath, "__builtin"); - if(!success){ - errorstream<<"Server: Failed to load and run " - <loadScript(scriptpath)) { + throw ModError("Failed to load and run " + scriptpath); } + + // Print 'em infostream<<"Server: Loading mods: "; for(std::vector::iterator i = m_mods.begin(); @@ -803,16 +338,21 @@ Server::Server( // Apply item aliases in the node definition manager m_nodedef->updateAliases(m_itemdef); + // Load the mapgen params from global settings now after any + // initial overrides have been set by the mods + m_emerge->loadMapgenParams(); + // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); - m_env = new ServerEnvironment(servermap, m_script, this, this); - + m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + + m_clients.setEnv(m_env); + // Run some callbacks after the MG params have been set up but before activation - MapgenParams *mgparams = servermap->getMapgenParams(); - m_script->environment_OnMapgenInit(mgparams); - + m_script->environment_OnMapgenInit(&m_emerge->params); + // Initialize mapgens - m_emerge->initMapgens(mgparams); + m_emerge->initMapgens(); // Give environment reference to scripting api m_script->initializeEnvironment(m_env); @@ -821,19 +361,13 @@ Server::Server( servermap->addEventReceiver(this); // If file exists, load environment metadata - if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt")) + if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) { infostream<<"Server: Loading environment metadata"<loadMeta(m_path_world); + m_env->loadMeta(); } - // Load players - infostream<<"Server: Loading players"<deSerializePlayers(m_path_world); - - /* - Add some test ActiveBlockModifiers to environment - */ + // Add some test ActiveBlockModifiers to environment add_legacy_abms(m_env, m_nodedef); m_liquid_transform_every = g_settings->getFloat("liquid_update"); @@ -843,88 +377,38 @@ Server::~Server() { infostream<<"Server destructing"<::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - try{ - SendChatMessage(client->peer_id, line); - } - catch(con::PeerNotFoundException &e) - {} - } - } + // Send shutdown message + SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down"); { JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - /* - Execute script shutdown hooks - */ + // Execute script shutdown hooks m_script->on_shutdown(); - } - { - JMutexAutoLock envlock(m_env_mutex); - - /* - Save players - */ infostream<<"Server: Saving players"<serializePlayers(m_path_world); + m_env->saveLoadedPlayers(); - /* - Save environment metadata - */ infostream<<"Server: Saving environment metadata"<saveMeta(m_path_world); + m_env->saveMeta(); } - /* - Stop threads - */ + // Stop threads stop(); + delete m_thread; - //shutdown all emerge threads first! - delete m_emerge; - - /* - Delete clients - */ - { - JMutexAutoLock clientslock(m_con_mutex); - - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - - // Delete client - delete i->second; - } - } + // stop all emerge threads before deleting players that may have + // requested blocks to be emerged + m_emerge->stopThreads(); // Delete things in the reverse order of creation delete m_env; + + // N.B. the EmergeManager should be deleted after the Environment since Map + // depends on EmergeManager to write its current params to the map meta + delete m_emerge; delete m_rollback; + delete m_banmanager; delete m_event; delete m_itemdef; delete m_nodedef; @@ -935,30 +419,28 @@ Server::~Server() delete m_script; // Delete detached inventories - { - for(std::map::iterator - i = m_detached_inventories.begin(); - i != m_detached_inventories.end(); i++){ - delete i->second; - } + for (std::map::iterator + i = m_detached_inventories.begin(); + i != m_detached_inventories.end(); i++) { + delete i->second; } } -void Server::start(unsigned short port) +void Server::start(Address bind_addr) { DSTACK(__FUNCTION_NAME); - infostream<<"Starting server on port "<Stop(); // Initialize connection m_con.SetTimeoutMs(30); - m_con.Serve(port); + m_con.Serve(bind_addr); // Start thread - m_thread.setRun(true); - m_thread.Start(); + m_thread->Start(); // ASCII art for the win! actionstream @@ -970,7 +452,8 @@ void Server::start(unsigned short port) <<" \\/ \\/ \\/ \\/ \\/ "<Stop(); //m_emergethread.setRun(false); - m_thread.stop(); + m_thread->Wait(); //m_emergethread.stop(); infostream<<"Server: Threads stopped"<add("Server::AsyncRunStep with dtime (num)", 1); @@ -1042,18 +525,7 @@ 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 - handlePeerChanges(); - } + handlePeerChanges(); /* Update time of day and overall game time @@ -1071,25 +543,24 @@ void Server::AsyncRunStep() if(m_time_of_day_send_timer < 0.0) { m_time_of_day_send_timer = g_settings->getFloat("time_send_interval"); - - //JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - RemoteClient *client = i->second; - SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_env->getTimeOfDay(), g_settings->getFloat("time_speed")); - // Send as reliable - m_con.Send(client->peer_id, 0, data, true); - } + u16 time = m_env->getTimeOfDay(); + float time_speed = g_settings->getFloat("time_speed"); + SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed); } } { JMutexAutoLock lock(m_env_mutex); + // Figure out and report maximum lag to environment + float max_lag = m_env->getMaxLagEstimate(); + max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes) + if(dtime > max_lag){ + if(dtime > 0.1 && dtime > max_lag * 2.0) + infostream<<"Server: Maximum lag peaked to "<reportMaxLagEstimate(max_lag); // Step environment ScopeProfiler sp(g_profiler, "SEnv step"); ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG); @@ -1115,16 +586,16 @@ void Server::AsyncRunStep() */ { JMutexAutoLock lock(m_env_mutex); - JMutexAutoLock lock2(m_con_mutex); + + std::list clientids = m_clients.getClientIDs(); ScopeProfiler sp(g_profiler, "Server: handle players"); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + for(std::list::iterator + i = clientids.begin(); + i != clientids.end(); ++i) { - RemoteClient *client = i->second; - PlayerSAO *playersao = getPlayerSAO(client->peer_id); + PlayerSAO *playersao = getPlayerSAO(*i); if(playersao == NULL) continue; @@ -1134,21 +605,28 @@ void Server::AsyncRunStep() if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage")) { if(playersao->getHP() == 0) - DiePlayer(client->peer_id); + DiePlayer(*i); else - SendPlayerHP(client->peer_id); + SendPlayerHP(*i); + } + + /* + Send player breath if changed + */ + if(playersao->m_breath_not_sent) { + SendPlayerBreath(*i); } /* Send player inventories if necessary */ if(playersao->m_moved){ - SendMovePlayer(client->peer_id); + SendMovePlayer(*i); playersao->m_moved = false; } if(playersao->m_inventory_not_sent){ - UpdateCrafting(client->peer_id); - SendInventory(client->peer_id); + UpdateCrafting(*i); + SendInventory(*i); } } } @@ -1185,76 +663,43 @@ void Server::AsyncRunStep() /* Set the modified blocks unsent for all the clients */ - - JMutexAutoLock lock2(m_con_mutex); - - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + if(modified_blocks.size() > 0) { - RemoteClient *client = i->second; - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } + SetBlocksNotSent(modified_blocks); } } + m_clients.step(dtime); - // Periodically print some info - { - float &counter = m_print_info_timer; - counter += dtime; - if(counter >= 30.0) - { - counter = 0.0; - - JMutexAutoLock lock2(m_con_mutex); - m_clients_number = 0; - if(m_clients.size() != 0) - infostream<<"Players:"<::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - //u16 peer_id = i.getNode()->getKey(); - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); - if(player==NULL) - continue; - infostream<<"* "<getName()<<"\t"; - client->PrintInfo(infostream); - ++m_clients_number; - } - } - } - - + m_lag += (m_lag > dtime ? -1 : 1) * dtime/100; #if USE_CURL // send masterserver announce { float &counter = m_masterserver_timer; - if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true) + if(!isSingleplayer() && (!counter || counter >= 300.0) && + g_settings->getBool("server_announce")) { - ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id); + ServerList::sendAnnounce(counter ? "update" : "start", + m_clients.getPlayerNames(), + m_uptime.get(), + m_env->getGameTime(), + m_lag, + m_gamespec.id, + m_mods); counter = 0.01; } counter += dtime; } #endif - //if(g_settings->getBool("enable_experimental")) - { - /* Check added and deleted active objects */ { //infostream<<"Server: Checking added and deleted active objects"< clients = m_clients.getClientList(); ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs"); // Radius inside which objects are active @@ -1262,14 +707,14 @@ void Server::AsyncRunStep() radius *= MAP_BLOCKSIZE; for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; // If definitions and textures have not been sent, don't // send objects either - if(!client->definitions_sent) + if (client->getState() < CS_DefinitionsSent) continue; Player *player = m_env->getPlayer(client->peer_id); @@ -1367,14 +812,14 @@ void Server::AsyncRunStep() memcpy((char*)&reply[2], data_buffer.c_str(), data_buffer.size()); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(client->peer_id, 0, reply, true); verbosestream<<"Server: Sent object remove/add: " <push_back(aom); } + m_clients.Lock(); + std::map clients = m_clients.getClientList(); // Route data to every client for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; std::string reliable_data; @@ -1488,7 +933,7 @@ void Server::AsyncRunStep() memcpy((char*)&reply[2], reliable_data.c_str(), reliable_data.size()); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(client->peer_id, 0, reply, true); } if(unreliable_data.size() > 0) { @@ -1497,7 +942,7 @@ void Server::AsyncRunStep() memcpy((char*)&reply[2], unreliable_data.c_str(), unreliable_data.size()); // Send as unreliable - m_con.Send(client->peer_id, 0, reply, false); + m_clients.send(client->peer_id, 1, reply, false); } /*if(reliable_data.size() > 0 || unreliable_data.size() > 0) @@ -1508,6 +953,7 @@ void Server::AsyncRunStep() <* >::iterator @@ -1518,15 +964,12 @@ void Server::AsyncRunStep() } } - } // enable_experimental - /* Send queued-for-sending map edit events. */ { - // We will be accessing the environment and the connection + // We will be accessing the environment JMutexAutoLock lock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); // Don't send too many at a time //u32 count = 0; @@ -1550,16 +993,16 @@ void Server::AsyncRunStep() // for them. std::list far_players; - if(event->type == MEET_ADDNODE) + if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE) { //infostream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, - &far_players, 5); + &far_players, 5, event->type == MEET_ADDNODE); else sendAddNode(event->p, event->n, event->already_known_by_peer, - &far_players, 30); + &far_players, 30, event->type == MEET_ADDNODE); } else if(event->type == MEET_REMOVENODE) { @@ -1652,8 +1095,7 @@ void Server::AsyncRunStep() { counter = 0.0; - for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++) - m_emerge->emergethread[i]->trigger(); + m_emerge->startThreads(); // Update m_enable_rollback_recording here too m_enable_rollback_recording = @@ -1672,18 +1114,19 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: saving stuff"); - //Ban stuff - if(m_banmanager.isModified()) - m_banmanager.save(); + // Save ban file + 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); + m_env->saveLoadedPlayers(); // Save environment metadata - m_env->saveMeta(m_path_world); + m_env->saveMeta(); } } } @@ -1695,15 +1138,7 @@ void Server::Receive() u16 peer_id; u32 datasize; try{ - { - JMutexAutoLock conlock(m_con_mutex); - datasize = m_con.Receive(peer_id, data); - } - - // This has to be called so that the client list gets synced - // with the peer list of the connection - handlePeerChanges(); - + datasize = m_con.Receive(peer_id,data); ProcessData(*data, datasize, peer_id); } catch(con::InvalidIncomingDataException &e) @@ -1712,21 +1147,121 @@ void Server::Receive() "InvalidIncomingDataException: what()=" <getName(); + playersao = emergePlayer(playername.c_str(), peer_id); + } + m_clients.Unlock(); + + RemotePlayer *player = + static_cast(m_env->getPlayer(playername.c_str())); + + // If failed, cancel + if((playersao == NULL) || (player == NULL)) + { + if(player && player->peer_id != 0){ + errorstream<<"Server: "<getBool("enable_damage")) + SendPlayerHP(peer_id); + + // Send Breath + SendPlayerBreath(peer_id); + + // Show death screen if necessary + if(player->hp == 0) + SendDeathscreen(peer_id, false, v3f(0,0,0)); + + // 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()); - // The peer has been disconnected. - // Find the associated player and remove it. + std::wstring message; + message += L"*** "; + message += name; + message += L" joined the game."; + SendChatMessage(PEER_ID_INEXISTENT,message); + } + } + Address addr = getPeerAddress(player->peer_id); + std::string ip_str = addr.serializeString(); + actionstream<getName() <<" [" << ip_str << "] joins game. " << std::endl; + /* + Print out action + */ + { + std::vector names = m_clients.getPlayerNames(); - /*JMutexAutoLock envlock(m_env_mutex); + actionstream<getName() <<" joins game. List of players: "; - infostream<<"ServerThread: peer_id="<::iterator i = names.begin(); + i != names.end(); i++) + { + actionstream << *i << " "; + } - m_env->removePlayer(peer_id);*/ + actionstream << player->getName() <isIpBanned(addr_s)){ + std::string ban_name = m_banmanager->getBanName(addr_s); infostream<<"Server: A banned client tried to connect from " <serialization_version; - try { @@ -1777,44 +1313,61 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT) { // [0] u16 TOSERVER_INIT - // [2] u8 SER_FMT_VER_HIGHEST + // [2] u8 SER_FMT_VER_HIGHEST_READ // [3] u8[20] player_name // [23] u8[28] password <--- can be sent without this, from old versions if(datasize < 2+1+PLAYERNAME_SIZE) return; - verbosestream<<"Server: Got TOSERVER_INIT from " - <getState() > CS_Created) + { + verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from " + < 1){ + infostream<<"Server: Not allowing another client ("<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 " <setPendingSerializationVersion(deployed); + /* Read and check network protocol version */ @@ -1844,21 +1397,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) net_proto_version = max_net_proto_version; } - verbosestream<<"Server: "<net_proto_version = net_proto_version; + client->net_proto_version = net_proto_version; if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { - actionstream<<"Server: A mismatched client tried to connect from "<on_prejoinplayer(playername, addr_s, reason)) + { + actionstream<<"Server: Player with the name \""<= g_settings->getU16("max_users") && + !checkPriv(playername, "server") && + !checkPriv(playername, "ban") && + !checkPriv(playername, "privs") && + !checkPriv(playername, "password") && + playername != g_settings->get("name")) + { + actionstream<<"Server: "<getBool("disallow_empty_password") && std::string(given_password) == ""){ - SendAccessDenied(m_con, peer_id, L"Empty passwords are " + actionstream<<"Server: "<getAuth(playername, &checkpwd, NULL); if(!has_auth){ - SendAccessDenied(m_con, peer_id, L"Not allowed to login"); + actionstream<<"Server: "< 1){ - infostream<<"Server: Not allowing another client to connect in" - <<" simple singleplayer mode"<(m_env->getPlayer(playername)); - // Enforce user limit. - // Don't enforce for users that have some admin right - if(m_clients.size() >= g_settings->getU16("max_users") && - !checkPriv(playername, "server") && - !checkPriv(playername, "ban") && - !checkPriv(playername, "privs") && - !checkPriv(playername, "password") && - playername != g_settings->get("name")) - { - actionstream<<"Server: "<peer_id != 0){ + errorstream<<"Server: "< reply(2+1+6+8+4); writeU16(&reply[0], TOCLIENT_INIT); writeU8(&reply[2], deployed); - writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS)); + //send dummy pos for legacy reasons only + writeV3S16(&reply[2+1], floatToInt(v3f(0,0,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); + m_clients.send(peer_id, 0, reply, true); + m_clients.event(peer_id, CSE_Init); } - /* - Send complete position information - */ - SendMovePlayer(peer_id); - return; } if(command == TOSERVER_INIT2) { + verbosestream<<"Server: Got TOSERVER_INIT2 from " <getPlayer(peer_id); - if(!player){ - verbosestream<<"Server: TOSERVER_INIT2: " - <<"Player not found; ignoring."<serialization_version = - getClient(peer_id)->pending_serialization_version; + ///// begin compatibility code + PlayerSAO* playersao = NULL; + if (protocol_version <= 22) { + playersao = StageTwoClientInit(peer_id); + + if (playersao == NULL) { + errorstream + << "TOSERVER_INIT2 stage 2 client init failed for peer " + << peer_id << std::endl; + return; + } + } + ///// end compatibility code /* Send some initialization data @@ -2080,65 +1643,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <net_proto_version); + SendItemDef(peer_id, m_itemdef, protocol_version); // Send node definitions - SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); + SendNodeDef(peer_id, m_nodedef, protocol_version); + + m_clients.event(peer_id, CSE_SetDefinitionsSent); // Send media announcement sendMediaAnnouncement(peer_id); - // Send privileges - SendPlayerPrivileges(peer_id); - - // Send inventory formspec - SendPlayerInventoryFormspec(peer_id); - - // Send inventory - UpdateCrafting(peer_id); - SendInventory(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)); - // Send time of day - { - SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - 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; - message += L" joined the game."; - BroadcastChatMessage(message); - } + u16 time = m_env->getTimeOfDay(); + float time_speed = g_settings->getFloat("time_speed"); + SendTimeOfDay(peer_id, time, time_speed); + + ///// begin compatibility code + if (protocol_version <= 22) { + m_clients.event(peer_id, CSE_SetClientReady); + m_script->on_joinplayer(playersao); } + ///// end compatibility code // Warnings about protocol version can be issued here if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION) @@ -2147,55 +1678,131 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!"); } + return; + } + + u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version; + u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version; + + if(peer_ser_ver == SER_FMT_VER_INVALID) + { + errorstream<<"Server::ProcessData(): Cancelling: Peer" + " serialization format invalid or not initialized." + " Skipping incoming command="< tosend; + u16 numfiles = readU16(is); + + infostream<<"Sending "<on_joinplayer(playersao); + + } + else if(command == TOSERVER_GOTBLOCKS) + { + if(datasize < 2+1) + return; + /* - Print out action + [0] u16 command + [2] u8 count + [3] v3s16 pos_0 + [3+6] v3s16 pos_1 + ... */ - { - std::ostringstream os(std::ios_base::binary); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - // Get player - Player *player = m_env->getPlayer(client->peer_id); - if(!player) - continue; - // Get name of player - os<getName()<<" "; - } - actionstream<getName()<<" ["<GotBlock(p); } - return; } - if(peer_ser_ver == SER_FMT_VER_INVALID) + if (m_clients.getClientState(peer_id) < CS_Active) { - infostream<<"Server::ProcessData(): Cancelling: Peer" - " serialization format invalid or not initialized." - " Skipping incoming command="<getPlayer(peer_id); - if(player == NULL){ - infostream<<"Server::ProcessData(): Cancelling: " + if(player == NULL) { + errorstream<<"Server::ProcessData(): Cancelling: " "No player for peer_id="<getPlayerSAO(); - if(playersao == NULL){ - infostream<<"Server::ProcessData(): Cancelling: " + if(playersao == NULL) { + errorstream<<"Server::ProcessData(): Cancelling: " "No player object for peer_id="<control.LMB = (bool)(keyPressed&128); player->control.RMB = (bool)(keyPressed&256); + bool cheated = playersao->checkMovementCheat(); + if(cheated){ + // Call callbacks + m_script->on_cheat(playersao, "moved_too_fast"); + } + /*infostream<<"Server::ProcessData(): Moved player "<GotBlock(p); - } - } else if(command == TOSERVER_DELETEDBLOCKS) { if(datasize < 2+1) @@ -2504,16 +2091,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // 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; + bool send_to_sender_only = false; // Commands are implemented in Lua, so only catch invalid // commands that were not "eaten" and send an error back if(message[0] == L'/') { message = message.substr(1); - send_to_sender = true; + send_to_sender_only = true; if(message.length() == 0) line += L"-!- Empty command"; else @@ -2526,39 +2111,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) line += name; line += L"> "; line += message; - send_to_others = true; } else { line += L"-!- You don't have permission to shout."; - send_to_sender = true; + send_to_sender_only = true; } } if(line != L"") { - if(send_to_others) - actionstream<<"CHAT: "<::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + if (send_to_sender_only) { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; + SendChatMessage(peer_id, line); + } + /* + Send the message to others + */ + else + { + actionstream<<"CHAT: "<peer_id); - if(sender_selected == true && send_to_sender == false) - continue; - if(sender_selected == false && send_to_others == false) - continue; + std::list clients = m_clients.getClientIDs(); - SendChatMessage(client->peer_id, line); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) + { + if (*i != peer_id) + SendChatMessage(*i, line); + } } } } @@ -2583,6 +2166,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerHP(peer_id); } } + else if(command == TOSERVER_BREATH) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u16 breath = readU16(is); + playersao->setBreath(breath); + m_script->player_event(playersao,"breath_changed"); + } else if(command == TOSERVER_PASSWORD) { /* @@ -2656,44 +2247,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) playersao->setWieldIndex(item); } else if(command == TOSERVER_RESPAWN) - { - if(player->hp != 0 || !g_settings->getBool("enable_damage")) - return; - - RespawnPlayer(peer_id); - - actionstream<getName()<<" respawns at " - <getPosition()/BS)< tosend; - u16 numfiles = readU16(is); - - infostream<<"Sending "<hp != 0 || !g_settings->getBool("enable_damage")) + return; - sendRequestedMedia(peer_id, tosend); + RespawnPlayer(peer_id); - // Now the client should know about everything - // (definitions and files) - getClient(peer_id)->definitions_sent = true; - } - else if(command == TOSERVER_RECEIVED_MEDIA) { - getClient(peer_id)->definitions_sent = true; + actionstream<getName()<<" respawns at " + <getPosition()/BS)<SetBlockNotSent(blockpos); + // Call callbacks + m_script->on_cheat(playersao, "interacted_too_far"); // Do nothing else return; } @@ -2841,7 +2407,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false); } if(n.getContent() != CONTENT_IGNORE) - m_script->node_on_punch(p_under, n, playersao); + m_script->node_on_punch(p_under, n, playersao, pointed); // Cheat prevention playersao->noCheatDigStart(p_under); } @@ -2911,6 +2477,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <on_cheat(playersao, "finished_unknown_dig"); } // Get player's wielded item ItemStack playeritem; @@ -2936,15 +2504,33 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<", which is not diggable with tool. not digging." <on_cheat(playersao, "dug_unbreakable"); } - // If time is considerably too short, ignore dig - // Check time only for medium and slow timed digs - if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){ + // Check digging time + // If already invalidated, we don't have to + if(!is_valid_dig){ + // Well not our problem then + } + // Clean and long dig + else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){ + // All is good, but grab time from pool; don't care if + // it's actually available + playersao->getDigPool().grab(params.time); + } + // Short or laggy dig + // Try getting the time from pool + else if(playersao->getDigPool().grab(params.time)){ + // All is good + } + // Dig not possible + else{ infostream<<"Server: NoCheat: "<getName() - <<" completed digging " - <on_cheat(playersao, "dug_too_fast"); } } @@ -3123,6 +2709,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } } +void Server::setTimeOfDay(u32 time) +{ + m_env->setTimeOfDay(time); + m_time_of_day_send_timer = 0; +} + void Server::onMapEditEvent(MapEditEvent *event) { //infostream<<"Server::onMapEditEvent()"< Server::getPlayerInfo() -//{ -// DSTACK(__FUNCTION_NAME); -// JMutexAutoLock envlock(m_env_mutex); -// JMutexAutoLock conlock(m_con_mutex); -// -// std::list list; -// -// std::list players = m_env->getPlayers(); -// -// std::list::iterator i; -// for(i = players.begin(); -// i != players.end(); ++i) -// { -// PlayerInfo info; -// -// Player *player = *i; -// -// try{ -// // Copy info from connection to info struct -// info.id = player->peer_id; -// info.address = m_con.GetPeerAddress(player->peer_id); -// info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); -// } -// catch(con::PeerNotFoundException &e) -// { -// // Set dummy peer info -// info.id = 0; -// info.address = Address(0,0,0,0,0); -// info.avg_rtt = 0.0; -// } -// -// snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName()); -// info.position = player->getPosition(); -// -// list.push_back(info); -// } -// -// return list; -//} - +void Server::SetBlocksNotSent(std::map& block) +{ + std::list clients = m_clients.getClientIDs(); + m_clients.Lock(); + // Set the modified blocks unsent for all the clients + for (std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) { + RemoteClient *client = m_clients.lockedGetClientNoEx(*i); + if (client != NULL) + client->SetBlocksNotSent(block); + } + m_clients.Unlock(); +} void Server::peerAdded(con::Peer *peer) { @@ -3261,8 +2826,8 @@ void Server::peerAdded(con::Peer *peer) verbosestream<<"Server::peerAdded(): peer->id=" <id<id; c.timeout = false; m_peer_change_queue.push_back(c); @@ -3274,18 +2839,84 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) verbosestream<<"Server::deletingPeer(): peer->id=" <id<<", timeout="<id, CSE_Disconnect); + con::PeerChange c; + c.type = con::PEER_REMOVED; c.peer_id = peer->id; c.timeout = timeout; m_peer_change_queue.push_back(c); } -/* - Static send methods -*/ +bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval) +{ + *retval = m_con.getPeerStat(peer_id,type); + if (*retval == -1) return false; + return true; +} + +bool Server::getClientInfo( + u16 peer_id, + ClientState* state, + u32* uptime, + u8* ser_vers, + u16* prot_vers, + u8* major, + u8* minor, + u8* patch, + std::string* vers_string + ) +{ + *state = m_clients.getClientState(peer_id); + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid); + + if (client == NULL) { + m_clients.Unlock(); + return false; + } + + *uptime = client->uptime(); + *ser_vers = client->serialization_version; + *prot_vers = client->net_proto_version; + + *major = client->getMajor(); + *minor = client->getMinor(); + *patch = client->getPatch(); + *vers_string = client->getPatch(); + + m_clients.Unlock(); + + return true; +} + +void Server::handlePeerChanges() +{ + while(m_peer_change_queue.size() > 0) + { + con::PeerChange c = m_peer_change_queue.pop_front(); + + verbosestream<<"Server: Handling peer change: " + <<"id="< data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) +void Server::SendHP(u16 peer_id, u8 hp) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3323,11 +2954,25 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); +} + +void Server::SendBreath(u16 peer_id, u16 breath) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_BREATH); + writeU16(os, breath); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_clients.send(peer_id, 0, data, true); } -void Server::SendAccessDenied(con::Connection &con, u16 peer_id, - const std::wstring &reason) +void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3339,11 +2984,11 @@ void Server::SendAccessDenied(con::Connection &con, u16 peer_id, std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendDeathscreen(con::Connection &con, u16 peer_id, - bool set_camera_point_target, v3f camera_point_target) +void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target, + v3f camera_point_target) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3356,10 +3001,10 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendItemDef(con::Connection &con, u16 peer_id, +void Server::SendItemDef(u16 peer_id, IItemDefManager *itemdef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); @@ -3383,10 +3028,10 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id, <<"): size="< data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendNodeDef(con::Connection &con, u16 peer_id, +void Server::SendNodeDef(u16 peer_id, INodeDefManager *nodedef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); @@ -3410,7 +3055,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id, <<"): size="< data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } /* @@ -3440,7 +3085,7 @@ void Server::SendInventory(u16 peer_id) memcpy(&data[2], s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendChatMessage(u16 peer_id, const std::wstring &message) @@ -3469,35 +3114,44 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message) // Make data buffer std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); + + if (peer_id != PEER_ID_INEXISTENT) + { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); + } } -void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, - const std::string formname) +void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec, + const std::string &formname) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); u8 buf[12]; + // Write command writeU16(buf, TOCLIENT_SHOW_FORMSPEC); os.write((char*)buf, 2); - os< data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } // Spawns a particle on peer with peer_id void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, - std::string texture) + bool vertical, std::string texture) { DSTACK(__FUNCTION_NAME); @@ -3510,38 +3164,27 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat writeF1000(os, size); writeU8(os, collisiondetection); os< data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} -// Spawns a particle on all peers -void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, bool collisiondetection, - std::string texture) -{ - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); i++) + if (peer_id != PEER_ID_INEXISTENT) { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - SendSpawnParticle(client->peer_id, pos, velocity, acceleration, - expirationtime, size, collisiondetection, texture); + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); } } // Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) + float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id) { DSTACK(__FUNCTION_NAME); @@ -3563,32 +3206,19 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 writeU8(os, collisiondetection); os< data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} -// Adds a ParticleSpawner on all peers -void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) -{ - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); i++) + if (peer_id != PEER_ID_INEXISTENT) { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - SendAddParticleSpawner(client->peer_id, amount, spawntime, - minpos, maxpos, minvel, maxvel, minacc, maxacc, - minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id); + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else { + m_clients.sendToAll(0,data,true); } } @@ -3604,24 +3234,15 @@ void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) // Make data buffer std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} - -void Server::SendDeleteParticleSpawnerAll(u32 id) -{ - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); i++) - { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - SendDeleteParticleSpawner(client->peer_id, id); + if (peer_id != PEER_ID_INEXISTENT) { + // Send as reliable + m_clients.send(peer_id, 0, data, true); } + else { + m_clients.sendToAll(0,data,true); + } + } void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form) @@ -3641,12 +3262,14 @@ void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form) writeU32(os, form->dir); writeV2F1000(os, form->align); writeV2F1000(os, form->offset); + writeV3F1000(os, form->world_pos); + writeV2S32(os,form->size); // Make data buffer std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 1, data, true); } void Server::SendHUDRemove(u16 peer_id, u32 id) @@ -3661,7 +3284,8 @@ void Server::SendHUDRemove(u16 peer_id, u32 id) std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + + m_clients.send(peer_id, 1, data, true); } void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value) @@ -3683,6 +3307,12 @@ void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value case HUD_STAT_TEXT: os << serializeString(*(std::string *)value); break; + case HUD_STAT_WORLD_POS: + writeV3F1000(os, *(v3f *)value); + break; + case HUD_STAT_SIZE: + writeV2S32(os,*(v2s32 *)value); + break; case HUD_STAT_NUMBER: case HUD_STAT_ITEM: case HUD_STAT_DIR: @@ -3695,7 +3325,7 @@ void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value std::string s = os.str(); SharedBuffer data((u8 *)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) @@ -3704,6 +3334,10 @@ void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) // Write command writeU16(os, TOCLIENT_HUD_SET_FLAGS); + + //////////////////////////// compatibility code to be removed ////////////// + flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE); + //////////////////////////////////////////////////////////////////////////// writeU32(os, flags); writeU32(os, mask); @@ -3711,7 +3345,7 @@ void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) std::string s = os.str(); SharedBuffer data((u8 *)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) @@ -3727,22 +3361,62 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) std::string s = os.str(); SharedBuffer data((u8 *)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::BroadcastChatMessage(const std::wstring &message) +void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, + const std::string &type, const std::vector ¶ms) { - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_SET_SKY); + writeARGB8(os, bgcolor); + os< data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_clients.send(peer_id, 0, data, true); +} + +void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override, + float ratio) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO); + writeU8(os, do_override); + writeU16(os, ratio*65535); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_clients.send(peer_id, 0, data, true); +} + +void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed) +{ + DSTACK(__FUNCTION_NAME); + + // Make packet + SharedBuffer data(2+2+4); + writeU16(&data[0], TOCLIENT_TIME_OF_DAY); + writeU16(&data[2], time); + writeF1000(&data[4], time_speed); - SendChatMessage(client->peer_id, message); + if (peer_id == PEER_ID_INEXISTENT) { + m_clients.sendToAll(0,data,true); + } + else { + // Send as reliable + m_clients.send(peer_id, 0, data, true); } } @@ -3752,7 +3426,23 @@ void Server::SendPlayerHP(u16 peer_id) PlayerSAO *playersao = getPlayerSAO(peer_id); assert(playersao); playersao->m_hp_not_sent = false; - SendHP(m_con, peer_id, playersao->getHP()); + SendHP(peer_id, playersao->getHP()); + m_script->player_event(playersao,"health_changed"); + + // Send to other clients + std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP()); + ActiveObjectMessage aom(playersao->getId(), true, str); + playersao->m_messages_out.push_back(aom); +} + +void Server::SendPlayerBreath(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + PlayerSAO *playersao = getPlayerSAO(peer_id); + assert(playersao); + playersao->m_breath_not_sent = false; + m_script->player_event(playersao,"breath_changed"); + SendBreath(peer_id, playersao->getBreath()); } void Server::SendMovePlayer(u16 peer_id) @@ -3782,9 +3472,41 @@ void Server::SendMovePlayer(u16 peer_id) std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); +} + +void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed) +{ + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS); + writeV2S32(os, animation_frames[0]); + writeV2S32(os, animation_frames[1]); + writeV2S32(os, animation_frames[2]); + writeV2S32(os, animation_frames[3]); + writeF1000(os, animation_speed); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_clients.send(peer_id, 0, data, true); } +void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third) +{ + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_EYE_OFFSET); + writeV3F1000(os, first); + writeV3F1000(os, third); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_clients.send(peer_id, 0, data, true); +} void Server::SendPlayerPrivileges(u16 peer_id) { Player *player = m_env->getPlayer(peer_id); @@ -3807,7 +3529,7 @@ void Server::SendPlayerPrivileges(u16 peer_id) std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendPlayerInventoryFormspec(u16 peer_id) @@ -3819,13 +3541,13 @@ void Server::SendPlayerInventoryFormspec(u16 peer_id) std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_INVENTORY_FORMSPEC); - os<inventory_formspec); + os<inventory_formspec); // Make data buffer std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } s32 Server::playSound(const SimpleSoundSpec &spec, @@ -3837,8 +3559,9 @@ s32 Server::playSound(const SimpleSoundSpec &spec, // If position is not found while it should be, cancel sound if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL)) return -1; + // Filter destination clients - std::set dst_clients; + std::list dst_clients; if(params.to_player != "") { Player *player = m_env->getPlayer(params.to_player.c_str()); @@ -3852,16 +3575,16 @@ s32 Server::playSound(const SimpleSoundSpec &spec, <<"\" not connected"<peer_id); - dst_clients.insert(client); + dst_clients.push_back(player->peer_id); } else { - for(std::map::iterator - i = m_clients.begin(); i != m_clients.end(); ++i) + std::list clients = m_clients.getClientIDs(); + + for(std::list::iterator + i = clients.begin(); i != clients.end(); ++i) { - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(!player) continue; if(pos_exists){ @@ -3869,20 +3592,21 @@ s32 Server::playSound(const SimpleSoundSpec &spec, params.max_hear_distance) continue; } - dst_clients.insert(client); + dst_clients.push_back(*i); } } if(dst_clients.size() == 0) return -1; + // Create the sound s32 id = m_next_sound_id++; // The sound will exist as a reference in m_playing_sounds m_playing_sounds[id] = ServerPlayingSound(); ServerPlayingSound &psound = m_playing_sounds[id]; psound.params = params; - for(std::set::iterator i = dst_clients.begin(); + for(std::list::iterator i = dst_clients.begin(); i != dst_clients.end(); i++) - psound.clients.insert((*i)->peer_id); + psound.clients.insert(*i); // Create packet std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PLAY_SOUND); @@ -3897,10 +3621,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec, std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send - for(std::set::iterator i = dst_clients.begin(); + for(std::list::iterator i = dst_clients.begin(); i != dst_clients.end(); i++){ // Send as reliable - m_con.Send((*i)->peer_id, 0, data, true); + m_clients.send(*i, 0, data, true); } return id; } @@ -3923,7 +3647,7 @@ void Server::stopSound(s32 handle) for(std::set::iterator i = psound.clients.begin(); i != psound.clients.end(); i++){ // Send as reliable - m_con.Send(*i, 0, data, true); + m_clients.send(*i, 0, data, true); } // Remove sound reference m_playing_sounds.erase(i); @@ -3943,103 +3667,107 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list clients = m_clients.getClientIDs(); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; - if(far_players) { // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(player) { // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); + far_players->push_back(*i); continue; } } } // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(*i, 0, reply, true); } } void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, - std::list *far_players, float far_d_nodes) + std::list *far_players, float far_d_nodes, + bool remove_metadata) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; + std::list clients = m_clients.getClientIDs(); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) + { if(far_players) { // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(player) { // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); + far_players->push_back(*i); continue; } } } - - // Create packet - u32 replysize = 8 + MapNode::serializedLength(client->serialization_version); - SharedBuffer reply(replysize); - writeU16(&reply[0], TOCLIENT_ADDNODE); - writeS16(&reply[2], p.X); - writeS16(&reply[4], p.Y); - writeS16(&reply[6], p.Z); - n.serialize(&reply[8], client->serialization_version); + SharedBuffer reply(0); + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(*i); + if (client != 0) + { + // Create packet + u32 replysize = 9 + MapNode::serializedLength(client->serialization_version); + reply = SharedBuffer(replysize); + writeU16(&reply[0], TOCLIENT_ADDNODE); + writeS16(&reply[2], p.X); + writeS16(&reply[4], p.Y); + writeS16(&reply[6], p.Z); + n.serialize(&reply[8], client->serialization_version); + u32 index = 8 + MapNode::serializedLength(client->serialization_version); + writeU8(&reply[index], remove_metadata ? 0 : 1); + + if (!remove_metadata) { + if (client->net_proto_version <= 21) { + // Old clients always clear metadata; fix it + // by sending the full block again. + client->SetBlockNotSent(p); + } + } + } + m_clients.Unlock(); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + if (reply.getSize() > 0) + m_clients.send(*i, 0, reply, true); } } void Server::setBlockNotSent(v3s16 p) { - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list clients = m_clients.getClientIDs(); + m_clients.Lock(); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) { - RemoteClient *client = i->second; + RemoteClient *client = m_clients.lockedGetClientNoEx(*i); client->SetBlockNotSent(p); } + m_clients.Unlock(); } -void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) +void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version) { DSTACK(__FUNCTION_NAME); @@ -4072,6 +3800,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false); + block->serializeNetworkSpecific(os, net_proto_version); std::string s = os.str(); SharedBuffer blockdata((u8*)s.c_str(), s.size()); @@ -4089,7 +3818,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) /* Send packet */ - m_con.Send(peer_id, 1, reply, true); + m_clients.send(peer_id, 2, reply, true); } void Server::SendBlocks(float dtime) @@ -4097,7 +3826,7 @@ void Server::SendBlocks(float dtime) DSTACK(__FUNCTION_NAME); JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); + //TODO check if one big lock could be faster then multiple small ones ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients"); @@ -4108,25 +3837,22 @@ void Server::SendBlocks(float dtime) { ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list clients = m_clients.getClientIDs(); + + m_clients.Lock(); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) { - RemoteClient *client = i->second; - assert(client->peer_id == i->first); + RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active); - // If definitions and textures have not been sent, don't - // send MapBlocks either - if(!client->definitions_sent) - continue; + if (client == NULL) + return; total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); + client->GetNextBlocks(m_env,m_emerge, dtime, queue); } + m_clients.Unlock(); } // Sort. @@ -4134,6 +3860,7 @@ void Server::SendBlocks(float dtime) // Lowest is most important. std::sort(queue.begin(), queue.end()); + m_clients.Lock(); for(u32 i=0; iserialization_version); + if(!client) + continue; - client->SentBlock(q.pos); + SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version); + client->SentBlock(q.pos); total_sending++; } + m_clients.Unlock(); } void Server::fillMediaCache() @@ -4179,8 +3909,7 @@ void Server::fillMediaCache() paths.push_back(mod.path + DIR_DELIM + "media"); paths.push_back(mod.path + DIR_DELIM + "models"); } - std::string path_all = "textures"; - paths.push_back(path_all + DIR_DELIM + "all"); + paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); // Collect media file information from paths into cache for(std::list::iterator i = paths.begin(); @@ -4265,8 +3994,8 @@ struct SendableMediaAnnouncement std::string name; std::string sha1_digest; - SendableMediaAnnouncement(const std::string name_="", - const std::string sha1_digest_=""): + SendableMediaAnnouncement(const std::string &name_="", + const std::string &sha1_digest_=""): name(name_), sha1_digest(sha1_digest_) {} @@ -4318,7 +4047,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } struct SendableMedia @@ -4327,8 +4056,8 @@ struct SendableMedia std::string path; std::string data; - SendableMedia(const std::string &name_="", const std::string path_="", - const std::string &data_=""): + SendableMedia(const std::string &name_="", const std::string &path_="", + const std::string &data_=""): name(name_), path(path_), data(data_) @@ -4336,7 +4065,7 @@ struct SendableMedia }; void Server::sendRequestedMedia(u16 peer_id, - const std::list &tosend) + const std::list &tosend) { DSTACK(__FUNCTION_NAME); @@ -4353,17 +4082,19 @@ void Server::sendRequestedMedia(u16 peer_id, u32 file_size_bunch_total = 0; - for(std::list::const_iterator i = tosend.begin(); + for(std::list::const_iterator i = tosend.begin(); i != tosend.end(); ++i) { - if(m_media.find(i->name) == m_media.end()){ + const std::string &name = *i; + + if(m_media.find(name) == m_media.end()){ errorstream<<"Server::sendRequestedMedia(): Client asked for " - <<"unknown file \""<<(i->name)<<"\""<= bytes_per_bunch){ @@ -4446,7 +4177,7 @@ void Server::sendRequestedMedia(u16 peer_id, <<" size=" < data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 2, data, true); } } @@ -4466,19 +4197,15 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id) // Make data buffer std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} - -void Server::sendDetachedInventoryToAll(const std::string &name) -{ - DSTACK(__FUNCTION_NAME); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i){ - RemoteClient *client = i->second; - sendDetachedInventory(name, client->peer_id); + if (peer_id != PEER_ID_INEXISTENT) + { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); } } @@ -4510,33 +4237,127 @@ void Server::DiePlayer(u16 peer_id) <getPlayer()->getName() <<" dies"<setHP(0); + playersao->setHP(0); + + // Trigger scripted stuff + m_script->on_dieplayer(playersao); + + SendPlayerHP(peer_id); + SendDeathscreen(peer_id, false, v3f(0,0,0)); +} + +void Server::RespawnPlayer(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + + PlayerSAO *playersao = getPlayerSAO(peer_id); + assert(playersao); + + infostream<<"Server::RespawnPlayer(): Player " + <getPlayer()->getName() + <<" respawns"<setHP(PLAYER_MAX_HP); + + bool repositioned = m_script->on_respawnplayer(playersao); + if(!repositioned){ + v3f pos = findSpawnPos(m_env->getServerMap()); + playersao->setPos(pos); + } +} + +void Server::DenyAccess(u16 peer_id, const std::wstring &reason) +{ + DSTACK(__FUNCTION_NAME); + + SendAccessDenied(peer_id, reason); + m_clients.event(peer_id, CSE_SetDenied); + m_con.DisconnectPeer(peer_id); +} + +void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) +{ + DSTACK(__FUNCTION_NAME); + std::wstring message; + { + /* + Clear references to playing sounds + */ + for(std::map::iterator + i = m_playing_sounds.begin(); + i != m_playing_sounds.end();) + { + ServerPlayingSound &psound = i->second; + psound.clients.erase(peer_id); + if(psound.clients.size() == 0) + m_playing_sounds.erase(i++); + else + i++; + } + + Player *player = m_env->getPlayer(peer_id); - // Trigger scripted stuff - m_script->on_dieplayer(playersao); + // Collect information about leaving in chat + { + if(player != NULL && reason != CDR_DENY) + { + std::wstring name = narrow_to_wide(player->getName()); + message += L"*** "; + message += name; + message += L" left the game."; + if(reason == CDR_TIMEOUT) + message += L" (timed out)"; + } + } - SendPlayerHP(peer_id); - SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); -} + /* Run scripts and remove from environment */ + { + if(player != NULL) + { + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); -void Server::RespawnPlayer(u16 peer_id) -{ - DSTACK(__FUNCTION_NAME); + m_script->on_leaveplayer(playersao); - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); + playersao->disconnected(); + } + } - infostream<<"Server::RespawnPlayer(): Player " - <getPlayer()->getName() - <<" respawns"< clients = m_clients.getClientIDs(); - playersao->setHP(PLAYER_MAX_HP); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i) + { + // Get player + Player *player = m_env->getPlayer(*i); + if(!player) + continue; + // Get name of player + os<getName()<<" "; + } - bool repositioned = m_script->on_respawnplayer(playersao); - if(!repositioned){ - v3f pos = findSpawnPos(m_env->getServerMap()); - playersao->setPos(pos); + actionstream<getName()<<" " + <<(reason==CDR_TIMEOUT?"times out.":"leaves game.") + <<" List of players: "<getName()); getCraftingResult(&player->inventory, preview, false, this); + m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc); // Put the new preview in InventoryList *plist = player->inventory.getList("craftpreview"); @@ -4557,15 +4381,33 @@ void Server::UpdateCrafting(u16 peer_id) plist->changeItem(0, preview); } -RemoteClient* Server::getClient(u16 peer_id) +RemoteClient* Server::getClient(u16 peer_id, ClientState state_min) { - DSTACK(__FUNCTION_NAME); - //JMutexAutoLock lock(m_con_mutex); - std::map::iterator n; - n = m_clients.find(peer_id); - // A client should exist for all peers - assert(n != m_clients.end()); - return n->second; + RemoteClient *client = getClientNoEx(peer_id,state_min); + if(!client) + throw ClientNotFoundException("Client not found"); + + return client; +} +RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min) +{ + return m_clients.getClientNoEx(peer_id, state_min); +} + +std::string Server::getPlayerName(u16 peer_id) +{ + Player *player = m_env->getPlayer(peer_id); + if(player == NULL) + return "[id="+itos(peer_id)+"]"; + return player->getName(); +} + +PlayerSAO* Server::getPlayerSAO(u16 peer_id) +{ + Player *player = m_env->getPlayer(peer_id); + if(player == NULL) + return NULL; + return player->getPlayerSAO(); } std::wstring Server::getStatusString() @@ -4573,23 +4415,20 @@ std::wstring Server::getStatusString() std::wostringstream os(std::ios_base::binary); os< clients = m_clients.getClientIDs(); + for(std::list::iterator i = clients.begin(); + i != clients.end(); ++i) { - // Get client and check that it is valid - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); // Get name of player std::wstring name = L"unknown"; if(player != NULL) @@ -4625,11 +4464,11 @@ bool Server::checkPriv(const std::string &name, const std::string &priv) void Server::reportPrivsModified(const std::string &name) { if(name == ""){ - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i){ - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); + std::list clients = m_clients.getClientIDs(); + for(std::list::iterator + i = clients.begin(); + i != clients.end(); ++i){ + Player *player = m_env->getPlayer(*i); reportPrivsModified(player->getName()); } } else { @@ -4654,22 +4493,31 @@ void Server::reportInventoryFormspecModified(const std::string &name) SendPlayerInventoryFormspec(player->peer_id); } -// Saves g_settings to configpath given at initialization -void Server::saveConfig() +void Server::setIpBanned(const std::string &ip, const std::string &name) +{ + m_banmanager->add(ip, name); +} + +void Server::unsetIpBanned(const std::string &ip_or_name) +{ + m_banmanager->remove(ip_or_name); +} + +std::string Server::getBanDescription(const std::string &ip_or_name) { - if(m_path_config != "") - g_settings->updateConfigFile(m_path_config.c_str()); + return m_banmanager->getBanDescription(ip_or_name); } -void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true) +void Server::notifyPlayer(const char *name, const std::wstring &msg) { Player *player = m_env->getPlayer(name); if(!player) return; - if (prepend) - SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg); - else - SendChatMessage(player->peer_id, msg); + + if (player->peer_id == PEER_ID_INEXISTENT) + return; + + SendChatMessage(player->peer_id, msg); } bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname) @@ -4689,24 +4537,25 @@ bool Server::showFormspec(const char *playername, const std::string &formspec, c u32 Server::hudAdd(Player *player, HudElement *form) { if (!player) return -1; - - u32 id = hud_get_free_id(player); - if (id < player->hud.size()) - player->hud[id] = form; - else - player->hud.push_back(form); + u32 id = player->addHud(form); + SendHUDAdd(player->peer_id, id, form); + return id; } bool Server::hudRemove(Player *player, u32 id) { - if (!player || id >= player->hud.size() || !player->hud[id]) + if (!player) return false; - delete player->hud[id]; - player->hud[id] = NULL; + HudElement* todel = player->removeHud(id); + + if (!todel) + return false; + delete todel; + SendHUDRemove(player->peer_id, id); return true; } @@ -4724,6 +4573,9 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) { return false; SendHUDSetFlags(player->peer_id, flags, mask); + player->hud_flags = flags; + + m_script->player_event(player->getPlayerSAO(),"hud_changed"); return true; } @@ -4739,29 +4591,81 @@ bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) { return true; } -void Server::notifyPlayers(const std::wstring msg) +void Server::hudSetHotbarImage(Player *player, std::string name) { + if (!player) + return; + + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name); +} + +void Server::hudSetHotbarSelectedImage(Player *player, std::string name) { + if (!player) + return; + + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name); +} + +bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed) +{ + if (!player) + return false; + + SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed); + return true; +} + +bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third) +{ + if (!player) + return false; + + SendEyeOffset(player->peer_id, first, third); + return true; +} + +bool Server::setSky(Player *player, const video::SColor &bgcolor, + const std::string &type, const std::vector ¶ms) +{ + if (!player) + return false; + + SendSetSky(player->peer_id, bgcolor, type, params); + return true; +} + +bool Server::overrideDayNightRatio(Player *player, bool do_override, + float ratio) +{ + if (!player) + return false; + + SendOverrideDayNightRatio(player->peer_id, do_override, ratio); + return true; +} + +void Server::notifyPlayers(const std::wstring &msg) { - BroadcastChatMessage(msg); + SendChatMessage(PEER_ID_INEXISTENT,msg); } void Server::spawnParticle(const char *playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool - collisiondetection, std::string texture) + collisiondetection, bool vertical, std::string texture) { Player *player = m_env->getPlayer(playername); if(!player) return; SendSpawnParticle(player->peer_id, pos, velocity, acceleration, - expirationtime, size, collisiondetection, texture); + expirationtime, size, collisiondetection, vertical, texture); } void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, - bool collisiondetection, std::string texture) + bool collisiondetection, bool vertical, std::string texture) { - SendSpawnParticleAll(pos, velocity, acceleration, - expirationtime, size, collisiondetection, texture); + SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration, + expirationtime, size, collisiondetection, vertical, texture); } u32 Server::addParticleSpawner(const char *playername, @@ -4771,7 +4675,7 @@ u32 Server::addParticleSpawner(const char *playername, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, std::string texture) + bool collisiondetection, bool vertical, std::string texture) { Player *player = m_env->getPlayer(playername); if(!player) @@ -4793,7 +4697,7 @@ u32 Server::addParticleSpawner(const char *playername, SendAddParticleSpawner(player->peer_id, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, - collisiondetection, texture, id); + collisiondetection, vertical, texture, id); return id; } @@ -4804,7 +4708,7 @@ u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, std::string texture) + bool collisiondetection, bool vertical, std::string texture) { u32 id = 0; for(;;) // look for unused particlespawner id @@ -4819,10 +4723,10 @@ u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, } } - SendAddParticleSpawnerAll(amount, spawntime, + SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, - collisiondetection, texture, id); + collisiondetection, vertical, texture, id); return id; } @@ -4846,12 +4750,7 @@ void Server::deleteParticleSpawnerAll(u32 id) std::remove(m_particlespawner_ids.begin(), m_particlespawner_ids.end(), id), m_particlespawner_ids.end()); - SendDeleteParticleSpawnerAll(id); -} - -void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) -{ - m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate); + SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id); } Inventory* Server::createDetachedInventory(const std::string &name) @@ -4865,7 +4764,8 @@ Inventory* Server::createDetachedInventory(const std::string &name) Inventory *inv = new Inventory(m_itemdef); assert(inv); m_detached_inventories[name] = inv; - sendDetachedInventoryToAll(name); + //TODO find a better way to do this + sendDetachedInventory(name,PEER_ID_INEXISTENT); return inv; } @@ -5026,7 +4926,7 @@ v3f findSpawnPos(ServerMap &map) #endif #if 1 - s16 water_level = map.m_mgparams->water_level; + s16 water_level = map.getWaterLevel(); // Try to find a good place a few times for(s32 i=0; i<1000; i++) @@ -5098,15 +4998,16 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) return NULL; } - /* - Create a new player if it doesn't exist yet - */ - if(player == NULL) - { + // Load player if it isn't already loaded + if (!player) { + player = static_cast(m_env->loadPlayer(name)); + } + + // Create player if it doesn't exist + if (!player) { newplayer = true; player = new RemotePlayer(this); player->updateName(name); - /* Set player position */ infostream<<"Server: Finding spawn place for player \"" <addPlayer(player); } - /* - Create a new player active object - */ + // Create a new player active object PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id, getPlayerEffectivePrivs(player->getName()), isSingleplayer()); /* Clean up old HUD elements from previous sessions */ - player->hud.clear(); + player->clearHud(); /* Add object to environment */ m_env->addActiveObject(playersao); /* Run scripts */ - if(newplayer) + if (newplayer) { m_script->on_newplayer(playersao); - - m_script->on_joinplayer(playersao); - - return playersao; -} - -void Server::handlePeerChange(PeerChange &c) -{ - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - if(c.type == PEER_ADDED) - { - /* - Add - */ - - // Error check - std::map::iterator n; - n = m_clients.find(c.peer_id); - // The client shouldn't already exist - assert(n == m_clients.end()); - - // Create client - RemoteClient *client = new RemoteClient(); - client->peer_id = c.peer_id; - m_clients[client->peer_id] = client; - - } // PEER_ADDED - else if(c.type == PEER_REMOVED) - { - /* - Delete - */ - - // Error check - std::map::iterator n; - n = m_clients.find(c.peer_id); - // The client should exist - assert(n != m_clients.end()); - - /* - Mark objects to be not known by the client - */ - RemoteClient *client = n->second; - // Handle objects - for(std::set::iterator - i = client->m_known_objects.begin(); - i != client->m_known_objects.end(); ++i) - { - // Get object - u16 id = *i; - ServerActiveObject* obj = m_env->getActiveObject(id); - - if(obj && obj->m_known_by_count > 0) - obj->m_known_by_count--; - } - - /* - Clear references to playing sounds - */ - for(std::map::iterator - i = m_playing_sounds.begin(); - i != m_playing_sounds.end();) - { - ServerPlayingSound &psound = i->second; - psound.clients.erase(c.peer_id); - if(psound.clients.size() == 0) - m_playing_sounds.erase(i++); - else - i++; - } - - Player *player = m_env->getPlayer(c.peer_id); - - // Collect information about leaving in chat - std::wstring message; - { - if(player != NULL) - { - std::wstring name = narrow_to_wide(player->getName()); - message += L"*** "; - message += name; - message += L" left the game."; - if(c.timeout) - message += L" (timed out)"; - } - } - - /* Run scripts and remove from environment */ - { - if(player != NULL) - { - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); - - m_script->on_leaveplayer(playersao); - - playersao->disconnected(); - } - } - - /* - Print out action - */ - { - if(player != NULL) - { - std::ostringstream os(std::ios_base::binary); - for(std::map::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - RemoteClient *client = i->second; - assert(client->peer_id == i->first); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - // Get player - Player *player = m_env->getPlayer(client->peer_id); - if(!player) - continue; - // Get name of player - os<getName()<<" "; - } - - actionstream<getName()<<" " - <<(c.timeout?"times out.":"leaves game.") - <<" List of players: " - < 0) - { - PeerChange c = m_peer_change_queue.pop_front(); - - verbosestream<<"Server: Handling peer change: " - <<"id="<