X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=95d2371ff1153591957ab906f54f3edfa4e441ff;hb=4faaadc8d50d6ab7a19d22bd5a760c4b8321a51f;hp=ada45dc684fdb2983327bccf0c1ef7177cbdf530;hpb=d499ec483837fa7210176ef39beba2d5a3a5a61d;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index ada45dc68..95d2371ff 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "scripting_game.h" +#include "scripting_server.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -47,14 +47,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" -#include "content_abm.h" #include "content_sao.h" #include "mods.h" -#include "sound.h" // dummySoundManager #include "event_manager.h" #include "serverlist.h" #include "util/string.h" -#include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" #include "util/thread.h" @@ -62,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/base64.h" #include "util/sha1.h" #include "util/hex.h" +#include "database.h" class ClientNotFoundException : public BaseException { @@ -108,7 +106,8 @@ void *ServerThread::run() } catch (con::ConnectionBindFailed &e) { m_server->setAsyncFatalError(e.what()); } catch (LuaError &e) { - m_server->setAsyncFatalError("Lua: " + std::string(e.what())); + m_server->setAsyncFatalError( + "ServerThread::run Lua: " + std::string(e.what())); } } @@ -149,48 +148,27 @@ Server::Server( const SubgameSpec &gamespec, bool simple_singleplayer_mode, bool ipv6, + bool dedicated, ChatInterface *iface ): m_path_world(path_world), m_gamespec(gamespec), m_simple_singleplayer_mode(simple_singleplayer_mode), + m_dedicated(dedicated), m_async_fatal_error(""), - m_env(NULL), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this), - m_banmanager(NULL), - m_rollback(NULL), - m_enable_rollback_recording(false), - m_emerge(NULL), - m_script(NULL), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), m_event(new EventManager()), - m_thread(NULL), - m_time_of_day_send_timer(0), m_uptime(0), m_clients(&m_con), - m_shutdown_requested(false), - m_shutdown_ask_reconnect(false), - m_admin_chat(iface), - m_ignore_map_edit_events(false), - m_ignore_map_edit_events_peer_id(0), - m_next_sound_id(0) - -{ - m_liquid_transform_timer = 0.0; - m_liquid_transform_every = 1.0; - m_print_info_timer = 0.0; - m_masterserver_timer = 0.0; - m_objectdata_timer = 0.0; - m_emergethread_trigger_timer = 0.0; - m_savemap_timer = 0.0; - - m_step_dtime = 0.0; + m_admin_chat(iface) +{ m_lag = g_settings->getFloat("dedicated_server_step"); if(path_world == "") @@ -221,82 +199,42 @@ Server::Server( std::string ban_path = m_path_world + DIR_DELIM "ipban.txt"; m_banmanager = new BanManager(ban_path); - ModConfiguration modconf(m_path_world); + ServerModConfiguration modconf(m_path_world); m_mods = modconf.getMods(); std::vector unsatisfied_mods = modconf.getUnsatisfiedMods(); // complain about mods with unsatisfied dependencies - if(!modconf.isConsistent()) { - for(std::vector::iterator it = unsatisfied_mods.begin(); - it != unsatisfied_mods.end(); ++it) { - ModSpec mod = *it; - errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: "; - for(std::set::iterator dep_it = mod.unsatisfied_depends.begin(); - dep_it != mod.unsatisfied_depends.end(); ++dep_it) - errorstream << " \"" << *dep_it << "\""; - errorstream << std::endl; - } - } - - Settings worldmt_settings; - std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; - worldmt_settings.readConfigFile(worldmt.c_str()); - std::vector names = worldmt_settings.getNames(); - std::set load_mod_names; - for(std::vector::iterator it = names.begin(); - it != names.end(); ++it) { - std::string name = *it; - if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name)) - load_mod_names.insert(name.substr(9)); - } - // complain about mods declared to be loaded, but not found - for(std::vector::iterator it = m_mods.begin(); - it != m_mods.end(); ++it) - load_mod_names.erase((*it).name); - for(std::vector::iterator it = unsatisfied_mods.begin(); - 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) - errorstream << " \"" << (*it) << "\""; - errorstream << std::endl; + if (!modconf.isConsistent()) { + modconf.printUnsatisfiedModsError(); } //lock environment MutexAutoLock envlock(m_env_mutex); - // Load mapgen params from Settings - m_emerge->loadMapgenParams(); - // Create the Map (loads map_meta.txt, overriding configured mapgen params) ServerMap *servermap = new ServerMap(path_world, this, m_emerge); // Initialize scripting infostream<<"Server: Initializing Lua"<loadMod(script_path, BUILTIN_MOD_NAME); + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); // Print mods infostream << "Server: Loading mods: "; - for(std::vector::iterator i = m_mods.begin(); + for (std::vector::const_iterator i = m_mods.begin(); i != m_mods.end(); ++i) { - const ModSpec &mod = *i; - infostream << mod.name << " "; + infostream << (*i).name << " "; } infostream << std::endl; // Load and run "mod" scripts - for (std::vector::iterator it = m_mods.begin(); + for (std::vector::const_iterator it = m_mods.begin(); it != m_mods.end(); ++it) { const ModSpec &mod = *it; if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " - "Only chararacters [a-z0-9_] are allowed."); + "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; infostream << " [" << padStringRight(mod.name, 12) << "] [\"" @@ -331,8 +269,11 @@ Server::Server( m_clients.setEnv(m_env); + if (!servermap->settings_mgr.makeMapgenParams()) + FATAL_ERROR("Couldn't create any mapgen type"); + // Initialize mapgens - m_emerge->initMapgens(); + m_emerge->initMapgens(servermap->getMapgenParams()); m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording"); if (m_enable_rollback_recording) { @@ -354,10 +295,8 @@ Server::Server( m_env->loadDefaultMeta(); } - // Add some test ActiveBlockModifiers to environment - add_legacy_abms(m_env, m_nodedef); - m_liquid_transform_every = g_settings->getFloat("liquid_update"); + m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); } Server::~Server() @@ -402,11 +341,8 @@ Server::~Server() 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_env; delete m_rollback; delete m_banmanager; delete m_event; @@ -492,7 +428,7 @@ void Server::step(float dtime) g_settings->get("kick_msg_crash"), g_settings->getBool("ask_reconnect_on_crash")); } - throw ServerError(async_err); + throw ServerError("AsyncErr: " + async_err); } } @@ -612,7 +548,7 @@ void Server::AsyncRunStep(bool initial_step) ScopeProfiler sp(g_profiler, "Server: liquid transform"); std::map modified_blocks; - m_env->getMap().transformLiquids(modified_blocks); + m_env->getMap().transformLiquids(modified_blocks, m_env); #if 0 /* Update lighting @@ -645,18 +581,19 @@ void Server::AsyncRunStep(bool initial_step) // send masterserver announce { float &counter = m_masterserver_timer; - if(!isSingleplayer() && (!counter || counter >= 300.0) && - g_settings->getBool("server_announce")) - { - ServerList::sendAnnounce(counter ? "update" : "start", + if (!isSingleplayer() && (!counter || counter >= 300.0) && + g_settings->getBool("server_announce")) { + ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE : + ServerList::AA_START, m_bind_addr.getPort(), m_clients.getPlayerNames(), m_uptime.get(), m_env->getGameTime(), m_lag, m_gamespec.id, - m_emerge->params.mg_name, - m_mods); + Mapgen::getMapgenName(m_emerge->mgparams->mgtype), + m_mods, + m_dedicated); counter = 0.01; } counter += dtime; @@ -671,24 +608,24 @@ void Server::AsyncRunStep(bool initial_step) MutexAutoLock envlock(m_env_mutex); m_clients.lock(); - std::map clients = m_clients.getClientList(); + RemoteClientMap clients = m_clients.getClientList(); ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs"); // Radius inside which objects are active - static const s16 radius = + static thread_local const s16 radius = g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE; // Radius inside which players are active - static const bool is_transfer_limited = + static thread_local const bool is_transfer_limited = g_settings->exists("unlimited_player_transfer_distance") && !g_settings->getBool("unlimited_player_transfer_distance"); - static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE; + static thread_local const s16 player_transfer_dist = + g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE; s16 player_radius = player_transfer_dist; if (player_radius == 0 && is_transfer_limited) player_radius = radius; - for (std::map::iterator - i = clients.begin(); + for (RemoteClientMap::iterator i = clients.begin(); i != clients.end(); ++i) { RemoteClient *client = i->second; @@ -697,8 +634,8 @@ void Server::AsyncRunStep(bool initial_step) if (client->getState() < CS_DefinitionsSent) continue; - Player *player = m_env->getPlayer(client->peer_id); - if(player == NULL) { + RemotePlayer *player = m_env->getPlayer(client->peer_id); + if (player == NULL) { // This can happen if the client timeouts somehow /*warningstream<peer_id @@ -706,11 +643,19 @@ void Server::AsyncRunStep(bool initial_step) continue; } + PlayerSAO *playersao = player->getPlayerSAO(); + if (playersao == NULL) + continue; + + s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE); + if (my_radius <= 0) my_radius = radius; + //infostream << "Server: Active Radius " << my_radius << std::endl; + std::queue removed_objects; std::queue added_objects; - m_env->getRemovedActiveObjects(player, radius, player_radius, + m_env->getRemovedActiveObjects(playersao, my_radius, player_radius, client->m_known_objects, removed_objects); - m_env->getAddedActiveObjects(player, radius, player_radius, + m_env->getAddedActiveObjects(playersao, my_radius, player_radius, client->m_known_objects, added_objects); // Ignore if nothing happened @@ -786,6 +731,18 @@ void Server::AsyncRunStep(bool initial_step) << "packet size is " << pktSize << std::endl; } m_clients.unlock(); + + m_mod_storage_save_timer -= dtime; + if (m_mod_storage_save_timer <= 0.0f) { + infostream << "Saving registered mod storages." << std::endl; + m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval"); + for (std::unordered_map::const_iterator + it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { + if (it->second->isModified()) { + it->second->save(getModStoragePath()); + } + } + } } /* @@ -797,7 +754,7 @@ void Server::AsyncRunStep(bool initial_step) // Key = object id // Value = data sent by object - std::map* > buffered_messages; + std::unordered_map*> buffered_messages; // Get active object messages from environment for(;;) { @@ -806,7 +763,7 @@ void Server::AsyncRunStep(bool initial_step) break; std::vector* message_list = NULL; - std::map* >::iterator n; + std::unordered_map* >::iterator n; n = buffered_messages.find(aom.id); if (n == buffered_messages.end()) { message_list = new std::vector; @@ -819,16 +776,15 @@ void Server::AsyncRunStep(bool initial_step) } m_clients.lock(); - std::map clients = m_clients.getClientList(); + RemoteClientMap clients = m_clients.getClientList(); // Route data to every client - for (std::map::iterator - i = clients.begin(); - i != clients.end(); ++i) { + for (std::unordered_map::iterator i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; std::string reliable_data; std::string unreliable_data; // Go through all objects in message buffer - for (std::map* >::iterator + for (std::unordered_map* >::iterator j = buffered_messages.begin(); j != buffered_messages.end(); ++j) { // If object is not known by client, skip it @@ -872,7 +828,7 @@ void Server::AsyncRunStep(bool initial_step) m_clients.unlock(); // Clear buffered_messages - for(std::map* >::iterator + for (std::unordered_map* >::iterator i = buffered_messages.begin(); i != buffered_messages.end(); ++i) { delete i->second; @@ -1001,7 +957,7 @@ void Server::AsyncRunStep(bool initial_step) { float &counter = m_savemap_timer; counter += dtime; - static const float save_interval = + static thread_local const float save_interval = g_settings->getFloat("server_map_save_interval"); if (counter >= save_interval) { counter = 0.0; @@ -1024,6 +980,39 @@ void Server::AsyncRunStep(bool initial_step) m_env->saveMeta(); } } + + // Timed shutdown + static const float shutdown_msg_times[] = + { + 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600 + }; + + if (m_shutdown_timer > 0.0f) { + // Automated messages + if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) { + for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) { + // If shutdown timer matches an automessage, shot it + if (m_shutdown_timer > shutdown_msg_times[i] && + m_shutdown_timer - dtime < shutdown_msg_times[i]) { + std::wstringstream ws; + + ws << L"*** Server shutting down in " + << duration_to_string(myround(m_shutdown_timer - dtime)).c_str() + << "."; + + infostream << wide_to_utf8(ws.str()).c_str() << std::endl; + SendChatMessage(PEER_ID_INEXISTENT, ws.str()); + break; + } + } + } + + m_shutdown_timer -= dtime; + if (m_shutdown_timer < 0.0f) { + m_shutdown_timer = 0.0f; + m_shutdown_requested = true; + } + } } void Server::Receive() @@ -1074,8 +1063,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) } m_clients.unlock(); - RemotePlayer *player = - static_cast(m_env->getPlayer(playername.c_str())); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); // If failed, cancel if ((playersao == NULL) || (player == NULL)) { @@ -1107,37 +1095,19 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) // Send inventory SendInventory(playersao); - // Send HP - SendPlayerHPOrDie(playersao); + // Send HP or death screen + if (playersao->isDead()) + SendDeathscreen(peer_id, false, v3f(0,0,0)); + else + SendPlayerHPOrDie(playersao); // Send Breath - SendPlayerBreath(peer_id); - - // Show death screen if necessary - if(player->isDead()) - SendDeathscreen(peer_id, false, v3f(0,0,0)); + SendPlayerBreath(playersao); // Note things in chat if not in simple singleplayer mode - if(!m_simple_singleplayer_mode) { + if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) { // Send information about server to player in chat SendChatMessage(peer_id, getStatusString()); - - // Send information about joining in chat - { - std::string name = "unknown"; - Player *player = m_env->getPlayer(peer_id); - if(player != NULL) - name = player->getName(); - - std::wstring message; - message += L"*** "; - message += narrow_to_wide(name); - message += L" joined the game."; - SendChatMessage(PEER_ID_INEXISTENT,message); - if (m_admin_chat) - m_admin_chat->outgoing_queue.push_back( - new ChatEventNick(CET_NICK_ADD, name)); - } } Address addr = getPeerAddress(player->peer_id); std::string ip_str = addr.serializeString(); @@ -1146,11 +1116,11 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) Print out action */ { - std::vector names = m_clients.getPlayerNames(); + const std::vector &names = m_clients.getPlayerNames(); - actionstream<getName() <<" joins game. List of players: "; + actionstream << player->getName() << " joins game. List of players: "; - for (std::vector::iterator i = names.begin(); + for (std::vector::const_iterator i = names.begin(); i != names.end(); ++i) { actionstream << *i << " "; } @@ -1277,7 +1247,7 @@ Inventory* Server::getInventory(const InventoryLocation &loc) break; case InventoryLocation::PLAYER: { - Player *player = m_env->getPlayer(loc.name.c_str()); + RemotePlayer *player = dynamic_cast(m_env->getPlayer(loc.name.c_str())); if(!player) return NULL; PlayerSAO *playersao = player->getPlayerSAO(); @@ -1317,9 +1287,12 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend) if (!playerSend) return; - Player *player = m_env->getPlayer(loc.name.c_str()); - if(!player) + RemotePlayer *player = + dynamic_cast(m_env->getPlayer(loc.name.c_str())); + + if (!player) return; + PlayerSAO *playersao = player->getPlayerSAO(); if(!playersao) return; @@ -1645,15 +1618,18 @@ void Server::SendInventory(PlayerSAO* playerSAO) void Server::SendChatMessage(u16 peer_id, const std::wstring &message) { DSTACK(FUNCTION_NAME); + if (peer_id != PEER_ID_INEXISTENT) { + NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - pkt << message; + if (m_clients.getProtocolVersion(peer_id) < 27) + pkt << unescape_enriched(message); + else + pkt << message; - if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); + } else { + for (u16 id : m_clients.getClientIDs()) + SendChatMessage(id, message); } } @@ -1663,20 +1639,52 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec, DSTACK(FUNCTION_NAME); NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id); - - pkt.putLongString(FORMSPEC_VERSION_STRING + formspec); + if (formspec == "" ){ + //the client should close the formspec + pkt.putLongString(""); + } else { + pkt.putLongString(FORMSPEC_VERSION_STRING + formspec); + } pkt << formname; Send(&pkt); } // Spawns a particle on peer with peer_id -void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, +void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version, + v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + static thread_local const float radius = + g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; + + if (peer_id == PEER_ID_INEXISTENT) { + std::vector clients = m_clients.getClientIDs(); + + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) + continue; + + // Do not send to distant clients + if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + continue; + + SendSpawnParticle(*i, player->protocol_version, + pos, velocity, acceleration, + expirationtime, size, collisiondetection, + collision_removal, vertical, texture, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); @@ -1685,22 +1693,39 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat pkt.putLongString(texture); pkt << vertical; pkt << collision_removal; + // This is horrible but required (why are there two ways to serialize pkts?) + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id -void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, +void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version, + 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, bool collision_removal, - bool vertical, const std::string &texture, u32 id) + u16 attached_id, bool vertical, const std::string &texture, u32 id, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + if (peer_id == PEER_ID_INEXISTENT) { + // This sucks and should be replaced: + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + SendAddParticleSpawner(*i, player->protocol_version, + amount, spawntime, minpos, maxpos, + minvel, maxvel, minacc, maxacc, minexptime, maxexptime, + minsize, maxsize, collisiondetection, collision_removal, + attached_id, vertical, texture, id, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); @@ -1712,13 +1737,14 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 pkt << id << vertical; pkt << collision_removal; + pkt << attached_id; + // This is horrible but required + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) @@ -1734,7 +1760,7 @@ void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) Send(&pkt); } else { - m_clients.sendToAll(0, &pkt, true); + m_clients.sendToAll(&pkt); } } @@ -1809,7 +1835,8 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) } void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, - const std::string &type, const std::vector ¶ms) + const std::string &type, const std::vector ¶ms, + bool &clouds) { NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id); pkt << bgcolor << type << (u16) params.size(); @@ -1817,6 +1844,22 @@ void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, for(size_t i=0; im_messages_out.push(aom); } -void Server::SendPlayerBreath(u16 peer_id) +void Server::SendPlayerBreath(PlayerSAO *sao) { DSTACK(FUNCTION_NAME); - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); + assert(sao); - m_script->player_event(playersao, "breath_changed"); - SendBreath(peer_id, playersao->getBreath()); + m_script->player_event(sao, "breath_changed"); + SendBreath(sao->getPeerID(), sao->getBreath()); } void Server::SendMovePlayer(u16 peer_id) { DSTACK(FUNCTION_NAME); - Player *player = m_env->getPlayer(peer_id); + RemotePlayer *player = m_env->getPlayer(peer_id); assert(player); + PlayerSAO *sao = player->getPlayerSAO(); + assert(sao); NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id); - pkt << player->getPosition() << player->getPitch() << player->getYaw(); + pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw(); { - v3f pos = player->getPosition(); - f32 pitch = player->getPitch(); - f32 yaw = player->getYaw(); + v3f pos = sao->getBasePosition(); verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER" << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")" - << " pitch=" << pitch - << " yaw=" << yaw + << " pitch=" << sao->getPitch() + << " yaw=" << sao->getYaw() << std::endl; } @@ -1916,7 +1958,7 @@ void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third) } void Server::SendPlayerPrivileges(u16 peer_id) { - Player *player = m_env->getPlayer(peer_id); + RemotePlayer *player = m_env->getPlayer(peer_id); assert(player); if(player->peer_id == PEER_ID_INEXISTENT) return; @@ -1937,7 +1979,7 @@ void Server::SendPlayerPrivileges(u16 peer_id) void Server::SendPlayerInventoryFormspec(u16 peer_id) { - Player *player = m_env->getPlayer(peer_id); + RemotePlayer *player = m_env->getPlayer(peer_id); assert(player); if(player->peer_id == PEER_ID_INEXISTENT) return; @@ -1982,7 +2024,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec, std::vector dst_clients; if(params.to_player != "") { - Player *player = m_env->getPlayer(params.to_player.c_str()); + RemotePlayer *player = m_env->getPlayer(params.to_player.c_str()); if(!player){ infostream<<"Server::playSound: Player \""< clients = m_clients.getClientIDs(); - for(std::vector::iterator - i = clients.begin(); i != clients.end(); ++i) { - Player *player = m_env->getPlayer(*i); - if(!player) + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) continue; - if(pos_exists) { - if(player->getPosition().getDistanceFrom(pos) > + if (pos_exists) { + if(sao->getBasePosition().getDistanceFrom(pos) > params.max_hear_distance) continue; } @@ -2022,39 +2067,93 @@ s32 Server::playSound(const SimpleSoundSpec &spec, m_playing_sounds[id] = ServerPlayingSound(); ServerPlayingSound &psound = m_playing_sounds[id]; psound.params = params; + psound.spec = spec; + float gain = params.gain * spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); - pkt << id << spec.name << (float) (spec.gain * params.gain) - << (u8) params.type << pos << params.object << params.loop; + pkt << id << spec.name << gain + << (u8) params.type << pos << params.object + << params.loop << params.fade << params.pitch; + + // Backwards compability + bool play_sound = gain > 0; - for(std::vector::iterator i = dst_clients.begin(); + for (std::vector::iterator i = dst_clients.begin(); i != dst_clients.end(); ++i) { - psound.clients.insert(*i); - m_clients.send(*i, 0, &pkt, true); + if (play_sound || m_clients.getProtocolVersion(*i) >= 32) { + psound.clients.insert(*i); + m_clients.send(*i, 0, &pkt, true); + } } return id; } void Server::stopSound(s32 handle) { // Get sound reference - std::map::iterator i = - m_playing_sounds.find(handle); - if(i == m_playing_sounds.end()) + std::unordered_map::iterator i = + m_playing_sounds.find(handle); + if (i == m_playing_sounds.end()) return; ServerPlayingSound &psound = i->second; NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4); pkt << handle; - for(std::set::iterator i = psound.clients.begin(); - i != psound.clients.end(); ++i) { + for (std::unordered_set::const_iterator si = psound.clients.begin(); + si != psound.clients.end(); ++si) { // Send as reliable - m_clients.send(*i, 0, &pkt, true); + m_clients.send(*si, 0, &pkt, true); } // Remove sound reference m_playing_sounds.erase(i); } +void Server::fadeSound(s32 handle, float step, float gain) +{ + // Get sound reference + std::unordered_map::iterator i = + m_playing_sounds.find(handle); + if (i == m_playing_sounds.end()) + return; + + ServerPlayingSound &psound = i->second; + psound.params.gain = gain; + + NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4); + pkt << handle << step << gain; + + // Backwards compability + bool play_sound = gain > 0; + ServerPlayingSound compat_psound = psound; + compat_psound.clients.clear(); + + NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4); + compat_pkt << handle; + + for (std::unordered_set::iterator it = psound.clients.begin(); + it != psound.clients.end();) { + if (m_clients.getProtocolVersion(*it) >= 32) { + // Send as reliable + m_clients.send(*it, 0, &pkt, true); + ++it; + } else { + compat_psound.clients.insert(*it); + // Stop old sound + m_clients.send(*it, 0, &compat_pkt, true); + psound.clients.erase(it++); + } + } + + // Remove sound reference + if (!play_sound || psound.clients.size() == 0) + m_playing_sounds.erase(i); + + if (play_sound && compat_psound.clients.size() > 0) { + // Play new sound volume on older clients + playSound(compat_psound.spec, compat_psound.params); + } +} + void Server::sendRemoveNode(v3s16 p, u16 ignore_id, std::vector *far_players, float far_d_nodes) { @@ -2065,14 +2164,17 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, pkt << p; std::vector clients = m_clients.getClientIDs(); - for(std::vector::iterator i = clients.begin(); - i != clients.end(); ++i) { + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { if (far_players) { // Get player - if(Player *player = m_env->getPlayer(*i)) { + if (RemotePlayer *player = m_env->getPlayer(*i)) { + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) + continue; + // If player is far away, only set modified blocks not sent - v3f player_pos = player->getPosition(); - if(player_pos.getDistanceFrom(p_f) > maxd) { + v3f player_pos = sao->getBasePosition(); + if (player_pos.getDistanceFrom(p_f) > maxd) { far_players->push_back(*i); continue; } @@ -2092,14 +2194,16 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, v3f p_f = intToFloat(p, BS); std::vector clients = m_clients.getClientIDs(); - for(std::vector::iterator i = clients.begin(); - i != clients.end(); ++i) { - - if(far_players) { + for(std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + if (far_players) { // Get player - if(Player *player = m_env->getPlayer(*i)) { + if (RemotePlayer *player = m_env->getPlayer(*i)) { + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) + continue; + // If player is far away, only set modified blocks not sent - v3f player_pos = player->getPosition(); + v3f player_pos = sao->getBasePosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { far_players->push_back(*i); continue; @@ -2113,14 +2217,6 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, if (client != 0) { pkt << p << n.param0 << n.param1 << n.param2 << (u8) (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(getNodeBlockPos(p)); - } - } } m_clients.unlock(); @@ -2154,7 +2250,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false); - block->serializeNetworkSpecific(os, net_proto_version); + block->serializeNetworkSpecific(os); std::string s = os.str(); NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id); @@ -2343,7 +2439,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id); pkt << (u16) m_media.size(); - for (std::map::iterator i = m_media.begin(); + for (std::unordered_map::iterator i = m_media.begin(); i != m_media.end(); ++i) { pkt << i->first << i->second.sha1_digest; } @@ -2388,7 +2484,7 @@ void Server::sendRequestedMedia(u16 peer_id, i != tosend.end(); ++i) { const std::string &name = *i; - if(m_media.find(name) == m_media.end()) { + if (m_media.find(name) == m_media.end()) { errorstream<<"Server::sendRequestedMedia(): Client asked for " <<"unknown file \""<<(name)<<"\""<getPlayer(check.c_str()); + if (p) + m_clients.send(p->peer_id, 0, &pkt, true); + } else { + if (check == "" || getPlayerName(peer_id) == check) + Send(&pkt); } } @@ -2552,15 +2653,13 @@ void Server::RespawnPlayer(u16 peer_id) playersao->setHP(PLAYER_MAX_HP); playersao->setBreath(PLAYER_MAX_BREATH); - SendPlayerHP(peer_id); - SendPlayerBreath(peer_id); - bool repositioned = m_script->on_respawnplayer(playersao); - if(!repositioned){ - v3f pos = findSpawnPos(); + if (!repositioned) { // setPos will send the new position to client - playersao->setPos(pos); + playersao->setPos(findSpawnPos()); } + + SendPlayerHP(peer_id); } @@ -2649,44 +2748,32 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) /* Clear references to playing sounds */ - for(std::map::iterator - i = m_playing_sounds.begin(); - i != m_playing_sounds.end();) - { + for (std::unordered_map::iterator + i = m_playing_sounds.begin(); i != m_playing_sounds.end();) { ServerPlayingSound &psound = i->second; psound.clients.erase(peer_id); - if(psound.clients.empty()) + if (psound.clients.empty()) m_playing_sounds.erase(i++); else ++i; } - Player *player = m_env->getPlayer(peer_id); - - // 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)"; - } - } + RemotePlayer *player = m_env->getPlayer(peer_id); /* Run scripts and remove from environment */ - { - if(player != NULL) - { - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); - - m_script->on_leaveplayer(playersao); - - playersao->disconnected(); - } + if (player != NULL) { + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); + + // inform connected clients + NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT); + // (u16) 1 + std::string represents a vector serialization representation + notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName()); + m_clients.sendToAll(¬ice); + // run scripts + m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT); + + playersao->disconnected(); } /* @@ -2700,8 +2787,8 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) for(std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { // Get player - Player *player = m_env->getPlayer(*i); - if(!player) + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) continue; // Get name of player @@ -2728,7 +2815,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) SendChatMessage(PEER_ID_INEXISTENT,message); } -void Server::UpdateCrafting(Player* player) +void Server::UpdateCrafting(RemotePlayer *player) { DSTACK(FUNCTION_NAME); @@ -2738,7 +2825,8 @@ void Server::UpdateCrafting(Player* player) loc.setPlayer(player->getName()); std::vector output_replacements; getCraftingResult(&player->inventory, preview, output_replacements, false, this); - m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc); + m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), + (&player->inventory)->getList("craft"), loc); // Put the new preview in InventoryList *plist = player->inventory.getList("craftpreview"); @@ -2767,44 +2855,58 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt) } std::wstring Server::handleChat(const std::string &name, const std::wstring &wname, - const std::wstring &wmessage, bool check_shout_priv, - u16 peer_id_to_avoid_sending) + std::wstring wmessage, bool check_shout_priv, RemotePlayer *player) { // If something goes wrong, this player is to blame RollbackScopeActor rollback_scope(m_rollback, std::string("player:") + name); + if (g_settings->getBool("strip_color_codes")) + wmessage = unescape_enriched(wmessage); + + if (player) { + switch (player->canSendChatMessage()) { + case RPLAYER_CHATRESULT_FLOODING: { + std::wstringstream ws; + ws << L"You cannot send more messages. You are limited to " + << g_settings->getFloat("chat_message_limit_per_10sec") + << L" messages per 10 seconds."; + return ws.str(); + } + case RPLAYER_CHATRESULT_KICK: + DenyAccess_Legacy(player->peer_id, + L"You have been kicked due to message flooding."); + return L""; + case RPLAYER_CHATRESULT_OK: + break; + default: + FATAL_ERROR("Unhandled chat filtering result found."); + } + } + + if (m_max_chatmessage_length > 0 + && wmessage.length() > m_max_chatmessage_length) { + return L"Your message exceed the maximum chat message limit set on the server. " + L"It was refused. Send a shorter message"; + } + + // Run script hook, exit if script ate the chat message + if (m_script->on_chat_message(name, wide_to_utf8(wmessage))) + return L""; + // Line to send std::wstring line; // Whether to send line to the player that sent the message, or to all players bool broadcast_line = true; - // Run script hook - bool ate = m_script->on_chat_message(name, - wide_to_utf8(wmessage)); - // If script ate the message, don't proceed - if (ate) - return L""; - - // Commands are implemented in Lua, so only catch invalid - // commands that were not "eaten" and send an error back - if (wmessage[0] == L'/') { - std::wstring wcmd = wmessage.substr(1); + if (check_shout_priv && !checkPriv(name, "shout")) { + line += L"-!- You don't have permission to shout."; broadcast_line = false; - if (wcmd.length() == 0) - line += L"-!- Empty command"; - else - line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0]; } else { - if (check_shout_priv && !checkPriv(name, "shout")) { - line += L"-!- You don't have permission to shout."; - broadcast_line = false; - } else { - line += L"<"; - line += wname; - line += L"> "; - line += wmessage; - } + line += L"<"; + line += wname; + line += L"> "; + line += wmessage; } /* @@ -2816,10 +2918,19 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna /* Send the message to others */ - actionstream << "CHAT: " << wide_to_narrow(line) << std::endl; + actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl; std::vector clients = m_clients.getClientIDs(); + /* + Send the message back to the inital sender + if they are using protocol version >= 29 + */ + + u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT); + if (player && player->protocol_version >= 29) + peer_id_to_avoid_sending = PEER_ID_INEXISTENT; + for (u16 i = 0; i < clients.size(); i++) { u16 cid = clients[i]; if (cid != peer_id_to_avoid_sending) @@ -2858,16 +2969,16 @@ RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min) std::string Server::getPlayerName(u16 peer_id) { - Player *player = m_env->getPlayer(peer_id); - if(player == NULL) + RemotePlayer *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) + RemotePlayer *player = m_env->getPlayer(peer_id); + if (player == NULL) return NULL; return player->getPlayerSAO(); } @@ -2886,13 +2997,12 @@ std::wstring Server::getStatusString() bool first = true; os< clients = m_clients.getClientIDs(); - for(std::vector::iterator i = clients.begin(); - i != clients.end(); ++i) { + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { // Get player - Player *player = m_env->getPlayer(*i); + RemotePlayer *player = m_env->getPlayer(*i); // Get name of player std::wstring name = L"unknown"; - if(player != NULL) + if (player != NULL) name = narrow_to_wide(player->getName()); // Add name to information string if(!first) @@ -2928,12 +3038,12 @@ void Server::reportPrivsModified(const std::string &name) std::vector clients = m_clients.getClientIDs(); for(std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { - Player *player = m_env->getPlayer(*i); + RemotePlayer *player = m_env->getPlayer(*i); reportPrivsModified(player->getName()); } } else { - Player *player = m_env->getPlayer(name.c_str()); - if(!player) + RemotePlayer *player = m_env->getPlayer(name.c_str()); + if (!player) return; SendPlayerPrivileges(player->peer_id); PlayerSAO *sao = player->getPlayerSAO(); @@ -2947,8 +3057,8 @@ void Server::reportPrivsModified(const std::string &name) void Server::reportInventoryFormspecModified(const std::string &name) { - Player *player = m_env->getPlayer(name.c_str()); - if(!player) + RemotePlayer *player = m_env->getPlayer(name.c_str()); + if (!player) return; SendPlayerInventoryFormspec(player->peer_id); } @@ -2978,7 +3088,7 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg) m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg)); } - Player *player = m_env->getPlayer(name); + RemotePlayer *player = m_env->getPlayer(name); if (!player) { return; } @@ -2996,7 +3106,7 @@ bool Server::showFormspec(const char *playername, const std::string &formspec, if (!m_env) return false; - Player *player = m_env->getPlayer(playername); + RemotePlayer *player = m_env->getPlayer(playername); if (!player) return false; @@ -3004,7 +3114,7 @@ bool Server::showFormspec(const char *playername, const std::string &formspec, return true; } -u32 Server::hudAdd(Player *player, HudElement *form) +u32 Server::hudAdd(RemotePlayer *player, HudElement *form) { if (!player) return -1; @@ -3016,7 +3126,7 @@ u32 Server::hudAdd(Player *player, HudElement *form) return id; } -bool Server::hudRemove(Player *player, u32 id) { +bool Server::hudRemove(RemotePlayer *player, u32 id) { if (!player) return false; @@ -3031,7 +3141,7 @@ bool Server::hudRemove(Player *player, u32 id) { return true; } -bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) +bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data) { if (!player) return false; @@ -3040,7 +3150,7 @@ bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) return true; } -bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) +bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask) { if (!player) return false; @@ -3058,10 +3168,11 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) return true; } -bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) +bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount) { if (!player) return false; + if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX) return false; @@ -3072,14 +3183,7 @@ bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) return true; } -s32 Server::hudGetHotbarItemcount(Player *player) -{ - if (!player) - return 0; - return player->getHotbarItemcount(); -} - -void Server::hudSetHotbarImage(Player *player, std::string name) +void Server::hudSetHotbarImage(RemotePlayer *player, std::string name) { if (!player) return; @@ -3088,14 +3192,14 @@ void Server::hudSetHotbarImage(Player *player, std::string name) SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name); } -std::string Server::hudGetHotbarImage(Player *player) +std::string Server::hudGetHotbarImage(RemotePlayer *player) { if (!player) return ""; return player->getHotbarImage(); } -void Server::hudSetHotbarSelectedImage(Player *player, std::string name) +void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name) { if (!player) return; @@ -3104,16 +3208,8 @@ void Server::hudSetHotbarSelectedImage(Player *player, std::string name) SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name); } -std::string Server::hudGetHotbarSelectedImage(Player *player) -{ - if (!player) - return ""; - - return player->getHotbarSelectedImage(); -} - -bool Server::setLocalPlayerAnimations(Player *player, - v2s32 animation_frames[4], f32 frame_speed) +bool Server::setLocalPlayerAnimations(RemotePlayer *player, + v2s32 animation_frames[4], f32 frame_speed) { if (!player) return false; @@ -3123,7 +3219,7 @@ bool Server::setLocalPlayerAnimations(Player *player, return true; } -bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third) +bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third) { if (!player) return false; @@ -3134,18 +3230,35 @@ bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third) return true; } -bool Server::setSky(Player *player, const video::SColor &bgcolor, - const std::string &type, const std::vector ¶ms) +bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor, + const std::string &type, const std::vector ¶ms, + bool &clouds) +{ + if (!player) + return false; + + player->setSky(bgcolor, type, params, clouds); + SendSetSky(player->peer_id, bgcolor, type, params, clouds); + return true; +} + +bool Server::setClouds(RemotePlayer *player, float density, + const video::SColor &color_bright, + const video::SColor &color_ambient, + float height, + float thickness, + const v2f &speed) { if (!player) return false; - player->setSky(bgcolor, type, params); - SendSetSky(player->peer_id, bgcolor, type, params); + SendCloudParams(player->peer_id, density, + color_bright, color_ambient, height, + thickness, speed); return true; } -bool Server::overrideDayNightRatio(Player *player, bool do_override, +bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, float ratio) { if (!player) @@ -3165,49 +3278,61 @@ void Server::spawnParticle(const std::string &playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { - Player* player = m_env->getPlayer(playername.c_str()); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return; peer_id = player->peer_id; + proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, pos, velocity, acceleration, + SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, expirationtime, size, collisiondetection, - collision_removal, vertical, texture); + collision_removal, vertical, texture, animation, glow); } u32 Server::addParticleSpawner(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, bool collision_removal, - bool vertical, const std::string &texture, - const std::string &playername) + ServerActiveObject *attached, bool vertical, const std::string &texture, + const std::string &playername, const struct TileAnimationParams &animation, + u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return -1; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { - Player* player = m_env->getPlayer(playername.c_str()); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return -1; peer_id = player->peer_id; + proto_ver = player->protocol_version; } - u32 id = m_env->addParticleSpawner(spawntime); - SendAddParticleSpawner(peer_id, amount, spawntime, + u16 attached_id = attached ? attached->getId() : 0; + + u32 id; + if (attached_id == 0) + id = m_env->addParticleSpawner(spawntime); + else + id = m_env->addParticleSpawner(spawntime, attached_id); + + SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, - collisiondetection, collision_removal, vertical, texture, id); + collisiondetection, collision_removal, attached_id, vertical, + texture, id, animation, glow); return id; } @@ -3220,7 +3345,7 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) u16 peer_id = PEER_ID_INEXISTENT; if (playername != "") { - Player* player = m_env->getPlayer(playername.c_str()); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return; peer_id = player->peer_id; @@ -3230,13 +3355,7 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -void Server::deleteParticleSpawnerAll(u32 id) -{ - m_env->deleteParticleSpawner(id); - SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id); -} - -Inventory* Server::createDetachedInventory(const std::string &name) +Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player) { if(m_detached_inventories.count(name) > 0){ infostream<<"Server clearing detached inventory \""<allocateDummy(name); } -ISoundManager *Server::getSoundManager() -{ - return &dummySoundManager; -} - MtEventManager *Server::getEventManager() { return m_event; @@ -3381,6 +3484,11 @@ std::string Server::getBuiltinLuaPath() return porting::path_share + DIR_DELIM + "builtin"; } +std::string Server::getModStoragePath() const +{ + return m_path_world + DIR_DELIM + "mod_storage"; +} + v3f Server::findSpawnPos() { ServerMap &map = m_env->getServerMap(); @@ -3390,14 +3498,16 @@ v3f Server::findSpawnPos() } bool is_good = false; + // Limit spawn range to mapgen edges (determined by 'mapgen_limit') + s32 range_max = map.getMapgenParams()->getSpawnRangeMax(); // Try to find a good place a few times for(s32 i = 0; i < 4000 && !is_good; i++) { - s32 range = 1 + i; + s32 range = MYMIN(1 + i, range_max); // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( - -range + (myrand() % (range * 2)), - -range + (myrand() % (range * 2))); + -range + (myrand() % (range * 2)), + -range + (myrand() % (range * 2))); // Get spawn level at point s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d); @@ -3431,18 +3541,52 @@ v3f Server::findSpawnPos() return nodeposf; } -PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version) +void Server::requestShutdown(const std::string &msg, bool reconnect, float delay) { - bool newplayer = false; + m_shutdown_timer = delay; + m_shutdown_msg = msg; + m_shutdown_ask_reconnect = reconnect; + + if (delay == 0.0f) { + // No delay, shutdown immediately + m_shutdown_requested = true; + // only print to the infostream, a chat message saying + // "Server Shutting Down" is sent when the server destructs. + infostream << "*** Immediate Server shutdown requested." << std::endl; + } else if (delay < 0.0f && m_shutdown_timer > 0.0f) { + // Negative delay, cancel shutdown if requested + m_shutdown_timer = 0.0f; + m_shutdown_msg = ""; + m_shutdown_ask_reconnect = false; + m_shutdown_requested = false; + std::wstringstream ws; + + ws << L"*** Server shutdown canceled."; + + infostream << wide_to_utf8(ws.str()).c_str() << std::endl; + SendChatMessage(PEER_ID_INEXISTENT, ws.str()); + } else if (delay > 0.0f) { + // Positive delay, tell the clients when the server will shut down + std::wstringstream ws; + + ws << L"*** Server shutting down in " + << duration_to_string(myround(m_shutdown_timer)).c_str() + << "."; + infostream << wide_to_utf8(ws.str()).c_str() << std::endl; + SendChatMessage(PEER_ID_INEXISTENT, ws.str()); + } +} + +PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version) +{ /* Try to get an existing player */ - RemotePlayer *player = static_cast(m_env->getPlayer(name)); + RemotePlayer *player = m_env->getPlayer(name); // If player is already connected, cancel - if(player != NULL && player->peer_id != 0) - { + if (player != NULL && player->peer_id != 0) { infostream<<"emergePlayer(): Player already connected"<getPlayer(peer_id) != NULL) - { + if (m_env->getPlayer(peer_id) != NULL) { infostream<<"emergePlayer(): Player with wrong name but same" " peer_id already exists"<(m_env->loadPlayer(name)); + player = new RemotePlayer(name, idef()); } - // Create player if it doesn't exist - if (!player) { - newplayer = true; - player = new RemotePlayer(this, name); - // Set player position - infostream<<"Server: Finding spawn place for player \"" - <setPosition(pos); - - // Make sure the player is saved - player->setModified(true); - - // Add player to environment - m_env->addPlayer(player); - } else { - // If the player exists, ensure that they respawn inside legal bounds - // This fixes an assert crash when the player can't be added - // to the environment - if (objectpos_over_limit(player->getPosition())) { - actionstream << "Respawn position for player \"" - << name << "\" outside limits, resetting" << std::endl; - v3f pos = findSpawnPos(); - player->setPosition(pos); - } - } + bool newplayer = false; - // Create a new player active object - PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id, - getPlayerEffectivePrivs(player->getName()), - isSingleplayer()); + // Load player + PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer()); + // Complete init with server parts + playersao->finalize(player, getPlayerEffectivePrivs(player->getName())); player->protocol_version = proto_version; - /* Clean up old HUD elements from previous sessions */ - player->clearHud(); - - /* Add object to environment */ - m_env->addActiveObject(playersao); - /* Run scripts */ if (newplayer) { m_script->on_newplayer(playersao); @@ -3510,6 +3621,28 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version return playersao; } +bool Server::registerModStorage(ModMetadata *storage) +{ + if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) { + errorstream << "Unable to register same mod storage twice. Storage name: " + << storage->getModName() << std::endl; + return false; + } + + m_mod_storages[storage->getModName()] = storage; + return true; +} + +void Server::unregisterModStorage(const std::string &name) +{ + std::unordered_map::const_iterator it = m_mod_storages.find(name); + if (it != m_mod_storages.end()) { + // Save unconditionaly on unregistration + it->second->save(getModStoragePath()); + m_mod_storages.erase(name); + } +} + void dedicated_server_loop(Server &server, bool &kill) { DSTACK(FUNCTION_NAME); @@ -3518,8 +3651,9 @@ void dedicated_server_loop(Server &server, bool &kill) IntervalLimiter m_profiler_interval; - static const float steplen = g_settings->getFloat("dedicated_server_step"); - static const float profiler_print_interval = + static thread_local const float steplen = + g_settings->getFloat("dedicated_server_step"); + static thread_local const float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); for(;;) { @@ -3531,15 +3665,8 @@ void dedicated_server_loop(Server &server, bool &kill) } server.step(steplen); - if(server.getShutdownRequested() || kill) - { - infostream<<"Dedicated server quitting"<getBool("server_announce")) - ServerList::sendAnnounce("delete", server.m_bind_addr.getPort()); -#endif + if (server.getShutdownRequested() || kill) break; - } /* Profiler @@ -3553,4 +3680,11 @@ void dedicated_server_loop(Server &server, bool &kill) } } } + + infostream << "Dedicated server quitting" << std::endl; +#if USE_CURL + if (g_settings->getBool("server_announce")) + ServerList::sendAnnounce(ServerList::AA_DELETE, + server.m_bind_addr.getPort()); +#endif }