diff --git a/builtin/mainmenu/serverlistmgr.lua b/builtin/mainmenu/serverlistmgr.lua index 34eca287a..3ac52b657 100644 --- a/builtin/mainmenu/serverlistmgr.lua +++ b/builtin/mainmenu/serverlistmgr.lua @@ -9,7 +9,7 @@ serverlistmgr = { -- list of locally favorites servers favorites = nil, - -- list of servers fetched from public list + -- list of servers fetched from public list and local servers servers = nil, } @@ -140,6 +140,14 @@ local function fetch_geoip() end function serverlistmgr.sync() + if minetest.settings:get("serverlist_lan") then + if core.ask_lan_servers then --This checks if the function exists before running it. + core.ask_lan_servers(); + else + print("core.ask_lan_servers isn't defined.") + end + end + if not serverlistmgr.servers then serverlistmgr.servers = {{ name = fgettext("Loading..."), diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index ab5ddfe11..a17595401 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -4,14 +4,27 @@ local function get_sorted_servers() local servers = { + lan = {}, fav = {}, public = {}, incompatible = {} } + local serverlist = table.copy(serverlistmgr.servers) + + if minetest.settings:get_bool("serverlist_lan") then + local lan_servers = core.get_lan_servers() + + for _, server in ipairs(lan_servers) do + server.is_compatible = is_server_protocol_compat(server.proto_min, server.proto_max) + server.is_local = true + table.insert(serverlist, server) + end + end + local favs = serverlistmgr.get_favorites() local taken_favs = {} - local result = menudata.search_result or serverlistmgr.servers + local result = menudata.search_result or serverlist for _, server in ipairs(result) do server.is_favorite = false for index, fav in ipairs(favs) do @@ -25,7 +38,11 @@ local function get_sorted_servers() if server.is_favorite then table.insert(servers.fav, server) elseif server.is_compatible then - table.insert(servers.public, server) + if server.is_local then + table.insert(servers.lan, server) + else + table.insert(servers.public, server) + end else table.insert(servers.incompatible, server) end @@ -249,11 +266,12 @@ local function get_formspec(tabview, name, tabdata) local servers = get_sorted_servers() local dividers = { + lan = "1,#00ff00," .. fgettext("Lan") .. ",,,0,0,,", fav = "5,#ffff00," .. fgettext("Favorites") .. ",,,0,0,,", public = "6,#4bdd42," .. fgettext("Public Servers") .. ",,,0,0,,", incompatible = "7,"..mt_color_grey.."," .. fgettext("Incompatible Servers") .. ",,,0,0,," } - local order = {"fav", "public", "incompatible"} + local order = {"lan", "fav", "public", "incompatible"} tabdata.lookup = {} -- maps row number to server local rows = {} diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 17ecf5b34..b419f1247 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1044,6 +1044,9 @@ server_announce (Announce server) bool false # Send names of online players to the serverlist. If disabled only the player count is revealed. server_announce_send_players (Send player names to the server list) bool true +# Make local servers visible to local clients. +serverlist_lan (Show local servers) bool true + # Announce to this serverlist. serverlist_url (Serverlist URL) [common] string https://servers.luanti.org diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 64f2e8f51..d4ef6a213 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -23,6 +23,7 @@ #include #include #include +#include "server/serverlist.h" #if USE_SOUND #include "sound/sound_openal.h" @@ -540,6 +541,7 @@ bool ClientLauncher::launch_game(std::string &error_message, void ClientLauncher::main_menu(MainMenuData *menudata) { + ServerList::lan_get(); bool *kill = porting::signal_handler_killstatus(); video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); auto *device = m_rendering_engine->get_raw_device(); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 916ce9e51..4d57b41af 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -533,6 +533,7 @@ void set_default_settings() Mapgen::setDefaultSettings(settings); // Server list announcing + settings->setDefault("serverlist_lan", "true"); settings->setDefault("server_announce", "false"); settings->setDefault("server_url", ""); settings->setDefault("server_address", ""); diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index d9e674304..ab4ea6231 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,6 +1,7 @@ set(common_network_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/address.cpp ${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/lan.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mtp/impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mtp/threads.cpp ${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp diff --git a/src/network/address.h b/src/network/address.h index 9a53e7f63..7b08ea50b 100644 --- a/src/network/address.h +++ b/src/network/address.h @@ -31,6 +31,9 @@ public: Address(u32 address, u16 port); Address(u8 a, u8 b, u8 c, u8 d, u16 port); Address(const IPv6AddressBytes *ipv6_bytes, u16 port); + Address(const in6_addr & addr, u16 port) { setAddress(addr); setPort(port); }; + Address(const sockaddr_in6 & sai) { m_address.ipv6 = sai.sin6_addr; m_addr_family = sai.sin6_family; m_port = ntohs(sai.sin6_port); }; + Address(const sockaddr_in & sai) { m_address.ipv4 = sai.sin_addr; m_addr_family = sai.sin_family; m_port = ntohs(sai.sin_port); }; bool operator==(const Address &address) const; bool operator!=(const Address &address) const { return !(*this == address); } @@ -59,6 +62,7 @@ public: void setAddress(u32 address); void setAddress(u8 a, u8 b, u8 c, u8 d); void setAddress(const IPv6AddressBytes *ipv6_bytes); + void setAddress(const in6_addr & addr) { m_address.ipv6 = addr; m_addr_family = AF_INET6;} void setPort(u16 port); private: diff --git a/src/network/lan.cpp b/src/network/lan.cpp new file mode 100644 index 000000000..1dd37d2d8 --- /dev/null +++ b/src/network/lan.cpp @@ -0,0 +1,355 @@ +/* +Luanti +Copyright (C) 2024 proller and contributors. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "network/lan.h" +#include +#include "convert_json.h" +#include "socket.h" +#include "log.h" +#include "settings.h" +#include "version.h" +#include "networkprotocol.h" +#include "server/serverlist.h" +#include "debug.h" +#include "json/json.h" +#include +#include +#include "porting.h" +#include "threading/thread.h" +#include "network/address.h" + +//copypaste from ../socket.cpp +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +// Without this some of the network functions are not found on mingw +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#include +#include +#include +#define LAST_SOCKET_ERR() WSAGetLastError() +typedef SOCKET socket_t; +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include + +#ifndef __ANDROID__ +#include +#define HAVE_IFADDRS 1 +#endif + +#define LAST_SOCKET_ERR() (errno) +typedef int socket_t; +#endif + +const char* adv_multicast_addr = "224.1.1.1"; +const static unsigned short int adv_port = 29998; +const std::string proto = "lanti"; +static std::string ask_str; + +bool use_ipv6 = true; + +lan_adv::lan_adv() : Thread("lan_adv") +{ +} + +void lan_adv::ask() +{ + if (!isRunning()) start(); + + if (ask_str.empty()) { + Json::Value j; + j["cmd"] = "ask"; + j["proto"] = proto; + ask_str = fastWriteJson(j); + } + + send_string(ask_str); +} + +void lan_adv::send_string(const std::string &str) +{ + if (g_settings->getBool("ipv6_server")) { + std::vector scopes; + // todo: windows and android + +#if HAVE_IFADDRS + struct ifaddrs *ifaddr = nullptr, *ifa = nullptr; + if (getifaddrs(&ifaddr) < 0) { + } else { + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + auto sa = *((struct sockaddr_in6 *)ifa->ifa_addr); + if (sa.sin6_scope_id) + scopes.push_back(sa.sin6_scope_id); + + /*errorstream<<"in=" << ifa->ifa_name << " a="<ifa_addr)).serializeString()<<" ba=" << ifa->ifa_broadaddr <<" sc=" << sa.sin6_scope_id <<" fl=" << ifa->ifa_flags + //<< " bn=" << Address(*((struct sockaddr_in6*)ifa->ifa_broadaddr)).serializeString() + <<"\n"; */ + } + } + freeifaddrs(ifaddr); +#endif + + if (scopes.empty()) + scopes.push_back(0); + + struct addrinfo hints + { + }; + + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + struct addrinfo *result = nullptr; + if (!getaddrinfo("ff02::1", nullptr, &hints, &result)) { + for (auto info = result; info; info = info->ai_next) { + try { + sockaddr_in6 addr = *((struct sockaddr_in6 *)info->ai_addr); + addr.sin6_port = htons(adv_port); + UDPSocket socket_send(true); + int set_option_on = 1; + //setsockopt(socket_send.GetHandle(), SOL_SOCKET, IPV6_MULTICAST_HOPS, + // (const char *)&set_option_on, sizeof(set_option_on)); + auto use_scopes = scopes; + if (addr.sin6_scope_id) { + use_scopes.clear(); + use_scopes.push_back(addr.sin6_scope_id); + } + for (auto &scope : use_scopes) { + addr.sin6_scope_id = scope; + socket_send.Send(Address(addr), str.c_str(), str.size()); + } + } catch (const std::exception &e) { + verbosestream << "udp multicast send over ipv6 fail [" << e.what() << "]\n" << "Trying ipv4.\n"; + try { + sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_port = htons(adv_port); + UDPSocket socket_send(false); + + inet_pton(AF_INET, adv_multicast_addr, &(addr.sin_addr)); + + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = inet_addr(adv_multicast_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + setsockopt(socket_send.GetHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&mreq, sizeof(mreq)); + + //int set_option_on = 2; + //setsockopt(socket_send.GetHandle(), SOL_SOCKET, IP_MULTICAST_TTL, + // (const char *)&set_option_on, sizeof(set_option_on)); + + socket_send.Send(Address(addr), str.c_str(), str.size()); + } catch (const std::exception &e) { + verbosestream << "udp mulitcast send over ipv4 fail too. " << e.what() << "\n"; + } + } + } + freeaddrinfo(result); + } + } else { + try { + sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_port = htons(adv_port); + UDPSocket socket_send(false); + + inet_pton(AF_INET, adv_multicast_addr, &(addr.sin_addr)); + + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = inet_addr(adv_multicast_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + setsockopt(socket_send.GetHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&mreq, sizeof(mreq)); + + //int set_option_on = 2; + //setsockopt(socket_send.GetHandle(), SOL_SOCKET, IP_MULTICAST_TTL, + // (const char *)&set_option_on, sizeof(set_option_on)); + + socket_send.Send(Address(addr), str.c_str(), str.size()); + } catch (const std::exception &e) { + verbosestream << "udp mulitcast send over ipv4 fail " << e.what() << "\n"; + } + } +} + +void lan_adv::serve(unsigned short port) +{ + server_port = port; + stop(); + start(); +} + +void *lan_adv::run() +{ + BEGIN_DEBUG_EXCEPTION_HANDLER; + + setName("lan_adv " + (server_port ? std::string("server") : std::string("client"))); + UDPSocket socket_recv(g_settings->getBool("ipv6_server")); + + int set_option_off = 0, set_option_on = 1; + setsockopt(socket_recv.GetHandle(), SOL_SOCKET, SO_REUSEADDR, + (const char *)&set_option_on, sizeof(set_option_on)); +#ifdef SO_REUSEPORT + setsockopt(socket_recv.GetHandle(), SOL_SOCKET, SO_REUSEPORT, + (const char *)&set_option_on, sizeof(set_option_on)); +#endif + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = inet_addr(adv_multicast_addr); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + setsockopt(socket_recv.GetHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&mreq, sizeof(mreq)); + setsockopt(socket_recv.GetHandle(), IPPROTO_IPV6, IPV6_V6ONLY, + (const char *)&set_option_off, sizeof(set_option_off)); + socket_recv.setTimeoutMs(200); + + if (g_settings->getBool("ipv6_server")) { + try { + socket_recv.Bind(Address(in6addr_any, adv_port)); + } catch (const std::exception &e) { + warningstream << m_name << ": cant bind ipv6 address [" << e.what() + << "], trying ipv4. " << std::endl; + try { + socket_recv.Bind(Address((u32)INADDR_ANY, adv_port)); + } catch (const std::exception &e) { + warningstream << m_name << ": cant bind ipv4 too [" << e.what() << "]" + << std::endl; + return nullptr; + } + } + } else { + try { + socket_recv.Bind(Address((u32)INADDR_ANY, adv_port)); + } catch (const std::exception &e) { + warningstream << m_name << ": cant bind ipv4 [" << e.what() << "]" + << std::endl; + return nullptr; + } + } + std::unordered_map limiter; + const unsigned int packet_maxsize = 16384; + char buffer[packet_maxsize]; + Json::Reader reader; + std::string answer_str; + Json::Value server; + if (server_port) { + server["name"] = g_settings->get("server_name"); + server["description"] = g_settings->get("server_description"); + server["version"] = g_version_string; + bool strict_checking = g_settings->getBool("strict_protocol_version_checking"); + server["proto_min"] = + strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MIN; + server["proto_max"] = LATEST_PROTOCOL_VERSION; + server["url"] = g_settings->get("server_url"); + server["creative"] = g_settings->getBool("creative_mode"); + server["damage"] = g_settings->getBool("enable_damage"); + server["password"] = g_settings->getBool("disallow_empty_password"); + server["pvp"] = g_settings->getBool("enable_pvp"); + server["port"] = server_port; + server["clients"] = clients_num.load(); + server["clients_max"] = g_settings->getU16("max_users"); + server["proto"] = proto; + + send_string(fastWriteJson(server)); + } + + while (isRunning() && !stopRequested()) { + BEGIN_DEBUG_EXCEPTION_HANDLER; + Address addr; + int rlen = socket_recv.Receive(addr, buffer, packet_maxsize); + if (rlen <= 0) + continue; + Json::Value p; + if (!reader.parse(std::string(buffer, rlen), p)) { + //errorstream << "cant parse "<< s << "\n"; + continue; + } + auto addr_str = addr.serializeString(); + auto now = porting::getTimeMs(); + //errorstream << " a=" << addr.serializeString() << " : " << addr.getPort() << " l=" << rlen << " b=" << p << " ; server=" << server_port << "\n"; + + if (server_port) { + if (p["cmd"] == "ask" && limiter[addr_str] < now) { + (clients_num.load() ? infostream : actionstream) + << "lan: want play " << addr_str << " " << p["proto"] << std::endl; + + server["clients"] = clients_num.load(); + answer_str = fastWriteJson(server); + limiter[addr_str] = now + 3000; + send_string(answer_str); + //UDPSocket socket_send(true); + //addr.setPort(adv_port); + //socket_send.Send(addr, answer_str.c_str(), answer_str.size()); + } + } else { + if (p["cmd"] == "ask") { + actionstream << "lan: want play " << addr_str << " " << p["proto"] << std::endl; + } + if (p["port"].isInt()) { + p["address"] = addr_str; + auto key = addr_str + ":" + p["port"].asString(); + std::unique_lock lock(mutex); + if (p["cmd"].asString() == "shutdown") { + //infostream << "server shutdown " << key << "\n"; + collected.erase(key); + fresh = true; + } else if (p["proto"] == proto) { + if (!collected.count(key)) + actionstream << "lan server start " << key << "\n"; + collected.insert_or_assign(key, p); + fresh = true; + } + } + + //errorstream<<" current list: ";for (auto & i : collected) {errorstream<< i.first <<" ; ";}errorstream< and contributors. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "json/json.h" +#include +#include +#include +#include +#include "threading/thread.h" + + +class lan_adv : public Thread +{ +public: + void *run(); + + lan_adv(); + void ask(); + void send_string(const std::string &str); + + void serve(unsigned short port); + + std::map collected; + std::shared_mutex mutex; + + std::atomic_bool fresh; + std::atomic_int clients_num; + +private: + unsigned short server_port = 0; +}; diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 4b878ee1c..3b01f6614 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -17,6 +17,7 @@ #include "convert_json.h" #include "content/content.h" #include "content/subgames.h" +#include "server/serverlist.h" #include "mapgen/mapgen.h" #include "settings.h" #include "clientdynamicinfo.h" @@ -27,6 +28,7 @@ #include "content/mod_configuration.h" #include "threading/mutex_auto_lock.h" #include "common/c_converter.h" +#include "json-forwards.h" #include "gui/guiOpenURL.h" #include "gettext.h" #include "log.h" @@ -1038,6 +1040,44 @@ int ModApiMainMenu::l_do_async_callback(lua_State *L) return 1; } +/******************************************************************************/ + +int ModApiMainMenu::l_ask_lan_servers(lua_State *L) +{ + ServerList::lan_get(); + return 0; +} + +int ModApiMainMenu::l_get_lan_servers(lua_State *L) +{ + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + + std::shared_lock lock(ServerList::lan_adv_client.mutex); + for (const auto &server : ServerList::lan_adv_client.collected) { + lua_pushnumber(L, index); + + lua_newtable(L); + int top_lvl2 = lua_gettop(L); + + for (const auto &field_name : server.second.getMemberNames()) { + lua_pushstring(L, field_name.c_str()); + if (server.second[field_name].isString()) + lua_pushstring(L, server.second[field_name].asCString()); + else if (server.second[field_name].isConvertibleTo(Json::realValue)) + lua_pushnumber(L, server.second[field_name].asDouble()); + else + lua_pushnil(L); + lua_settable(L, top_lvl2); + } + + lua_settable(L, top); + ++index; + } + return 1; +} + /******************************************************************************/ void ModApiMainMenu::Initialize(lua_State *L, int top) { @@ -1089,7 +1129,8 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(open_dir); API_FCT(share_file); API_FCT(do_async_callback); - + API_FCT(ask_lan_servers); + API_FCT(get_lan_servers); lua_pushboolean(L, g_first_run); lua_setfield(L, top, "is_first_run"); } @@ -1120,4 +1161,6 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top) API_FCT(get_max_supp_proto); API_FCT(get_formspec_version); API_FCT(get_language); + API_FCT(get_lan_servers); + API_FCT(ask_lan_servers); } diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index fc2d90af8..75a5cf41a 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -37,6 +37,20 @@ private: */ static int getBoolData(lua_State *L, const std::string &name ,bool& valid); + /** + * Checks if a path may be modified. Paths in the temp directory or the user + * games, mods, textures, or worlds directories may be modified. + * @param path path to check + * @return true if the path may be modified + */ + static bool mayModifyPath(std::string path); + + //lan discovery + + static int l_ask_lan_servers(lua_State *L); + + static int l_get_lan_servers(lua_State *L); + //api calls static int l_start(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index a1263ef1d..ed6f2d490 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -63,8 +63,10 @@ #include "gameparams.h" #include "particles.h" #include "gettext.h" +#include "network/lan.h" #include "util/tracy_wrapper.h" + class ClientNotFoundException : public BaseException { public: @@ -427,6 +429,17 @@ Server::~Server() delete m_unsent_map_edit_queue.front(); m_unsent_map_edit_queue.pop(); } + + if (g_settings->getBool("serverlist_lan")) { + lan_adv_server.stop(); + + while (lan_adv_server.isRunning()) { + // Wait until the lan_adv_server thread has finished. + // This is so that its thread destructor doesn't kill the thread + // before it sends the 'shutdown' command to remove this server's + // server info from the serverlist of local clients. + } + } } void Server::init() @@ -578,6 +591,10 @@ void Server::start() // Start thread m_thread->start(); + if (!m_simple_singleplayer_mode && g_settings->getBool("serverlist_lan")) { + lan_adv_server.serve(m_bind_addr.getPort()); + }; + // ASCII art for the win! const char *art[] = { R"( _ _ _ )", @@ -802,6 +819,10 @@ void Server::AsyncRunStep(float dtime, bool initial_step) } #endif + if (!isSingleplayer() && g_settings->getBool("serverlist_lan")) { + lan_adv_server.clients_num = m_clients.getPlayerNames().size(); + }; + /* Check added and deleted active objects */ diff --git a/src/server.h b/src/server.h index ae5b10682..8e9aa1b62 100644 --- a/src/server.h +++ b/src/server.h @@ -4,6 +4,7 @@ #pragma once +#include "network/lan.h" #include "irr_v3d.h" #include "map.h" #include "hud.h" @@ -711,6 +712,9 @@ private: // The server mainly operates in this thread ServerThread *m_thread = nullptr; + // For local server discovery. + lan_adv lan_adv_server; + /* Client interface */ diff --git a/src/server/serverlist.cpp b/src/server/serverlist.cpp index ca82bbc52..d3419a6ed 100644 --- a/src/server/serverlist.cpp +++ b/src/server/serverlist.cpp @@ -13,9 +13,27 @@ #include "convert_json.h" #include "httpfetch.h" #include "server.h" +#include "network/lan.h" +#include "json/json.h" namespace ServerList { + +static std::string ask_str; + +lan_adv lan_adv_client; + +void lan_get() { + if (!g_settings->getBool("serverlist_lan")) + return; + lan_adv_client.ask(); +} + +bool lan_fresh() { + auto result = lan_adv_client.fresh.load(); + lan_adv_client.fresh = false; + return result; +} #if USE_CURL void sendAnnounce(AnnounceAction action, const u16 port, diff --git a/src/server/serverlist.h b/src/server/serverlist.h index fd7551b69..a5b12de9d 100644 --- a/src/server/serverlist.h +++ b/src/server/serverlist.h @@ -6,6 +6,7 @@ #include "content/mods.h" #include "json-forwards.h" #include +#include "network/lan.h" #pragma once @@ -13,6 +14,10 @@ namespace ServerList { + extern lan_adv lan_adv_client; + void lan_get(); + bool lan_fresh(); + #if USE_CURL enum AnnounceAction {AA_START, AA_UPDATE, AA_DELETE}; void sendAnnounce(AnnounceAction, u16 port,