mirror of
https://github.com/minetest/minetest.git
synced 2024-10-05 17:33:18 +02:00
Basic Lan Implmentation
This commit is contained in:
parent
bc23a610d3
commit
f03922f218
@ -153,6 +153,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..."),
|
||||
|
@ -22,6 +22,24 @@ local function get_sorted_servers()
|
||||
incompatible = {}
|
||||
}
|
||||
|
||||
--Special thanks to proller <proller@gmail.com> for letting use use the get_lan_servers function.
|
||||
if minetest.settings:get_bool("serverlist_lan") then
|
||||
if core.get_lan_servers then
|
||||
servers.lan = core.get_lan_servers();
|
||||
|
||||
for _, server in ipairs(servers.lan) do
|
||||
server.is_compatible = is_server_protocol_compat(server.proto_min, server.proto_max)
|
||||
end
|
||||
else
|
||||
print("core.get_lan_servers isn't defined.")
|
||||
servers.lan = {}
|
||||
end
|
||||
else
|
||||
servers.lan = {}
|
||||
end
|
||||
|
||||
print(dump(servers.lan))
|
||||
|
||||
local favs = serverlistmgr.get_favorites()
|
||||
local taken_favs = {}
|
||||
local result = menudata.search_result or serverlistmgr.servers
|
||||
@ -154,11 +172,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 = {}
|
||||
|
@ -819,6 +819,9 @@ server_url (Server URL) string https://minetest.net
|
||||
# Automatically report to the serverlist.
|
||||
server_announce (Announce server) bool false
|
||||
|
||||
# Make local servers visible to local clients.
|
||||
serverlist_lan (Show local servers) bool true
|
||||
|
||||
# Announce to this serverlist.
|
||||
serverlist_url (Serverlist URL) string servers.minetest.net
|
||||
|
||||
|
@ -417,6 +417,7 @@ set(common_SRCS
|
||||
light.cpp
|
||||
lighting.cpp
|
||||
log.cpp
|
||||
#log_types.cpp
|
||||
main.cpp
|
||||
map.cpp
|
||||
map_settings_manager.cpp
|
||||
|
@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <IGUISpriteBank.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <unordered_map>
|
||||
#include "server/serverlist.h"
|
||||
|
||||
#if USE_SOUND
|
||||
#include "sound/sound_openal.h"
|
||||
@ -536,6 +537,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();
|
||||
|
||||
|
@ -78,7 +78,7 @@ void debug_set_exception_handler();
|
||||
These should be put into every thread
|
||||
*/
|
||||
|
||||
#if CATCH_UNHANDLED_EXCEPTIONS == 1
|
||||
#if CATCH_UNHANDLED_EXCEPTIONS == 0
|
||||
#define BEGIN_DEBUG_EXCEPTION_HANDLER try {
|
||||
#define END_DEBUG_EXCEPTION_HANDLER \
|
||||
} catch (std::exception &e) { \
|
||||
|
@ -529,6 +529,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", "");
|
||||
|
96
src/fm_porting.h
Normal file
96
src/fm_porting.h
Normal file
@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "porting.h"
|
||||
|
||||
#if defined(linux) || defined(__linux)
|
||||
#include <sys/prctl.h>
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
#elif defined(__NetBSD__)
|
||||
#include <pthread.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined(linux) || defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define PORTING_USE_PTHREAD 1
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace porting
|
||||
{
|
||||
|
||||
extern std::atomic_bool g_sighup, g_siginfo;
|
||||
|
||||
|
||||
#if defined(linux) || defined(__linux)
|
||||
inline void setThreadName(const char *name) {
|
||||
/* It would be cleaner to do this with pthread_setname_np,
|
||||
* which was added to glibc in version 2.12, but some major
|
||||
* distributions are still runing 2.11 and previous versions.
|
||||
*/
|
||||
prctl(PR_SET_NAME, name);
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
inline void setThreadName(const char *name) {
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
}
|
||||
#elif defined(__NetBSD__)
|
||||
inline void setThreadName(const char *name) {
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
}
|
||||
#elif defined(_MSC_VER)
|
||||
typedef struct tagTHREADNAME_INFO {
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} THREADNAME_INFO;
|
||||
|
||||
inline void setThreadName(const char *name) {
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = -1;
|
||||
info.dwFlags = 0;
|
||||
__try {
|
||||
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *) &info);
|
||||
} __except (EXCEPTION_CONTINUE_EXECUTION) {}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
inline void setThreadName(const char *name) {
|
||||
pthread_setname_np(name);
|
||||
}
|
||||
#elif defined(_WIN32) || defined(__GNU__)
|
||||
inline void setThreadName(const char* name) {}
|
||||
#else
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#warning "Unrecognized platform, thread names will not be available."
|
||||
#endif
|
||||
|
||||
inline void setThreadName(const char* name) {}
|
||||
#endif
|
||||
|
||||
inline void setThreadPriority(int priority) {
|
||||
#if PORTING_USE_PTHREAD
|
||||
// http://en.cppreference.com/w/cpp/thread/thread/native_handle
|
||||
sched_param sch;
|
||||
//int policy;
|
||||
//pthread_getschedparam(pthread_self(), &policy, &sch);
|
||||
sch.sched_priority = priority;
|
||||
if(pthread_setschedparam(pthread_self(), SCHED_FIFO /*SCHED_RR*/, &sch)) {
|
||||
//std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SERVER
|
||||
|
||||
//void irr_device_wait_egl (irr::IrrlichtDevice * device = nullptr);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
101
src/log_types.cpp
Normal file
101
src/log_types.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "log_types.h"
|
||||
#include "convert_json.h"
|
||||
#include "irr_v3d.h"
|
||||
#include "network/address.h"
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const v2s16 &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const v2s32 &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const v2f &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const v3pos_t &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << "," << p.Z << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const v3f &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << "," << p.Z << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
#if USE_OPOS64
|
||||
std::ostream &operator<<(std::ostream &s, const v3opos_t &p)
|
||||
{
|
||||
s << "(" << p.X << "," << p.Y << "," << p.Z << ")";
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const std::map<v3pos_t, unsigned int> &p)
|
||||
{
|
||||
for (auto &i : p)
|
||||
s << i.first << "=" << i.second << " ";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const irr::video::SColor &c)
|
||||
{
|
||||
s << "c32(" << c.color << ": a=" << c.getAlpha() << ",r=" << c.getRed()
|
||||
<< ",g=" << c.getGreen() << ",b=" << c.getBlue() << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const irr::video::SColorf &c)
|
||||
{
|
||||
s << "cf32("
|
||||
<< "a=" << c.getAlpha() << ",r=" << c.getRed() << ",g=" << c.getGreen()
|
||||
<< ",b=" << c.getBlue() << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
#include "util/string.h"
|
||||
std::ostream &operator<<(std::ostream &s, const std::wstring &w)
|
||||
{
|
||||
s << wide_to_utf8(w);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const Json::Value &json)
|
||||
{
|
||||
s << fastWriteJson(json);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const Address &addr)
|
||||
{
|
||||
addr.print(s);
|
||||
// s << addr.getPort();
|
||||
return s;
|
||||
}
|
55
src/log_types.h
Normal file
55
src/log_types.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOG_TYPES_HEADER
|
||||
#define LOG_TYPES_HEADER
|
||||
|
||||
//#include "log.h" //for replacing log.h to log_types.h in includes
|
||||
|
||||
#include "irr_v2d.h"
|
||||
#include "irr_v3d.h"
|
||||
|
||||
#include <ostream>
|
||||
std::ostream &operator<<(std::ostream &s, const v2s16 &p);
|
||||
std::ostream &operator<<(std::ostream &s, const v2s32 &p);
|
||||
std::ostream &operator<<(std::ostream &s, const v2f &p);
|
||||
std::ostream &operator<<(std::ostream &s, const v3pos_t &p);
|
||||
std::ostream &operator<<(std::ostream &s, const v3f &p);
|
||||
#if USE_OPOS64
|
||||
std::ostream &operator<<(std::ostream &s, const v3opos_t &p);
|
||||
#endif
|
||||
|
||||
#include <SColor.h>
|
||||
std::ostream &operator<<(std::ostream &s, const irr::video::SColor &c);
|
||||
std::ostream &operator<<(std::ostream &s, const irr::video::SColorf &c);
|
||||
|
||||
#include <map>
|
||||
std::ostream &operator<<(std::ostream &s, const std::map<v3pos_t, unsigned int> &p);
|
||||
|
||||
std::ostream &operator<<(std::ostream &s, const std::wstring &w);
|
||||
|
||||
namespace Json
|
||||
{
|
||||
class Value;
|
||||
};
|
||||
|
||||
//std::ostream &operator<<(std::ostream &s, const Json::Value &json);
|
||||
|
||||
class Address;
|
||||
std::ostream &operator<<(std::ostream &s, const Address &addr);
|
||||
|
||||
#endif
|
@ -2,6 +2,7 @@ set(common_network_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/address.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connectionthreads.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lan.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp
|
||||
|
@ -46,6 +46,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); }
|
||||
@ -74,6 +77,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:
|
||||
|
296
src/network/lan.cpp
Normal file
296
src/network/lan.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
Copyright (C) 2016 proller <proler@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "network/lan.h"
|
||||
#include <cstdint>
|
||||
#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 "porting.h"
|
||||
#include "threading/thread_vector.h"
|
||||
#include "threading/concurrent_map.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 <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define LAST_SOCKET_ERR() WSAGetLastError()
|
||||
typedef SOCKET socket_t;
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifndef __ANDROID__
|
||||
#include <ifaddrs.h>
|
||||
#define HAVE_IFADDRS 1
|
||||
#endif
|
||||
|
||||
#define LAST_SOCKET_ERR() (errno)
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
|
||||
const static unsigned short int adv_port = 29998;
|
||||
static std::string ask_str;
|
||||
|
||||
lan_adv::lan_adv() : thread_vector("lan_adv")
|
||||
{
|
||||
}
|
||||
|
||||
void lan_adv::ask()
|
||||
{
|
||||
reanimate();
|
||||
|
||||
if (ask_str.empty()) {
|
||||
Json::Value j;
|
||||
j["cmd"] = "ask";
|
||||
ask_str = fastWriteJson(j);
|
||||
}
|
||||
|
||||
send_string(ask_str);
|
||||
}
|
||||
|
||||
void lan_adv::send_string(const std::string &str)
|
||||
{
|
||||
try {
|
||||
sockaddr_in addr = {};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(adv_port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||
UDPSocket socket_send(false);
|
||||
int set_option_on = 1;
|
||||
setsockopt(socket_send.GetHandle(), SOL_SOCKET, SO_BROADCAST,
|
||||
(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 broadcast send4 fail " << e.what() << "\n";
|
||||
}
|
||||
|
||||
std::vector<uint32_t> 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="<<Address(*((struct sockaddr_in6*)ifa->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, SO_BROADCAST,
|
||||
(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 broadcast send6 fail " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
}
|
||||
|
||||
void lan_adv::serve(unsigned short port)
|
||||
{
|
||||
server_port = port;
|
||||
restart();
|
||||
}
|
||||
|
||||
void *lan_adv::run()
|
||||
{
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER;
|
||||
|
||||
reg("LanAdv" + (server_port ? std::string("Server") : std::string("Client")));
|
||||
|
||||
UDPSocket socket_recv(true);
|
||||
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
|
||||
setsockopt(socket_recv.GetHandle(), SOL_SOCKET, SO_BROADCAST,
|
||||
(const char *)&set_option_on, sizeof(set_option_on));
|
||||
setsockopt(socket_recv.GetHandle(), IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(const char *)&set_option_off, sizeof(set_option_off));
|
||||
socket_recv.setTimeoutMs(200);
|
||||
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;
|
||||
}
|
||||
}
|
||||
std::unordered_map<std::string, uint64_t> 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"] =
|
||||
strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MAX;
|
||||
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");
|
||||
|
||||
send_string(fastWriteJson(server));
|
||||
}
|
||||
|
||||
while (!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 << std::endl;
|
||||
|
||||
server["clients"] = clients_num.load();
|
||||
answer_str = fastWriteJson(server);
|
||||
|
||||
limiter[addr_str] = now + 3000;
|
||||
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 << std::endl;
|
||||
}
|
||||
if (p["port"].isInt()) {
|
||||
p["address"] = addr_str;
|
||||
auto key = addr_str + ":" + p["port"].asString();
|
||||
if (p["cmd"].asString() == "shutdown") {
|
||||
//infostream << "server shutdown " << key << "\n";
|
||||
collected.erase(key);
|
||||
fresh = true;
|
||||
} else {
|
||||
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<<std::endl;
|
||||
}
|
||||
END_DEBUG_EXCEPTION_HANDLER;
|
||||
}
|
||||
|
||||
if (server_port) {
|
||||
Json::Value answer_json;
|
||||
answer_json["port"] = server_port;
|
||||
answer_json["cmd"] = "shutdown";
|
||||
send_string(fastWriteJson(answer_json));
|
||||
}
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER;
|
||||
|
||||
return nullptr;
|
||||
}
|
46
src/network/lan.h
Normal file
46
src/network/lan.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (C) 2016 proller <proler@gmail.com>
|
||||
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json/value.h>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include "threading/thread_vector.h"
|
||||
#include "threading/concurrent_map.h"
|
||||
|
||||
|
||||
class lan_adv : public thread_vector
|
||||
{
|
||||
public:
|
||||
void *run();
|
||||
|
||||
lan_adv();
|
||||
void ask();
|
||||
void send_string(const std::string &str);
|
||||
|
||||
void serve(unsigned short port);
|
||||
|
||||
concurrent_map<std::string, Json::Value> collected;
|
||||
std::atomic_bool fresh;
|
||||
std::atomic_int clients_num;
|
||||
|
||||
private:
|
||||
unsigned short server_port = 0;
|
||||
};
|
@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "convert_json.h"
|
||||
#include "content/content.h"
|
||||
#include "content/subgames.h"
|
||||
#include "server/serverlist.h"
|
||||
#include "mapgen/mapgen.h"
|
||||
#include "settings.h"
|
||||
#include "client/client.h"
|
||||
@ -40,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "content/mod_configuration.h"
|
||||
#include "threading/mutex_auto_lock.h"
|
||||
#include "common/c_converter.h"
|
||||
#include "json-forwards.h"
|
||||
|
||||
/******************************************************************************/
|
||||
std::string ModApiMainMenu::getTextData(lua_State *L, const std::string &name)
|
||||
@ -1080,6 +1082,48 @@ int ModApiMainMenu::l_do_async_callback(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/*
|
||||
ModApiMainMenu::l_ask_lan_servers, ModApiMainMenu::l_get_lan_servers
|
||||
by proller <proler@gmail.com>, special thanks for letting us use them.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1132,6 +1176,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);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@ -1160,4 +1206,6 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
|
||||
API_FCT(get_max_supp_proto);
|
||||
API_FCT(get_language);
|
||||
API_FCT(gettext);
|
||||
API_FCT(get_lan_servers);
|
||||
API_FCT(ask_lan_servers);
|
||||
}
|
||||
|
@ -60,6 +60,17 @@ private:
|
||||
*/
|
||||
static bool mayModifyPath(std::string path);
|
||||
|
||||
//lan
|
||||
|
||||
/*
|
||||
ModApiMainMenu::l_ask_lan_servers, ModApiMainMenu::l_get_lan_servers
|
||||
by proller <proler@gmail.com>, special thanks for letting us use it.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
@ -75,6 +75,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "gameparams.h"
|
||||
#include "particles.h"
|
||||
#include "gettext.h"
|
||||
#include "network/lan.h"
|
||||
|
||||
class ClientNotFoundException : public BaseException
|
||||
{
|
||||
@ -561,6 +562,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[] = {
|
||||
" __. __. __. ",
|
||||
@ -778,6 +783,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
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "network/lan.h"
|
||||
#include "irr_v3d.h"
|
||||
#include "map.h"
|
||||
#include "hud.h"
|
||||
@ -671,6 +672,9 @@ private:
|
||||
// The server mainly operates in this thread
|
||||
ServerThread *m_thread = nullptr;
|
||||
|
||||
// For local server discovery.
|
||||
lan_adv lan_adv_server;
|
||||
|
||||
/*
|
||||
Client interface
|
||||
*/
|
||||
|
@ -28,9 +28,45 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "convert_json.h"
|
||||
#include "httpfetch.h"
|
||||
#include "server.h"
|
||||
#include "network/lan.h"
|
||||
#include "json/json.h"
|
||||
|
||||
namespace ServerList
|
||||
{
|
||||
|
||||
/*
|
||||
Special thanks to proller <proler@gmail.com> for
|
||||
letting us use the methods lan_get() and lan_fresh()
|
||||
as well as the lan_adv class.
|
||||
*/
|
||||
|
||||
static std::string ask_str;
|
||||
|
||||
lan_adv lan_adv_client;
|
||||
|
||||
void lan_get() {
|
||||
if (!g_settings->getBool("serverlist_lan"))
|
||||
return;
|
||||
lan_adv_client.ask();
|
||||
}
|
||||
|
||||
/*
|
||||
if (ask_str.empty()) {
|
||||
Json::Value j;
|
||||
j["cmd"] = "ask";
|
||||
j["proto_min"] = Server::getProtocolVersionMin();
|
||||
j["proto_max"] = Server::getProtocolVersionMax();
|
||||
ask_str = fastWriteJson(j);
|
||||
|
||||
};
|
||||
lan_adv_client.send_string(ask_str);
|
||||
*/
|
||||
|
||||
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,
|
||||
|
@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "content/mods.h"
|
||||
#include "json-forwards.h"
|
||||
#include <iostream>
|
||||
#include "network/lan.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -28,6 +29,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
namespace ServerList
|
||||
{
|
||||
/*
|
||||
Special thanks to proller <proler@gmail.com> for
|
||||
letting us use the methods lan_get() and lan_fresh()
|
||||
as well as the lan_adv class.
|
||||
*/
|
||||
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,
|
||||
|
@ -2,5 +2,7 @@ set(JTHREAD_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/event.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/thread.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/semaphore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/thread_vector.cpp
|
||||
PARENT_SCOPE)
|
||||
|
||||
|
209
src/threading/concurrent_map.h
Normal file
209
src/threading/concurrent_map.h
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
Copyright (C) 2024 proller <proler@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "lock.h"
|
||||
|
||||
template <class LOCKER, class Key, class T, class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
class concurrent_map_ : public std::map<Key, T, Compare, Allocator>, public LOCKER
|
||||
{
|
||||
public:
|
||||
typedef typename std::map<Key, T, Compare, Allocator> full_type;
|
||||
typedef Key key_type;
|
||||
typedef T mapped_type;
|
||||
|
||||
mapped_type &operator[](const key_type &k) = delete;
|
||||
mapped_type &operator[](key_type &&k) = delete;
|
||||
|
||||
mapped_type nothing = {};
|
||||
|
||||
template <typename... Args>
|
||||
mapped_type& get(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
|
||||
//if (!full_type::contains(std::forward<Args>(args)...))
|
||||
if (full_type::find(std::forward<Args>(args)...) == full_type::end())
|
||||
return nothing;
|
||||
|
||||
return full_type::operator[](std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) at(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::at(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) assign(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::assign(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) insert(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::insert(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) emplace(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) emplace_try(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::try_lock_unique_rec();
|
||||
if (!lock->owns_lock())
|
||||
return false;
|
||||
return full_type::emplace(std::forward<Args>(args)...).second;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) insert_or_assign(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::insert_or_assign(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) empty(Args &&...args) const noexcept
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::empty(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) size(Args &&...args) const
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::size(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) count(Args &&...args) const
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::count(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) contains(Args &&...args) const
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::contains(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) find(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::find(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) begin(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::begin(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) rbegin(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::rbegin(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) end(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::end(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) rend(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_shared_rec();
|
||||
return full_type::rend(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) erase(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::erase(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
decltype(auto) clear(Args &&...args)
|
||||
{
|
||||
auto lock = LOCKER::lock_unique_rec();
|
||||
return full_type::clear(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Key, class T, class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
using concurrent_map = concurrent_map_<locker<>, Key, T, Compare, Allocator>;
|
||||
|
||||
template <class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
using concurrent_shared_map = concurrent_map_<shared_locker, Key, T, Compare, Allocator>;
|
||||
|
||||
#if ENABLE_THREADS
|
||||
|
||||
template <class Key, class T, class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
using maybe_concurrent_map = concurrent_map<Key, T, Compare, Allocator>;
|
||||
|
||||
#else
|
||||
|
||||
template <class Key, class T, class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
class not_concurrent_map : public std::map<Key, T, Compare, Allocator>,
|
||||
public dummy_locker
|
||||
{
|
||||
public:
|
||||
typedef typename std::map<Key, T, Compare, Allocator> full_type;
|
||||
typedef Key key_type;
|
||||
typedef T mapped_type;
|
||||
|
||||
mapped_type &get(const key_type &k) { return full_type::operator[](k); }
|
||||
};
|
||||
|
||||
template <class Key, class T, class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
using maybe_concurrent_map = not_concurrent_map<Key, T, Compare, Allocator>;
|
||||
|
||||
#endif
|
131
src/threading/lock.cpp
Normal file
131
src/threading/lock.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include "lock.h"
|
||||
#include "log.h"
|
||||
#include "profiler.h"
|
||||
|
||||
#if !defined(NDEBUG) && !defined(LOCK_PROFILE)
|
||||
//#define LOCK_PROFILE 1
|
||||
#endif
|
||||
|
||||
#if LOCK_PROFILE
|
||||
#define SCOPE_PROFILE(a) ScopeProfiler scp___(g_profiler, "Lock: " a);
|
||||
#else
|
||||
#define SCOPE_PROFILE(a)
|
||||
#endif
|
||||
|
||||
template<class GUARD, class MUTEX>
|
||||
recursive_lock<GUARD, MUTEX>::recursive_lock(MUTEX & mtx, std::atomic<std::size_t> & thread_id_, bool try_lock):
|
||||
thread_id(thread_id_) {
|
||||
auto thread_me = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
if(thread_me != thread_id) {
|
||||
if (try_lock) {
|
||||
SCOPE_PROFILE("try_lock");
|
||||
lock = new GUARD(mtx, try_to_lock);
|
||||
if (lock->owns_lock()) {
|
||||
thread_id = thread_me;
|
||||
return;
|
||||
} else {
|
||||
#if LOCK_PROFILE
|
||||
g_profiler->add("Lock: try_lock fail", 1);
|
||||
#endif
|
||||
//infostream<<"not locked "<<" thread="<<thread_id<<" lock="<<lock<<std::endl;
|
||||
}
|
||||
delete lock;
|
||||
} else {
|
||||
SCOPE_PROFILE("lock");
|
||||
lock = new GUARD(mtx);
|
||||
thread_id = thread_me;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
#if LOCK_PROFILE
|
||||
g_profiler->add("Lock: recursive", 1);
|
||||
#endif
|
||||
}
|
||||
lock = nullptr;
|
||||
}
|
||||
|
||||
template<class GUARD, class MUTEX>
|
||||
recursive_lock<GUARD, MUTEX>::~recursive_lock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
template<class GUARD, class MUTEX>
|
||||
bool recursive_lock<GUARD, MUTEX>::owns_lock() {
|
||||
if (lock)
|
||||
return lock;
|
||||
auto thread_me = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
return thread_id == thread_me;
|
||||
}
|
||||
|
||||
template<class GUARD, class MUTEX>
|
||||
void recursive_lock<GUARD, MUTEX>::unlock() {
|
||||
if(lock) {
|
||||
thread_id = 0;
|
||||
lock->unlock();
|
||||
delete lock;
|
||||
lock = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
locker<mutex, unique_lock, shared_lock>::locker() {
|
||||
thread_id = 0;
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<unique_lock> locker<mutex, unique_lock, shared_lock>::lock_unique() {
|
||||
return std::make_unique<unique_lock>(mtx);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<unique_lock> locker<mutex, unique_lock, shared_lock>::try_lock_unique() {
|
||||
SCOPE_PROFILE("locker::try_lock_unique");
|
||||
return std::make_unique<unique_lock>(mtx, std::try_to_lock);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<shared_lock> locker<mutex, unique_lock, shared_lock>::lock_shared() const {
|
||||
SCOPE_PROFILE("locker::lock_shared");
|
||||
return std::make_unique<shared_lock>(mtx);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<shared_lock> locker<mutex, unique_lock, shared_lock>::try_lock_shared() {
|
||||
SCOPE_PROFILE("locker::try_lock_shared");
|
||||
return std::make_unique<shared_lock>(mtx, std::try_to_lock);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<recursive_lock<unique_lock, mutex>> locker<mutex, unique_lock, shared_lock>::lock_unique_rec() const {
|
||||
SCOPE_PROFILE("locker::lock_unique_rec");
|
||||
return std::make_unique<lock_rec_unique>(mtx, thread_id);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<recursive_lock<unique_lock, mutex>> locker<mutex, unique_lock, shared_lock>::try_lock_unique_rec() {
|
||||
SCOPE_PROFILE("locker::try_lock_unique_rec");
|
||||
return std::make_unique<lock_rec_unique>(mtx, thread_id, true);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<recursive_lock<shared_lock, mutex>> locker<mutex, unique_lock, shared_lock>::lock_shared_rec() const {
|
||||
SCOPE_PROFILE("locker::lock_shared_rec");
|
||||
return std::make_unique<lock_rec_shared>(mtx, thread_id);
|
||||
}
|
||||
|
||||
template<class mutex, class unique_lock, class shared_lock>
|
||||
std::unique_ptr<recursive_lock<shared_lock, mutex>> locker<mutex, unique_lock, shared_lock>::try_lock_shared_rec() {
|
||||
SCOPE_PROFILE("locker::try_lock_shared_rec");
|
||||
return std::make_unique<lock_rec_shared>(mtx, thread_id, true);
|
||||
}
|
||||
|
||||
|
||||
template class recursive_lock<std::unique_lock<use_mutex>>;
|
||||
template class locker<>;
|
||||
#if LOCK_TWO
|
||||
template class recursive_lock<try_shared_lock, try_shared_mutex>;
|
||||
template class recursive_lock<std::unique_lock<try_shared_mutex>, try_shared_mutex>;
|
||||
|
||||
template class locker<try_shared_mutex, std::unique_lock<try_shared_mutex>, std::shared_lock<try_shared_mutex>>;
|
||||
#endif
|
164
src/threading/lock.h
Normal file
164
src/threading/lock.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
This file is part of Freeminer.
|
||||
|
||||
Freeminer is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Freeminer 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
//#include "../threading/mutex.h"
|
||||
using use_mutex = std::mutex;
|
||||
using try_shared_mutex = use_mutex;
|
||||
using try_shared_lock = std::unique_lock<try_shared_mutex>;
|
||||
using unique_lock = std::unique_lock<try_shared_mutex>;
|
||||
const auto try_to_lock = std::try_to_lock;
|
||||
|
||||
#else
|
||||
|
||||
typedef std::mutex use_mutex;
|
||||
|
||||
|
||||
#if USE_BOOST // not finished
|
||||
|
||||
//#include <ctime>
|
||||
#include <boost/thread.hpp>
|
||||
//#include <boost/thread/locks.hpp>
|
||||
typedef boost::shared_mutex try_shared_mutex;
|
||||
typedef boost::shared_lock<try_shared_mutex> try_shared_lock;
|
||||
typedef boost::unique_lock<try_shared_mutex> unique_lock;
|
||||
const auto try_to_lock = boost::try_to_lock;
|
||||
#define LOCK_TWO 1
|
||||
|
||||
#elif HAVE_SHARED_MUTEX
|
||||
//#elif __cplusplus >= 201305L
|
||||
|
||||
#include <shared_mutex>
|
||||
using try_shared_mutex = std::shared_mutex;
|
||||
using try_shared_lock = std::shared_lock<try_shared_mutex>;
|
||||
using unique_lock = std::unique_lock<try_shared_mutex>;
|
||||
const auto try_to_lock = std::try_to_lock;
|
||||
#define LOCK_TWO 1
|
||||
|
||||
#else
|
||||
|
||||
using try_shared_mutex = use_mutex;
|
||||
using try_shared_lock = std::unique_lock<try_shared_mutex> ;
|
||||
using unique_lock = std::unique_lock<try_shared_mutex> ;
|
||||
const auto try_to_lock = std::try_to_lock;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads
|
||||
/* uncomment when need
|
||||
#include <condition_variable>
|
||||
class semaphore {
|
||||
private:
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
int count;
|
||||
|
||||
public:
|
||||
semaphore(int count_ = 0):count(count_){;}
|
||||
void notify() {
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
++count;
|
||||
cv.notify_one();
|
||||
}
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
while(count == 0){
|
||||
cv.wait(lck);
|
||||
}
|
||||
count--;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
template<class GUARD, class MUTEX = use_mutex>
|
||||
class recursive_lock {
|
||||
public:
|
||||
GUARD * lock;
|
||||
std::atomic<std::size_t> & thread_id;
|
||||
recursive_lock(MUTEX & mtx, std::atomic<std::size_t> & thread_id_, bool try_lock = false);
|
||||
~recursive_lock();
|
||||
bool owns_lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
template<class mutex = use_mutex, class unique_lock = std::unique_lock<mutex> , class shared_lock = std::unique_lock<mutex> >
|
||||
class locker {
|
||||
public:
|
||||
using lock_rec_shared = recursive_lock<shared_lock, mutex>;
|
||||
using lock_rec_unique = recursive_lock<unique_lock, mutex>;
|
||||
|
||||
mutable mutex mtx;
|
||||
mutable std::atomic<std::size_t> thread_id;
|
||||
|
||||
locker();
|
||||
std::unique_ptr<unique_lock> lock_unique();
|
||||
std::unique_ptr<unique_lock> try_lock_unique();
|
||||
std::unique_ptr<shared_lock> lock_shared() const;
|
||||
std::unique_ptr<shared_lock> try_lock_shared();
|
||||
std::unique_ptr<lock_rec_unique> lock_unique_rec() const;
|
||||
std::unique_ptr<lock_rec_unique> try_lock_unique_rec();
|
||||
std::unique_ptr<lock_rec_shared> lock_shared_rec() const;
|
||||
std::unique_ptr<lock_rec_shared> try_lock_shared_rec();
|
||||
};
|
||||
|
||||
using shared_locker = locker<try_shared_mutex, unique_lock, try_shared_lock>;
|
||||
|
||||
class dummy_lock {
|
||||
public:
|
||||
~dummy_lock() {}; //no unused variable warning
|
||||
bool owns_lock() {return true;}
|
||||
bool operator!() {return true;}
|
||||
dummy_lock * operator->() {return this; }
|
||||
void unlock() {};
|
||||
};
|
||||
|
||||
class dummy_locker {
|
||||
public:
|
||||
dummy_lock lock_unique() { return {}; };
|
||||
dummy_lock try_lock_unique() { return {}; };
|
||||
dummy_lock lock_shared() { return {}; };
|
||||
dummy_lock try_lock_shared() { return {}; };
|
||||
dummy_lock lock_unique_rec() { return {}; };
|
||||
dummy_lock try_lock_unique_rec() { return {}; };
|
||||
dummy_lock lock_shared_rec() { return {}; };
|
||||
dummy_lock try_lock_shared_rec() { return {}; };
|
||||
};
|
||||
|
||||
|
||||
#if ENABLE_THREADS
|
||||
|
||||
using maybe_locker = locker<>;
|
||||
using maybe_shared_locker = shared_locker;
|
||||
|
||||
#else
|
||||
|
||||
using maybe_locker = dummy_locker;
|
||||
using maybe_shared_locker = dummy_locker;
|
||||
|
||||
#endif
|
118
src/threading/thread_vector.cpp
Normal file
118
src/threading/thread_vector.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "thread_vector.h"
|
||||
#include "fm_porting.h"
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
|
||||
thread_vector::thread_vector(const std::string &name, int priority) :
|
||||
m_name(name), m_priority(priority)
|
||||
{
|
||||
request_stop = false;
|
||||
};
|
||||
|
||||
thread_vector::~thread_vector()
|
||||
{
|
||||
join();
|
||||
};
|
||||
|
||||
void thread_vector::func()
|
||||
{
|
||||
reg();
|
||||
run();
|
||||
};
|
||||
|
||||
void thread_vector::reg(const std::string &name, int priority)
|
||||
{
|
||||
if (!name.empty())
|
||||
m_name = name;
|
||||
|
||||
porting::setThreadName(m_name.c_str());
|
||||
g_logger.registerThread(m_name);
|
||||
|
||||
if (priority)
|
||||
m_priority = priority;
|
||||
if (m_priority)
|
||||
porting::setThreadPriority(m_priority);
|
||||
};
|
||||
|
||||
void thread_vector::start(const size_t n)
|
||||
{
|
||||
#if !NDEBUG
|
||||
infostream << "start thread " << m_name << " n=" << n << std::endl;
|
||||
#endif
|
||||
request_stop = false;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
workers.emplace_back(&thread_vector::func, this);
|
||||
}
|
||||
}
|
||||
|
||||
void thread_vector::stop()
|
||||
{
|
||||
request_stop = true;
|
||||
}
|
||||
|
||||
void thread_vector::join()
|
||||
{
|
||||
stop();
|
||||
for (auto &worker : workers) {
|
||||
try {
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
workers.clear();
|
||||
}
|
||||
|
||||
void thread_vector::restart(size_t n)
|
||||
{
|
||||
join();
|
||||
start(n);
|
||||
}
|
||||
void thread_vector::reanimate(size_t n)
|
||||
{
|
||||
if (workers.empty()) {
|
||||
start(n);
|
||||
}
|
||||
}
|
||||
|
||||
void thread_vector::sleep(const int seconds)
|
||||
{
|
||||
for (int i = 0; i <= seconds; ++i) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
if (request_stop) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JThread compat:
|
||||
bool thread_vector::stopRequested()
|
||||
{
|
||||
return request_stop;
|
||||
}
|
||||
bool thread_vector::isRunning()
|
||||
{
|
||||
return !workers.empty();
|
||||
}
|
||||
void thread_vector::wait()
|
||||
{
|
||||
join();
|
||||
};
|
||||
void thread_vector::kill()
|
||||
{
|
||||
join();
|
||||
};
|
||||
void *thread_vector::run()
|
||||
{
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
bool thread_vector::isCurrentThread()
|
||||
{
|
||||
auto thread_me = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
for (auto &worker : workers)
|
||||
if (thread_me == std::hash<std::thread::id>()(worker.get_id()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
37
src/threading/thread_vector.h
Normal file
37
src/threading/thread_vector.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class thread_vector {
|
||||
public:
|
||||
std::vector<std::thread> workers;
|
||||
std::atomic_bool request_stop;
|
||||
|
||||
thread_vector(const std::string &name = "Unnamed", int priority = 0);
|
||||
virtual ~thread_vector();
|
||||
|
||||
virtual void func();
|
||||
|
||||
void reg (const std::string &name = "", int priority = 0);
|
||||
void start (const size_t n = 1);
|
||||
void restart (const size_t n = 1);
|
||||
void reanimate (const size_t n = 1);
|
||||
void stop ();
|
||||
void join ();
|
||||
|
||||
void sleep(const int second);
|
||||
// Thread compat:
|
||||
|
||||
bool stopRequested();
|
||||
bool isRunning();
|
||||
void wait();
|
||||
void kill();
|
||||
virtual void * run() = 0;
|
||||
bool isCurrentThread();
|
||||
protected:
|
||||
std::string m_name;
|
||||
int m_priority = 0;
|
||||
};
|
Loading…
Reference in New Issue
Block a user