mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-10-22 19:22:04 +00:00
The old Windows64 port had no real player identity — it used hardcoded fake XUIDs, so anyone could impersonate anyone. This replaces that with proper auth supporting Mojang, Ely.by, and offline accounts. MCAuth library (new, MCAuth/): Mojang auth via MSA device code flow (XBL, SISU, MC services), Ely.by via Yggdrasil with 2FA, offline UUID generation matching Java Edition (MD5 v3 from "OfflinePlayer:<name>"). Multi-account manager with background token refresh, per-slot sessions, and on-disk token persistence. Server-side session verification via Mojang/Ely.by hasJoined API. Skin fetching and PNG validation from texture servers. Network protocol (version bumped to 80): Three new packets (AuthScheme, AuthResponse, AuthResult) implement a server-driven auth handshake before login completes. Player identity migrated from 64-bit XUID to 128-bit GameUUID backed by two uint64 fields (hi/lo). readPlayerUID/writePlayerUID now serialize 16 bytes on the wire. Old and new clients cannot connect to each other — version mismatch is rejected at PreLogin. Save migration: Map data mappings auto-migrate from old format: the old 64-bit XUID is placed in hi, lo is set to 0 as a sentinel. On first access by the real player, the sentinel entry is upgraded in-place to the full 128-bit UUID. Format detection is by file size (2080, 2112, or 4160 bytes). Player .dat filenames inside saveData.ms change from decimal XUID to dashed UUID — old saves need manual entry renaming in the archive. UI: NativeUIRenderer: immediate-mode drawing system (quads, text, 9-slice panels, scrollbars, focus lists) for rendering auth screens without Flash/Scaleform. UIScene_MSAuth handles device code display, Ely.by credential input with 2FA, per-account skin head preview, and multi-account add/remove/switch. Server: online-mode and auth-provider (mojang/elyby) in server.properties. Whitelist and ban checks validate against the server-verified UUID. Incompatible auth scheme logs which provider the server expects vs what the client is using. Also fixes a pre-existing exploit where any client could send a DebugOptionsPacket to grant themselves CraftAnything and other debug privileges on any server — now requires OP status server-side.
109 lines
2.6 KiB
C++
109 lines
2.6 KiB
C++
#pragma once
|
|
|
|
#include "FileUtils.h"
|
|
#include "StringUtils.h"
|
|
#include "..\..\Minecraft.World\GameUUID.h"
|
|
|
|
#include "..\vendor\nlohmann\json.hpp"
|
|
|
|
#include <stdio.h>
|
|
|
|
namespace ServerRuntime
|
|
{
|
|
namespace AccessStorageUtils
|
|
{
|
|
inline bool IsRegularFile(const std::string &path)
|
|
{
|
|
const std::wstring widePath = StringUtils::Utf8ToWide(path);
|
|
if (widePath.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const DWORD attributes = GetFileAttributesW(widePath.c_str());
|
|
return (attributes != INVALID_FILE_ATTRIBUTES) && ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
}
|
|
|
|
inline bool EnsureJsonListFileExists(const std::string &path)
|
|
{
|
|
return IsRegularFile(path) || FileUtils::WriteTextFileAtomic(path, "[]\n");
|
|
}
|
|
|
|
inline bool TryGetStringField(const nlohmann::ordered_json &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;
|
|
}
|
|
|
|
inline std::string NormalizeXuid(const std::string &xuid)
|
|
{
|
|
const std::string trimmed = StringUtils::TrimAscii(xuid);
|
|
if (trimmed.empty())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
// Try parsing as a dashed 128-bit UUID (standard format)
|
|
GameUUID parsed = GameUUID::fromDashed(trimmed);
|
|
if (parsed.isValid())
|
|
{
|
|
return parsed.toDashed();
|
|
}
|
|
|
|
// Try parsing as an undashed 128-bit UUID
|
|
parsed = GameUUID::fromUndashed(trimmed);
|
|
if (parsed.isValid())
|
|
{
|
|
return parsed.toDashed();
|
|
}
|
|
|
|
// Fallback: lowercase whatever was passed (e.g. player names in legacy files)
|
|
return StringUtils::ToLowerAscii(trimmed);
|
|
}
|
|
|
|
inline std::string BuildPathFromBaseDirectory(const std::string &baseDirectory, const char *fileName)
|
|
{
|
|
if (fileName == nullptr || fileName[0] == 0)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
const std::wstring wideFileName = StringUtils::Utf8ToWide(fileName);
|
|
if (wideFileName.empty())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
if (baseDirectory.empty() || baseDirectory == ".")
|
|
{
|
|
return StringUtils::WideToUtf8(wideFileName);
|
|
}
|
|
|
|
const std::wstring wideBaseDirectory = StringUtils::Utf8ToWide(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);
|
|
}
|
|
}
|
|
}
|