mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
add: add Whitelist to Dedicated Server
This commit is contained in:
parent
4ad824713f
commit
cfa0a7de7f
|
|
@ -97,6 +97,7 @@ list(APPEND MINECRAFT_SERVER_SOURCES
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/Access.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/Access.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/BanManager.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/BanManager.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/WhitelistManager.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCli.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCli.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliInput.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliInput.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliParser.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliParser.cpp"
|
||||||
|
|
@ -111,6 +112,7 @@ list(APPEND MINECRAFT_SERVER_SOURCES
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandStop.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandStop.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandList.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandList.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandTp.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandTp.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandWhitelist.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandGamemode.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/CliCommandGamemode.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/FileUtils.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/FileUtils.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
#include "..\Minecraft.Server\ServerLogManager.h"
|
#include "..\Minecraft.Server\ServerLogManager.h"
|
||||||
|
#include "..\Minecraft.Server\Access\Access.h"
|
||||||
#include "..\Minecraft.World\Socket.h"
|
#include "..\Minecraft.World\Socket.h"
|
||||||
#endif
|
#endif
|
||||||
// #ifdef __PS3__
|
// #ifdef __PS3__
|
||||||
|
|
@ -213,6 +214,22 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
||||||
bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid);
|
bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool whitelistSatisfied = true;
|
||||||
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
|
if (ServerRuntime::Access::IsWhitelistEnabled())
|
||||||
|
{
|
||||||
|
whitelistSatisfied = false;
|
||||||
|
if (loginXuid != INVALID_XUID)
|
||||||
|
{
|
||||||
|
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(loginXuid);
|
||||||
|
}
|
||||||
|
if (!whitelistSatisfied && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
|
||||||
|
{
|
||||||
|
whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(packet->m_onlineXuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if( sentDisconnect )
|
if( sentDisconnect )
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
|
@ -221,6 +238,13 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
||||||
{
|
{
|
||||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid);
|
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid);
|
||||||
|
#endif
|
||||||
|
disconnect(DisconnectPacket::eDisconnect_Banned);
|
||||||
|
}
|
||||||
|
else if (!whitelistSatisfied)
|
||||||
|
{
|
||||||
|
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||||
|
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted);
|
||||||
#endif
|
#endif
|
||||||
disconnect(DisconnectPacket::eDisconnect_Banned);
|
disconnect(DisconnectPacket::eDisconnect_Banned);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,13 @@
|
||||||
|
|
||||||
#include "Access.h"
|
#include "Access.h"
|
||||||
|
|
||||||
|
#include "..\Common\StringUtils.h"
|
||||||
#include "..\ServerLogger.h"
|
#include "..\ServerLogger.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
namespace ServerRuntime
|
namespace ServerRuntime
|
||||||
{
|
{
|
||||||
|
|
@ -25,6 +28,8 @@ namespace ServerRuntime
|
||||||
std::mutex stateLock;
|
std::mutex stateLock;
|
||||||
std::mutex writeLock;
|
std::mutex writeLock;
|
||||||
std::shared_ptr<BanManager> banManager;
|
std::shared_ptr<BanManager> banManager;
|
||||||
|
std::shared_ptr<WhitelistManager> whitelistManager;
|
||||||
|
bool whitelistEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessState g_accessState;
|
AccessState g_accessState;
|
||||||
|
|
@ -48,6 +53,18 @@ namespace ServerRuntime
|
||||||
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
g_accessState.banManager = banManager;
|
g_accessState.banManager = banManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<WhitelistManager> GetWhitelistManagerSnapshot()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
return g_accessState.whitelistManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PublishWhitelistManagerSnapshot(const std::shared_ptr<WhitelistManager> &whitelistManager)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
g_accessState.whitelistManager = whitelistManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FormatXuid(PlayerUID xuid)
|
std::string FormatXuid(PlayerUID xuid)
|
||||||
|
|
@ -62,70 +79,175 @@ namespace ServerRuntime
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Initialize(const std::string &baseDirectory)
|
bool TryParseXuid(const std::string &text, PlayerUID *outXuid)
|
||||||
|
{
|
||||||
|
if (outXuid == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmed = StringUtils::TrimAscii(text);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char *end = nullptr;
|
||||||
|
unsigned long long value = _strtoui64(trimmed.c_str(), &end, 0);
|
||||||
|
if (end == trimmed.c_str() || errno != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')
|
||||||
|
{
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end != 0 || value == 0ULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outXuid = (PlayerUID)value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Initialize(const std::string &baseDirectory, bool whitelistEnabled)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
|
|
||||||
// Build the replacement manager privately so readers keep using the last published snapshot during disk I/O.
|
// Build the replacement manager privately so readers keep using the last published snapshot during disk I/O.
|
||||||
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(baseDirectory);
|
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(baseDirectory);
|
||||||
|
std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(baseDirectory);
|
||||||
if (!banManager->EnsureBanFilesExist())
|
if (!banManager->EnsureBanFilesExist())
|
||||||
{
|
{
|
||||||
LogError("access", "failed to ensure dedicated server ban files exist");
|
LogError("access", "failed to ensure dedicated server ban files exist");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!whitelistManager->EnsureWhitelistFileExists())
|
||||||
|
{
|
||||||
|
LogError("access", "failed to ensure dedicated server whitelist file exists");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!banManager->Reload())
|
if (!banManager->Reload())
|
||||||
{
|
{
|
||||||
LogError("access", "failed to load dedicated server ban files");
|
LogError("access", "failed to load dedicated server ban files");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!whitelistManager->Reload())
|
||||||
|
{
|
||||||
|
LogError("access", "failed to load dedicated server whitelist file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<BannedPlayerEntry> playerEntries;
|
std::vector<BannedPlayerEntry> playerEntries;
|
||||||
std::vector<BannedIpEntry> ipEntries;
|
std::vector<BannedIpEntry> ipEntries;
|
||||||
|
std::vector<WhitelistedPlayerEntry> whitelistEntries;
|
||||||
banManager->SnapshotBannedPlayers(&playerEntries);
|
banManager->SnapshotBannedPlayers(&playerEntries);
|
||||||
banManager->SnapshotBannedIps(&ipEntries);
|
banManager->SnapshotBannedIps(&ipEntries);
|
||||||
|
whitelistManager->SnapshotWhitelistedPlayers(&whitelistEntries);
|
||||||
PublishBanManagerSnapshot(banManager);
|
PublishBanManagerSnapshot(banManager);
|
||||||
|
PublishWhitelistManagerSnapshot(whitelistManager);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
g_accessState.whitelistEnabled = whitelistEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
LogInfof(
|
LogInfof(
|
||||||
"access",
|
"access",
|
||||||
"loaded %u player bans and %u ip bans",
|
"loaded %u player bans, %u ip bans, and %u whitelist entries (whitelist=%s)",
|
||||||
(unsigned)playerEntries.size(),
|
(unsigned)playerEntries.size(),
|
||||||
(unsigned)ipEntries.size());
|
(unsigned)ipEntries.size(),
|
||||||
|
(unsigned)whitelistEntries.size(),
|
||||||
|
whitelistEnabled ? "enabled" : "disabled");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
PublishBanManagerSnapshot(std::shared_ptr<BanManager>());
|
PublishBanManagerSnapshot(std::shared_ptr<BanManager>{});
|
||||||
|
PublishWhitelistManagerSnapshot(std::shared_ptr<WhitelistManager>{});
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
g_accessState.whitelistEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Reload()
|
bool Reload()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
|
std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
|
||||||
if (current == nullptr)
|
std::shared_ptr<WhitelistManager> currentWhitelist = GetWhitelistManagerSnapshot();
|
||||||
|
if (current == nullptr || currentWhitelist == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
|
std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
|
||||||
|
std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(*currentWhitelist);
|
||||||
if (!banManager->EnsureBanFilesExist())
|
if (!banManager->EnsureBanFilesExist())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!whitelistManager->EnsureWhitelistFileExists())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!banManager->Reload())
|
if (!banManager->Reload())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!whitelistManager->Reload())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PublishBanManagerSnapshot(banManager);
|
PublishBanManagerSnapshot(banManager);
|
||||||
|
PublishWhitelistManagerSnapshot(whitelistManager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReloadWhitelist()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
|
const auto current = GetWhitelistManagerSnapshot();
|
||||||
|
if (current == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
|
||||||
|
if (!whitelistManager->EnsureWhitelistFileExists())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!whitelistManager->Reload())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublishWhitelistManagerSnapshot(whitelistManager);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInitialized()
|
bool IsInitialized()
|
||||||
{
|
{
|
||||||
return GetBanManagerSnapshot() != nullptr;
|
return GetBanManagerSnapshot() != nullptr && GetWhitelistManagerSnapshot() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWhitelistEnabled()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
return g_accessState.whitelistEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWhitelistEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
|
std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
|
||||||
|
g_accessState.whitelistEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPlayerBanned(PlayerUID xuid)
|
bool IsPlayerBanned(PlayerUID xuid)
|
||||||
|
|
@ -146,6 +268,18 @@ namespace ServerRuntime
|
||||||
return (banManager != nullptr) ? banManager->IsIpBanned(ip) : false;
|
return (banManager != nullptr) ? banManager->IsIpBanned(ip) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsPlayerWhitelisted(PlayerUID xuid)
|
||||||
|
{
|
||||||
|
const std::string formatted = FormatXuid(xuid);
|
||||||
|
if (formatted.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<WhitelistManager> whitelistManager = GetWhitelistManagerSnapshot();
|
||||||
|
return (whitelistManager != nullptr) ? whitelistManager->IsPlayerWhitelistedByXuid(formatted) : false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata)
|
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata)
|
||||||
{
|
{
|
||||||
const std::string formatted = FormatXuid(xuid);
|
const std::string formatted = FormatXuid(xuid);
|
||||||
|
|
@ -241,6 +375,57 @@ namespace ServerRuntime
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata)
|
||||||
|
{
|
||||||
|
const auto formatted = FormatXuid(xuid);
|
||||||
|
if (formatted.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
|
const auto current = GetWhitelistManagerSnapshot();
|
||||||
|
if (current == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
|
||||||
|
const WhitelistedPlayerEntry entry = { formatted, name, metadata };
|
||||||
|
if (!whitelistManager->AddPlayer(entry))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublishWhitelistManagerSnapshot(whitelistManager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveWhitelistedPlayer(PlayerUID xuid)
|
||||||
|
{
|
||||||
|
const auto formatted = FormatXuid(xuid);
|
||||||
|
if (formatted.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
|
||||||
|
const auto current = GetWhitelistManagerSnapshot();
|
||||||
|
if (current == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto whitelistManager = std::make_shared<WhitelistManager>(*current);
|
||||||
|
if (!whitelistManager->RemovePlayerByXuid(formatted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublishWhitelistManagerSnapshot(whitelistManager);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries)
|
bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries)
|
||||||
{
|
{
|
||||||
if (outEntries == nullptr)
|
if (outEntries == nullptr)
|
||||||
|
|
@ -274,5 +459,22 @@ namespace ServerRuntime
|
||||||
|
|
||||||
return banManager->SnapshotBannedIps(outEntries);
|
return banManager->SnapshotBannedIps(outEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries)
|
||||||
|
{
|
||||||
|
if (outEntries == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto whitelistManager = GetWhitelistManagerSnapshot();
|
||||||
|
if (whitelistManager == nullptr)
|
||||||
|
{
|
||||||
|
outEntries->clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return whitelistManager->SnapshotWhitelistedPlayers(outEntries);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BanManager.h"
|
#include "BanManager.h"
|
||||||
|
#include "WhitelistManager.h"
|
||||||
|
|
||||||
namespace ServerRuntime
|
namespace ServerRuntime
|
||||||
{
|
{
|
||||||
|
|
@ -9,18 +10,24 @@ namespace ServerRuntime
|
||||||
*/
|
*/
|
||||||
namespace Access
|
namespace Access
|
||||||
{
|
{
|
||||||
bool Initialize(const std::string &baseDirectory = ".");
|
bool Initialize(const std::string &baseDirectory = ".", bool whitelistEnabled = false);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
bool Reload();
|
bool Reload();
|
||||||
|
bool ReloadWhitelist();
|
||||||
bool IsInitialized();
|
bool IsInitialized();
|
||||||
|
bool IsWhitelistEnabled();
|
||||||
|
void SetWhitelistEnabled(bool enabled);
|
||||||
|
|
||||||
bool IsPlayerBanned(PlayerUID xuid);
|
bool IsPlayerBanned(PlayerUID xuid);
|
||||||
bool IsIpBanned(const std::string &ip);
|
bool IsIpBanned(const std::string &ip);
|
||||||
|
bool IsPlayerWhitelisted(PlayerUID xuid);
|
||||||
|
|
||||||
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata);
|
bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata);
|
||||||
bool AddIpBan(const std::string &ip, const BanMetadata &metadata);
|
bool AddIpBan(const std::string &ip, const BanMetadata &metadata);
|
||||||
bool RemovePlayerBan(PlayerUID xuid);
|
bool RemovePlayerBan(PlayerUID xuid);
|
||||||
bool RemoveIpBan(const std::string &ip);
|
bool RemoveIpBan(const std::string &ip);
|
||||||
|
bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata);
|
||||||
|
bool RemoveWhitelistedPlayer(PlayerUID xuid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the current cached player bans for inspection or command output
|
* Copies the current cached player bans for inspection or command output
|
||||||
|
|
@ -32,7 +39,9 @@ namespace ServerRuntime
|
||||||
* 現在のIP BAN一覧を複製取得
|
* 現在のIP BAN一覧を複製取得
|
||||||
*/
|
*/
|
||||||
bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries);
|
bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries);
|
||||||
|
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries);
|
||||||
|
|
||||||
std::string FormatXuid(PlayerUID xuid);
|
std::string FormatXuid(PlayerUID xuid);
|
||||||
|
bool TryParseXuid(const std::string &text, PlayerUID *outXuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
439
Minecraft.Server/Access/WhitelistManager.cpp
Normal file
439
Minecraft.Server/Access/WhitelistManager.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "WhitelistManager.h"
|
||||||
|
|
||||||
|
#include "..\Common\FileUtils.h"
|
||||||
|
#include "..\Common\StringUtils.h"
|
||||||
|
#include "..\ServerLogger.h"
|
||||||
|
#include "..\vendor\nlohmann\json.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
namespace Access
|
||||||
|
{
|
||||||
|
using OrderedJson = nlohmann::ordered_json;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static const char *kWhitelistFileName = "whitelist.json";
|
||||||
|
|
||||||
|
static bool FileExists(const std::string &path)
|
||||||
|
{
|
||||||
|
const std::wstring widePath = StringUtils::Utf8ToWide(path);
|
||||||
|
if (widePath.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD attrs = GetFileAttributesW(widePath.c_str());
|
||||||
|
return (attrs != INVALID_FILE_ATTRIBUTES) && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool EnsureJsonListFile(const std::string &path)
|
||||||
|
{
|
||||||
|
if (FileExists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return FileUtils::WriteTextFileAtomic(path, "[]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseNumericXuid(const std::string &text, unsigned long long *outValue)
|
||||||
|
{
|
||||||
|
if (outValue == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string trimmed = StringUtils::TrimAscii(text);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char *end = nullptr;
|
||||||
|
unsigned long long value = _strtoui64(trimmed.c_str(), &end, 0);
|
||||||
|
if (end == trimmed.c_str() || errno != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')
|
||||||
|
{
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outValue = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryGetStringField(const OrderedJson &object, const char *key, std::string *outValue)
|
||||||
|
{
|
||||||
|
if (key == nullptr || outValue == nullptr || !object.is_object())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = object.find(key);
|
||||||
|
if (it == object.end() || !it->is_string())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outValue = it->get<std::string>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitelistManager::WhitelistManager(const std::string &baseDirectory)
|
||||||
|
: m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::EnsureWhitelistFileExists() const
|
||||||
|
{
|
||||||
|
const std::string path = GetWhitelistFilePath();
|
||||||
|
if (!EnsureJsonListFile(path))
|
||||||
|
{
|
||||||
|
LogErrorf("access", "failed to create %s", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::Reload()
|
||||||
|
{
|
||||||
|
std::vector<WhitelistedPlayerEntry> players;
|
||||||
|
if (!LoadPlayers(&players))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_whitelistedPlayers.swap(players);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::Save() const
|
||||||
|
{
|
||||||
|
std::vector<WhitelistedPlayerEntry> players;
|
||||||
|
return SnapshotWhitelistedPlayers(&players) && SavePlayers(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
|
||||||
|
{
|
||||||
|
if (outEntries == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outEntries->clear();
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
const std::string path = GetWhitelistFilePath();
|
||||||
|
if (!FileUtils::ReadTextFile(path, &text))
|
||||||
|
{
|
||||||
|
LogErrorf("access", "failed to read %s", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.empty())
|
||||||
|
{
|
||||||
|
text = "[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedJson root;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::exception &e)
|
||||||
|
{
|
||||||
|
LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!root.is_array())
|
||||||
|
{
|
||||||
|
LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &object : root)
|
||||||
|
{
|
||||||
|
if (!object.is_object())
|
||||||
|
{
|
||||||
|
LogWarnf("access", "skipping whitelist entry that is not an object in %s", path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rawXuid;
|
||||||
|
if (!TryGetStringField(object, "xuid", &rawXuid))
|
||||||
|
{
|
||||||
|
LogWarnf("access", "skipping whitelist entry without xuid in %s", path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitelistedPlayerEntry entry;
|
||||||
|
entry.xuid = NormalizeXuid(rawXuid);
|
||||||
|
if (entry.xuid.empty())
|
||||||
|
{
|
||||||
|
LogWarnf("access", "skipping whitelist entry with empty xuid in %s", path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryGetStringField(object, "name", &entry.name);
|
||||||
|
TryGetStringField(object, "created", &entry.metadata.created);
|
||||||
|
TryGetStringField(object, "source", &entry.metadata.source);
|
||||||
|
NormalizeMetadata(&entry.metadata);
|
||||||
|
|
||||||
|
outEntries->push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const
|
||||||
|
{
|
||||||
|
OrderedJson root = OrderedJson::array();
|
||||||
|
for (const auto &entry : entries)
|
||||||
|
{
|
||||||
|
OrderedJson object = OrderedJson::object();
|
||||||
|
object["xuid"] = NormalizeXuid(entry.xuid);
|
||||||
|
object["name"] = entry.name;
|
||||||
|
object["created"] = entry.metadata.created;
|
||||||
|
object["source"] = entry.metadata.source;
|
||||||
|
root.push_back(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string path = GetWhitelistFilePath();
|
||||||
|
const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
|
||||||
|
if (!FileUtils::WriteTextFileAtomic(path, json))
|
||||||
|
{
|
||||||
|
LogErrorf("access", "failed to write %s", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<WhitelistedPlayerEntry> &WhitelistManager::GetWhitelistedPlayers() const
|
||||||
|
{
|
||||||
|
return m_whitelistedPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
|
||||||
|
{
|
||||||
|
if (outEntries == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outEntries = m_whitelistedPlayers;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::IsPlayerWhitelistedByXuid(const std::string &xuid) const
|
||||||
|
{
|
||||||
|
const auto normalized = NormalizeXuid(xuid);
|
||||||
|
if (normalized.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::any_of(
|
||||||
|
m_whitelistedPlayers.begin(),
|
||||||
|
m_whitelistedPlayers.end(),
|
||||||
|
[&normalized](const WhitelistedPlayerEntry &entry)
|
||||||
|
{
|
||||||
|
return entry.xuid == normalized;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::AddPlayer(const WhitelistedPlayerEntry &entry)
|
||||||
|
{
|
||||||
|
std::vector<WhitelistedPlayerEntry> updatedEntries;
|
||||||
|
if (!SnapshotWhitelistedPlayers(&updatedEntries))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto normalized = entry;
|
||||||
|
normalized.xuid = NormalizeXuid(normalized.xuid);
|
||||||
|
NormalizeMetadata(&normalized.metadata);
|
||||||
|
if (normalized.xuid.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto existing = std::find_if(
|
||||||
|
updatedEntries.begin(),
|
||||||
|
updatedEntries.end(),
|
||||||
|
[&normalized](const WhitelistedPlayerEntry &candidate)
|
||||||
|
{
|
||||||
|
return candidate.xuid == normalized.xuid;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing != updatedEntries.end())
|
||||||
|
{
|
||||||
|
*existing = normalized;
|
||||||
|
if (!SavePlayers(updatedEntries))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_whitelistedPlayers.swap(updatedEntries);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedEntries.push_back(normalized);
|
||||||
|
if (!SavePlayers(updatedEntries))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_whitelistedPlayers.swap(updatedEntries);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhitelistManager::RemovePlayerByXuid(const std::string &xuid)
|
||||||
|
{
|
||||||
|
const auto normalized = NormalizeXuid(xuid);
|
||||||
|
if (normalized.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WhitelistedPlayerEntry> updatedEntries;
|
||||||
|
if (!SnapshotWhitelistedPlayers(&updatedEntries))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto oldSize = updatedEntries.size();
|
||||||
|
updatedEntries.erase(
|
||||||
|
std::remove_if(
|
||||||
|
updatedEntries.begin(),
|
||||||
|
updatedEntries.end(),
|
||||||
|
[&normalized](const WhitelistedPlayerEntry &entry) { return entry.xuid == normalized; }),
|
||||||
|
updatedEntries.end());
|
||||||
|
|
||||||
|
if (updatedEntries.size() == oldSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SavePlayers(updatedEntries))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_whitelistedPlayers.swap(updatedEntries);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WhitelistManager::GetWhitelistFilePath() const
|
||||||
|
{
|
||||||
|
return BuildPath(kWhitelistFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
WhitelistMetadata WhitelistManager::BuildDefaultMetadata(const char *source)
|
||||||
|
{
|
||||||
|
WhitelistMetadata metadata;
|
||||||
|
|
||||||
|
SYSTEMTIME utc;
|
||||||
|
GetSystemTime(&utc);
|
||||||
|
|
||||||
|
char created[64] = {};
|
||||||
|
sprintf_s(
|
||||||
|
created,
|
||||||
|
sizeof(created),
|
||||||
|
"%04u-%02u-%02uT%02u:%02u:%02uZ",
|
||||||
|
(unsigned)utc.wYear,
|
||||||
|
(unsigned)utc.wMonth,
|
||||||
|
(unsigned)utc.wDay,
|
||||||
|
(unsigned)utc.wHour,
|
||||||
|
(unsigned)utc.wMinute,
|
||||||
|
(unsigned)utc.wSecond);
|
||||||
|
metadata.created = created;
|
||||||
|
metadata.source = (source != nullptr) ? source : "Server";
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WhitelistManager::NormalizeXuid(const std::string &xuid)
|
||||||
|
{
|
||||||
|
std::string trimmed = StringUtils::TrimAscii(xuid);
|
||||||
|
if (trimmed.empty())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long numericXuid = 0;
|
||||||
|
if (TryParseNumericXuid(trimmed, &numericXuid))
|
||||||
|
{
|
||||||
|
if (numericXuid == 0ULL)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[32] = {};
|
||||||
|
sprintf_s(buffer, sizeof(buffer), "0x%016llx", numericXuid);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringUtils::ToLowerAscii(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WhitelistManager::NormalizeMetadata(WhitelistMetadata *metadata)
|
||||||
|
{
|
||||||
|
if (metadata == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata->created = StringUtils::TrimAscii(metadata->created);
|
||||||
|
metadata->source = StringUtils::TrimAscii(metadata->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WhitelistManager::BuildPath(const char *fileName) const
|
||||||
|
{
|
||||||
|
if (fileName == nullptr || fileName[0] == 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring wideFileName = StringUtils::Utf8ToWide(fileName);
|
||||||
|
if (wideFileName.empty())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_baseDirectory.empty() || m_baseDirectory == ".")
|
||||||
|
{
|
||||||
|
return StringUtils::WideToUtf8(wideFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring wideBaseDirectory = StringUtils::Utf8ToWide(m_baseDirectory);
|
||||||
|
if (wideBaseDirectory.empty())
|
||||||
|
{
|
||||||
|
return StringUtils::WideToUtf8(wideFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t last = wideBaseDirectory[wideBaseDirectory.size() - 1];
|
||||||
|
if (last == L'\\' || last == L'/')
|
||||||
|
{
|
||||||
|
return StringUtils::WideToUtf8(wideBaseDirectory + wideFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringUtils::WideToUtf8(wideBaseDirectory + L"\\" + wideFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Minecraft.Server/Access/WhitelistManager.h
Normal file
69
Minecraft.Server/Access/WhitelistManager.h
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
namespace Access
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Information stored with dedicated-server whitelist entries
|
||||||
|
* ホワイトリスト項目に保存する補助情報
|
||||||
|
*/
|
||||||
|
struct WhitelistMetadata
|
||||||
|
{
|
||||||
|
std::string created;
|
||||||
|
std::string source;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WhitelistedPlayerEntry
|
||||||
|
{
|
||||||
|
std::string xuid;
|
||||||
|
std::string name;
|
||||||
|
WhitelistMetadata metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dedicated server whitelist file manager.
|
||||||
|
*
|
||||||
|
* Files:
|
||||||
|
* - whitelist.json
|
||||||
|
*
|
||||||
|
* Stores and normalizes XUID-based allow entries.
|
||||||
|
*/
|
||||||
|
class WhitelistManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit WhitelistManager(const std::string &baseDirectory = ".");
|
||||||
|
|
||||||
|
bool EnsureWhitelistFileExists() const;
|
||||||
|
bool Reload();
|
||||||
|
bool Save() const;
|
||||||
|
|
||||||
|
bool LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
|
||||||
|
bool SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const;
|
||||||
|
|
||||||
|
const std::vector<WhitelistedPlayerEntry> &GetWhitelistedPlayers() const;
|
||||||
|
bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
|
||||||
|
|
||||||
|
bool IsPlayerWhitelistedByXuid(const std::string &xuid) const;
|
||||||
|
bool AddPlayer(const WhitelistedPlayerEntry &entry);
|
||||||
|
bool RemovePlayerByXuid(const std::string &xuid);
|
||||||
|
|
||||||
|
std::string GetWhitelistFilePath() const;
|
||||||
|
|
||||||
|
static WhitelistMetadata BuildDefaultMetadata(const char *source = "Server");
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string NormalizeXuid(const std::string &xuid);
|
||||||
|
static void NormalizeMetadata(WhitelistMetadata *metadata);
|
||||||
|
|
||||||
|
std::string BuildPath(const char *fileName) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_baseDirectory;
|
||||||
|
std::vector<WhitelistedPlayerEntry> m_whitelistedPlayers;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -101,6 +101,36 @@ namespace ServerRuntime
|
||||||
return lowered;
|
return lowered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex, const char *separator)
|
||||||
|
{
|
||||||
|
if (startIndex >= tokens.size())
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto joinSeparator = std::string((separator != nullptr) ? separator : " ");
|
||||||
|
size_t totalLength = 0;
|
||||||
|
for (size_t i = startIndex; i < tokens.size(); ++i)
|
||||||
|
{
|
||||||
|
totalLength += tokens[i].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
totalLength += (tokens.size() - startIndex - 1) * joinSeparator.size();
|
||||||
|
std::string joined;
|
||||||
|
joined.reserve(totalLength);
|
||||||
|
for (size_t i = startIndex; i < tokens.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!joined.empty())
|
||||||
|
{
|
||||||
|
joined += joinSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
joined += tokens[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return joined;
|
||||||
|
}
|
||||||
|
|
||||||
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix)
|
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix)
|
||||||
{
|
{
|
||||||
if (prefix.size() > value.size())
|
if (prefix.size() > value.size())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace ServerRuntime
|
namespace ServerRuntime
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +14,7 @@ namespace ServerRuntime
|
||||||
|
|
||||||
std::string TrimAscii(const std::string &value);
|
std::string TrimAscii(const std::string &value);
|
||||||
std::string ToLowerAscii(const std::string &value);
|
std::string ToLowerAscii(const std::string &value);
|
||||||
|
std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex = 0, const char *separator = " ");
|
||||||
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix);
|
bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "commands\CliCommandPardonIp.h"
|
#include "commands\CliCommandPardonIp.h"
|
||||||
#include "commands\CliCommandStop.h"
|
#include "commands\CliCommandStop.h"
|
||||||
#include "commands\CliCommandTp.h"
|
#include "commands\CliCommandTp.h"
|
||||||
|
#include "commands\CliCommandWhitelist.h"
|
||||||
#include "..\Common\StringUtils.h"
|
#include "..\Common\StringUtils.h"
|
||||||
#include "..\ServerShutdown.h"
|
#include "..\ServerShutdown.h"
|
||||||
#include "..\ServerLogger.h"
|
#include "..\ServerLogger.h"
|
||||||
|
|
@ -49,6 +50,7 @@ namespace ServerRuntime
|
||||||
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardon()));
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardon()));
|
||||||
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardonIp()));
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardonIp()));
|
||||||
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanList()));
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanList()));
|
||||||
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandWhitelist()));
|
||||||
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTp()));
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTp()));
|
||||||
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGamemode()));
|
m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGamemode()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
284
Minecraft.Server/Console/commands/CliCommandWhitelist.cpp
Normal file
284
Minecraft.Server/Console/commands/CliCommandWhitelist.cpp
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "CliCommandWhitelist.h"
|
||||||
|
|
||||||
|
#include "..\ServerCliEngine.h"
|
||||||
|
#include "..\ServerCliParser.h"
|
||||||
|
#include "..\..\Access\Access.h"
|
||||||
|
#include "..\..\Common\StringUtils.h"
|
||||||
|
#include "..\..\ServerProperties.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static const char *kWhitelistUsage = "whitelist <on|off|list|add|remove|reload> [...]";
|
||||||
|
|
||||||
|
static bool CompareWhitelistEntries(const ServerRuntime::Access::WhitelistedPlayerEntry &left, const ServerRuntime::Access::WhitelistedPlayerEntry &right)
|
||||||
|
{
|
||||||
|
const auto leftName = StringUtils::ToLowerAscii(left.name);
|
||||||
|
const auto rightName = StringUtils::ToLowerAscii(right.name);
|
||||||
|
if (leftName != rightName)
|
||||||
|
{
|
||||||
|
return leftName < rightName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringUtils::ToLowerAscii(left.xuid) < StringUtils::ToLowerAscii(right.xuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool PersistWhitelistToggle(bool enabled)
|
||||||
|
{
|
||||||
|
auto config = LoadServerPropertiesConfig();
|
||||||
|
config.whiteListEnabled = enabled;
|
||||||
|
return SaveServerPropertiesConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BuildWhitelistEntryRow(const ServerRuntime::Access::WhitelistedPlayerEntry &entry)
|
||||||
|
{
|
||||||
|
std::string row = " ";
|
||||||
|
row += entry.xuid;
|
||||||
|
if (!entry.name.empty())
|
||||||
|
{
|
||||||
|
row += " - ";
|
||||||
|
row += entry.name;
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LogWhitelistMode(ServerCliEngine *engine)
|
||||||
|
{
|
||||||
|
engine->LogInfo(std::string("Whitelist is ") + (ServerRuntime::Access::IsWhitelistEnabled() ? "enabled." : "disabled."));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool LogWhitelistEntries(ServerCliEngine *engine)
|
||||||
|
{
|
||||||
|
std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
|
||||||
|
if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
|
||||||
|
{
|
||||||
|
engine->LogError("Failed to read whitelist entries.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(entries.begin(), entries.end(), CompareWhitelistEntries);
|
||||||
|
LogWhitelistMode(engine);
|
||||||
|
engine->LogInfo("There are " + std::to_string(entries.size()) + " whitelisted player(s).");
|
||||||
|
for (const auto &entry : entries)
|
||||||
|
{
|
||||||
|
engine->LogInfo(BuildWhitelistEntryRow(entry));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryParseWhitelistXuid(const std::string &text, ServerCliEngine *engine, PlayerUID *outXuid)
|
||||||
|
{
|
||||||
|
if (ServerRuntime::Access::TryParseXuid(text, outXuid))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine->LogWarn("Invalid XUID: " + text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SuggestLiteral(const std::string &candidate, const ServerCliCompletionContext &context, std::vector<std::string> *out)
|
||||||
|
{
|
||||||
|
if (out == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix))
|
||||||
|
{
|
||||||
|
out->push_back(context.linePrefix + candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *CliCommandWhitelist::Name() const
|
||||||
|
{
|
||||||
|
return "whitelist";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *CliCommandWhitelist::Usage() const
|
||||||
|
{
|
||||||
|
return kWhitelistUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *CliCommandWhitelist::Description() const
|
||||||
|
{
|
||||||
|
return "Manage the dedicated-server XUID whitelist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CliCommandWhitelist::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
|
||||||
|
{
|
||||||
|
if (line.tokens.size() < 2)
|
||||||
|
{
|
||||||
|
engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ServerRuntime::Access::IsInitialized())
|
||||||
|
{
|
||||||
|
engine->LogWarn("Access manager is not initialized.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto subcommand = StringUtils::ToLowerAscii(line.tokens[1]);
|
||||||
|
if (subcommand == "on" || subcommand == "off")
|
||||||
|
{
|
||||||
|
if (line.tokens.size() != 2)
|
||||||
|
{
|
||||||
|
engine->LogWarn("Usage: whitelist <on|off>");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool enabled = (subcommand == "on");
|
||||||
|
if (!PersistWhitelistToggle(enabled))
|
||||||
|
{
|
||||||
|
engine->LogError("Failed to persist whitelist mode to server.properties.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerRuntime::Access::SetWhitelistEnabled(enabled);
|
||||||
|
engine->LogInfo(std::string("Whitelist ") + (enabled ? "enabled." : "disabled."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand == "list")
|
||||||
|
{
|
||||||
|
if (line.tokens.size() != 2)
|
||||||
|
{
|
||||||
|
engine->LogWarn("Usage: whitelist list");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LogWhitelistEntries(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand == "reload")
|
||||||
|
{
|
||||||
|
if (line.tokens.size() != 2)
|
||||||
|
{
|
||||||
|
engine->LogWarn("Usage: whitelist reload");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ServerRuntime::Access::ReloadWhitelist())
|
||||||
|
{
|
||||||
|
engine->LogError("Failed to reload whitelist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config = LoadServerPropertiesConfig();
|
||||||
|
ServerRuntime::Access::SetWhitelistEnabled(config.whiteListEnabled);
|
||||||
|
engine->LogInfo("Reloaded whitelist from disk.");
|
||||||
|
LogWhitelistMode(engine);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand == "add")
|
||||||
|
{
|
||||||
|
if (line.tokens.size() < 3)
|
||||||
|
{
|
||||||
|
engine->LogWarn("Usage: whitelist add <xuid> [name ...]");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerUID xuid = INVALID_XUID;
|
||||||
|
if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerRuntime::Access::IsPlayerWhitelisted(xuid))
|
||||||
|
{
|
||||||
|
engine->LogWarn("That XUID is already whitelisted.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto metadata = ServerRuntime::Access::WhitelistManager::BuildDefaultMetadata("Console");
|
||||||
|
const auto name = StringUtils::JoinTokens(line.tokens, 3);
|
||||||
|
if (!ServerRuntime::Access::AddWhitelistedPlayer(xuid, name, metadata))
|
||||||
|
{
|
||||||
|
engine->LogError("Failed to write whitelist entry.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message = "Whitelisted XUID " + ServerRuntime::Access::FormatXuid(xuid) + ".";
|
||||||
|
if (!name.empty())
|
||||||
|
{
|
||||||
|
message += " Name: " + name;
|
||||||
|
}
|
||||||
|
engine->LogInfo(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand == "remove")
|
||||||
|
{
|
||||||
|
if (line.tokens.size() != 3)
|
||||||
|
{
|
||||||
|
engine->LogWarn("Usage: whitelist remove <xuid>");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerUID xuid = INVALID_XUID;
|
||||||
|
if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ServerRuntime::Access::IsPlayerWhitelisted(xuid))
|
||||||
|
{
|
||||||
|
engine->LogWarn("That XUID is not whitelisted.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ServerRuntime::Access::RemoveWhitelistedPlayer(xuid))
|
||||||
|
{
|
||||||
|
engine->LogError("Failed to remove whitelist entry.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine->LogInfo("Removed XUID " + ServerRuntime::Access::FormatXuid(xuid) + " from the whitelist.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CliCommandWhitelist::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
|
||||||
|
{
|
||||||
|
(void)engine;
|
||||||
|
if (out == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.currentTokenIndex == 1)
|
||||||
|
{
|
||||||
|
SuggestLiteral("on", context, out);
|
||||||
|
SuggestLiteral("off", context, out);
|
||||||
|
SuggestLiteral("list", context, out);
|
||||||
|
SuggestLiteral("add", context, out);
|
||||||
|
SuggestLiteral("remove", context, out);
|
||||||
|
SuggestLiteral("reload", context, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.currentTokenIndex == 2 && context.parsed.tokens.size() >= 2 && StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "remove")
|
||||||
|
{
|
||||||
|
std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
|
||||||
|
if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &entry : entries)
|
||||||
|
{
|
||||||
|
SuggestLiteral(entry.xuid, context, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Minecraft.Server/Console/commands/CliCommandWhitelist.h
Normal file
16
Minecraft.Server/Console/commands/CliCommandWhitelist.h
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IServerCliCommand.h"
|
||||||
|
|
||||||
|
namespace ServerRuntime
|
||||||
|
{
|
||||||
|
class CliCommandWhitelist : public IServerCliCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char *Name() const override;
|
||||||
|
const char *Usage() const override;
|
||||||
|
const char *Description() const override;
|
||||||
|
bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
|
||||||
|
void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Access\Access.cpp" />
|
<ClCompile Include="Access\Access.cpp" />
|
||||||
<ClCompile Include="Access\BanManager.cpp" />
|
<ClCompile Include="Access\BanManager.cpp" />
|
||||||
|
<ClCompile Include="Access\WhitelistManager.cpp" />
|
||||||
<ClCompile Include="ServerLogManager.cpp" />
|
<ClCompile Include="ServerLogManager.cpp" />
|
||||||
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
|
<ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
|
||||||
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
|
<ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
|
||||||
|
|
@ -662,6 +663,7 @@
|
||||||
<ClCompile Include="Console\commands\CliCommandPardonIp.cpp" />
|
<ClCompile Include="Console\commands\CliCommandPardonIp.cpp" />
|
||||||
<ClCompile Include="Console\commands\CliCommandStop.cpp" />
|
<ClCompile Include="Console\commands\CliCommandStop.cpp" />
|
||||||
<ClCompile Include="Console\commands\CliCommandTp.cpp" />
|
<ClCompile Include="Console\commands\CliCommandTp.cpp" />
|
||||||
|
<ClCompile Include="Console\commands\CliCommandWhitelist.cpp" />
|
||||||
<ClCompile Include="Console\ServerCliEngine.cpp" />
|
<ClCompile Include="Console\ServerCliEngine.cpp" />
|
||||||
<ClCompile Include="Console\ServerCliParser.cpp" />
|
<ClCompile Include="Console\ServerCliParser.cpp" />
|
||||||
<ClCompile Include="Console\ServerCliRegistry.cpp" />
|
<ClCompile Include="Console\ServerCliRegistry.cpp" />
|
||||||
|
|
@ -684,6 +686,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Access\Access.h" />
|
<ClInclude Include="Access\Access.h" />
|
||||||
<ClInclude Include="Access\BanManager.h" />
|
<ClInclude Include="Access\BanManager.h" />
|
||||||
|
<ClInclude Include="Access\WhitelistManager.h" />
|
||||||
<ClInclude Include="Console\ServerCli.h" />
|
<ClInclude Include="Console\ServerCli.h" />
|
||||||
<ClInclude Include="Console\ServerCliInput.h" />
|
<ClInclude Include="Console\ServerCliInput.h" />
|
||||||
<ClInclude Include="Console\commands\CliCommandBan.h" />
|
<ClInclude Include="Console\commands\CliCommandBan.h" />
|
||||||
|
|
@ -696,6 +699,7 @@
|
||||||
<ClInclude Include="Console\commands\CliCommandPardonIp.h" />
|
<ClInclude Include="Console\commands\CliCommandPardonIp.h" />
|
||||||
<ClInclude Include="Console\commands\CliCommandStop.h" />
|
<ClInclude Include="Console\commands\CliCommandStop.h" />
|
||||||
<ClInclude Include="Console\commands\CliCommandTp.h" />
|
<ClInclude Include="Console\commands\CliCommandTp.h" />
|
||||||
|
<ClInclude Include="Console\commands\CliCommandWhitelist.h" />
|
||||||
<ClInclude Include="Console\commands\IServerCliCommand.h" />
|
<ClInclude Include="Console\commands\IServerCliCommand.h" />
|
||||||
<ClInclude Include="Console\ServerCliEngine.h" />
|
<ClInclude Include="Console\ServerCliEngine.h" />
|
||||||
<ClInclude Include="Console\ServerCliParser.h" />
|
<ClInclude Include="Console\ServerCliParser.h" />
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@
|
||||||
<ClCompile Include="Access\BanManager.cpp">
|
<ClCompile Include="Access\BanManager.cpp">
|
||||||
<Filter>Server\Access</Filter>
|
<Filter>Server\Access</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Access\WhitelistManager.cpp">
|
||||||
|
<Filter>Server\Access</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Common\FileUtils.cpp">
|
<ClCompile Include="Common\FileUtils.cpp">
|
||||||
<Filter>Server\Common</Filter>
|
<Filter>Server\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
@ -588,6 +591,9 @@
|
||||||
<ClCompile Include="Console\commands\CliCommandTp.cpp">
|
<ClCompile Include="Console\commands\CliCommandTp.cpp">
|
||||||
<Filter>Server\Console\Commands</Filter>
|
<Filter>Server\Console\Commands</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Console\commands\CliCommandWhitelist.cpp">
|
||||||
|
<Filter>Server\Console\Commands</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Console\ServerCli.h">
|
<ClInclude Include="Console\ServerCli.h">
|
||||||
|
|
@ -608,6 +614,9 @@
|
||||||
<ClInclude Include="Access\BanManager.h">
|
<ClInclude Include="Access\BanManager.h">
|
||||||
<Filter>Server\Access</Filter>
|
<Filter>Server\Access</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Access\WhitelistManager.h">
|
||||||
|
<Filter>Server\Access</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="Common\FileUtils.h">
|
<ClInclude Include="Common\FileUtils.h">
|
||||||
<Filter>Server\Common</Filter>
|
<Filter>Server\Common</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
@ -660,6 +669,9 @@
|
||||||
<ClInclude Include="Console\commands\CliCommandTp.h">
|
<ClInclude Include="Console\commands\CliCommandTp.h">
|
||||||
<Filter>Server\Console\Commands</Filter>
|
<Filter>Server\Console\Commands</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Console\commands\CliCommandWhitelist.h">
|
||||||
|
<Filter>Server\Console\Commands</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="Console\commands\IServerCliCommand.h">
|
<ClInclude Include="Console\commands\IServerCliCommand.h">
|
||||||
<Filter>Server\Console\Commands</Filter>
|
<Filter>Server\Console\Commands</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ namespace ServerRuntime
|
||||||
switch (reason)
|
switch (reason)
|
||||||
{
|
{
|
||||||
case eLoginRejectReason_BannedXuid: return "banned-xuid";
|
case eLoginRejectReason_BannedXuid: return "banned-xuid";
|
||||||
|
case eLoginRejectReason_NotWhitelisted: return "not-whitelisted";
|
||||||
case eLoginRejectReason_DuplicateXuid: return "duplicate-xuid";
|
case eLoginRejectReason_DuplicateXuid: return "duplicate-xuid";
|
||||||
case eLoginRejectReason_DuplicateName: return "duplicate-name";
|
case eLoginRejectReason_DuplicateName: return "duplicate-name";
|
||||||
default: return "unknown";
|
default: return "unknown";
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace ServerRuntime
|
||||||
enum ELoginRejectReason
|
enum ELoginRejectReason
|
||||||
{
|
{
|
||||||
eLoginRejectReason_BannedXuid = 0,
|
eLoginRejectReason_BannedXuid = 0,
|
||||||
|
eLoginRejectReason_NotWhitelisted,
|
||||||
eLoginRejectReason_DuplicateXuid,
|
eLoginRejectReason_DuplicateXuid,
|
||||||
eLoginRejectReason_DuplicateName
|
eLoginRejectReason_DuplicateName
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ static const ServerPropertyDefault kServerPropertyDefaults[] =
|
||||||
{ "server-ip", "0.0.0.0" },
|
{ "server-ip", "0.0.0.0" },
|
||||||
{ "server-name", "DedicatedServer" },
|
{ "server-name", "DedicatedServer" },
|
||||||
{ "server-port", "25565" },
|
{ "server-port", "25565" },
|
||||||
|
{ "white-list", "false" },
|
||||||
{ "lan-advertise", "false" },
|
{ "lan-advertise", "false" },
|
||||||
{ "spawn-animals", "true" },
|
{ "spawn-animals", "true" },
|
||||||
{ "spawn-monsters", "true" },
|
{ "spawn-monsters", "true" },
|
||||||
|
|
@ -691,6 +692,7 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
|
||||||
config.serverPort = ReadNormalizedIntProperty(&merged, "server-port", kDefaultServerPort, 1, 65535, &shouldWrite);
|
config.serverPort = ReadNormalizedIntProperty(&merged, "server-port", kDefaultServerPort, 1, 65535, &shouldWrite);
|
||||||
config.serverIp = ReadNormalizedStringProperty(&merged, "server-ip", "0.0.0.0", 255, &shouldWrite);
|
config.serverIp = ReadNormalizedStringProperty(&merged, "server-ip", "0.0.0.0", 255, &shouldWrite);
|
||||||
config.lanAdvertise = ReadNormalizedBoolProperty(&merged, kLanAdvertisePropertyKey, false, &shouldWrite);
|
config.lanAdvertise = ReadNormalizedBoolProperty(&merged, kLanAdvertisePropertyKey, false, &shouldWrite);
|
||||||
|
config.whiteListEnabled = ReadNormalizedBoolProperty(&merged, "white-list", false, &shouldWrite);
|
||||||
config.serverName = ReadNormalizedStringProperty(&merged, "server-name", "DedicatedServer", 16, &shouldWrite);
|
config.serverName = ReadNormalizedStringProperty(&merged, "server-name", "DedicatedServer", 16, &shouldWrite);
|
||||||
config.maxPlayers = ReadNormalizedIntProperty(&merged, "max-players", kDefaultMaxPlayers, 1, kMaxDedicatedPlayers, &shouldWrite);
|
config.maxPlayers = ReadNormalizedIntProperty(&merged, "max-players", kDefaultMaxPlayers, 1, kMaxDedicatedPlayers, &shouldWrite);
|
||||||
config.seed = 0;
|
config.seed = 0;
|
||||||
|
|
@ -750,7 +752,7 @@ ServerPropertiesConfig LoadServerPropertiesConfig()
|
||||||
*
|
*
|
||||||
* Saves world identity fields while preserving as many other settings as possible
|
* Saves world identity fields while preserving as many other settings as possible
|
||||||
* - Reads existing file and merges including unknown keys
|
* - Reads existing file and merges including unknown keys
|
||||||
* - Updates only `level-name` and `level-id` before writing back
|
* - Updates `level-name`, `level-id`, and `white-list` before writing back
|
||||||
* ワールド識別情報の保存処理
|
* ワールド識別情報の保存処理
|
||||||
*/
|
*/
|
||||||
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
|
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
|
||||||
|
|
@ -787,6 +789,7 @@ bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
|
||||||
|
|
||||||
merged["level-name"] = worldName;
|
merged["level-name"] = worldName;
|
||||||
merged["level-id"] = worldSaveId;
|
merged["level-id"] = worldSaveId;
|
||||||
|
merged["white-list"] = BoolToString(config.whiteListEnabled);
|
||||||
|
|
||||||
return WriteServerPropertiesFile(kServerPropertiesPath, merged);
|
return WriteServerPropertiesFile(kServerPropertiesPath, merged);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ namespace ServerRuntime
|
||||||
std::string serverIp;
|
std::string serverIp;
|
||||||
/** `lan-advertise` */
|
/** `lan-advertise` */
|
||||||
bool lanAdvertise;
|
bool lanAdvertise;
|
||||||
|
/** `white-list` */
|
||||||
|
bool whiteListEnabled;
|
||||||
/** `server-name` (max 16 chars at runtime) */
|
/** `server-name` (max 16 chars at runtime) */
|
||||||
std::string serverName;
|
std::string serverName;
|
||||||
/** `max-players` */
|
/** `max-players` */
|
||||||
|
|
@ -85,9 +87,10 @@ namespace ServerRuntime
|
||||||
* server.properties saver
|
* server.properties saver
|
||||||
*
|
*
|
||||||
* - `level-name` と `level-id` を更新
|
* - `level-name` と `level-id` を更新
|
||||||
|
* - `white-list` を更新
|
||||||
* - それ以外の既存キーは極力保持
|
* - それ以外の既存キーは極力保持
|
||||||
*
|
*
|
||||||
* @param config 保存するワールド識別情報
|
* @param config 保存するワールド識別情報と永続化対象設定
|
||||||
* @return 書き込み成功時 `true`
|
* @return 書き込み成功時 `true`
|
||||||
*/
|
*/
|
||||||
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config);
|
bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config);
|
||||||
|
|
|
||||||
|
|
@ -389,13 +389,14 @@ int main(int argc, char **argv)
|
||||||
LogStartupStep("initializing server log manager");
|
LogStartupStep("initializing server log manager");
|
||||||
ServerRuntime::ServerLogManager::Initialize();
|
ServerRuntime::ServerLogManager::Initialize();
|
||||||
LogStartupStep("initializing dedicated access control");
|
LogStartupStep("initializing dedicated access control");
|
||||||
if (!ServerRuntime::Access::Initialize("."))
|
if (!ServerRuntime::Access::Initialize(".", serverProperties.whiteListEnabled))
|
||||||
{
|
{
|
||||||
LogError("startup", "Failed to initialize dedicated server access control.");
|
LogError("startup", "Failed to initialize dedicated server access control.");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
accessShutdownGuard.Activate();
|
accessShutdownGuard.Activate();
|
||||||
LogInfof("startup", "LAN advertise: %s", serverProperties.lanAdvertise ? "enabled" : "disabled");
|
LogInfof("startup", "LAN advertise: %s", serverProperties.lanAdvertise ? "enabled" : "disabled");
|
||||||
|
LogInfof("startup", "Whitelist: %s", serverProperties.whiteListEnabled ? "enabled" : "disabled");
|
||||||
|
|
||||||
LogStartupStep("registering hidden window class");
|
LogStartupStep("registering hidden window class");
|
||||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue