mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +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.
159 lines
5.5 KiB
C++
159 lines
5.5 KiB
C++
#pragma once
|
|
/*
|
|
* UIScene_MSAuth — Microsoft Account Manager.
|
|
*
|
|
* Two views:
|
|
* 1. Account List — shows saved accounts, select/remove/add.
|
|
* 2. Device Code — device code flow for adding a new account.
|
|
*
|
|
* Native overlay (no SWF).
|
|
*/
|
|
|
|
#include "UIScene.h"
|
|
#include "NativeUIRenderer.h"
|
|
#include "../../../MCAuth/include/MCAuthManager.h"
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
// Control IDs for UIScene_MSAuth focus list.
|
|
// File-scope so static render helpers in the .cpp can reference them directly.
|
|
namespace MSAuthUI
|
|
{
|
|
enum EControls {
|
|
eBtn_Back = 0,
|
|
eBtn_AddAccount = 1,
|
|
eBtn_AddOffline = 2,
|
|
eBtn_OfflineConfirm = 3,
|
|
eBtn_ConfirmYes = 4,
|
|
eBtn_ConfirmNo = 5,
|
|
eBtn_OfflineTextBox = 6,
|
|
eBtn_AddElyby = 7,
|
|
eBtn_ElybyUsername = 8,
|
|
eBtn_ElybyPassword = 9,
|
|
eBtn_ElybySignIn = 10,
|
|
eBtn_ElybyCancel = 11,
|
|
eBtn_Elyby2FACode = 12,
|
|
eBtn_Elyby2FASubmit = 13,
|
|
eBtn_Elyby2FACancel = 14,
|
|
eBtn_RemoveBase = 50, // 50 + index = remove button for account i
|
|
eAccountBase = 100, // 100 + index = account row for account i
|
|
eLink_URL = 200,
|
|
};
|
|
}
|
|
|
|
class UIScene_MSAuth : public UIScene
|
|
{
|
|
private:
|
|
// View mode
|
|
enum EView { eView_AccountList, eView_DeviceCode, eView_OfflineInput, eView_ElybyInput, eView_Elyby2FA };
|
|
EView m_view = eView_AccountList;
|
|
|
|
// Shared flags survive scene destruction (prevent UAF from async callback)
|
|
struct AuthFlags {
|
|
std::atomic<bool> done { false };
|
|
std::atomic<bool> success{ false };
|
|
std::atomic<bool> need2FA{ false };
|
|
};
|
|
std::shared_ptr<AuthFlags> m_authFlags = std::make_shared<AuthFlags>();
|
|
|
|
int m_closeCountdown = 0;
|
|
int m_spinnerTick = 0;
|
|
NativeUI::NineSlice m_panel;
|
|
NativeUI::NineSlice m_recessPanel;
|
|
bool m_panelLoaded = false;
|
|
NativeUI::FocusList m_focus;
|
|
std::string m_cachedUri;
|
|
|
|
// Cached account list (refreshed each tick)
|
|
std::vector<MCAuthManager::JavaAccountInfo> m_accounts;
|
|
int m_activeIdx = -1;
|
|
|
|
// Scroll offset for account list
|
|
int m_scrollOffset = 0;
|
|
|
|
void StartAddAccount();
|
|
void SwitchToAccountList();
|
|
void SwitchToDeviceCode();
|
|
void SwitchToOfflineInput();
|
|
void SwitchToElybyInput();
|
|
void SwitchToElyby2FA();
|
|
void ConfirmOfflineAccount();
|
|
void SubmitElybyLogin();
|
|
void SubmitElyby2FA();
|
|
|
|
// Offline username input state
|
|
std::string m_offlineUsername;
|
|
int m_offlineCursorBlink = 0;
|
|
bool m_textInputActive = false; // true = text box has focus and is accepting keyboard input
|
|
|
|
// Ely.by login state
|
|
std::string m_elybyUsername;
|
|
std::string m_elybyPassword;
|
|
std::string m_elyby2FACode;
|
|
// (2FA flag lives in m_authFlags->need2FA for async safety)
|
|
int m_elybyActiveField = 0; // 0=username, 1=password, 2=2fa code
|
|
|
|
// Target slot for splitscreen (0 = primary player, 1-3 = splitscreen).
|
|
// When > 0, account selection binds to that slot instead of slot 0.
|
|
int m_targetSlot = 0;
|
|
|
|
// Input guard: ignore input for the first N ticks after opening to avoid
|
|
// processing the button press that triggered the scene to open.
|
|
int m_inputGuardTicks = 6;
|
|
|
|
// Remove confirmation dialog (-1 = not showing, >=0 = account index pending removal)
|
|
int m_pendingRemoveIdx = -1;
|
|
std::string m_pendingRemoveUuid; // UUID captured at click time for stale-index safety
|
|
|
|
public:
|
|
// Shared buffer for virtual keyboard callback (public so the file-static
|
|
// callback function can access it; prevents UAF if scene is destroyed
|
|
// while the keyboard is open on a different UI group).
|
|
struct PendingKeyboardResult {
|
|
std::string value;
|
|
std::atomic<bool> ready{false};
|
|
std::atomic<bool> valid{true};
|
|
};
|
|
|
|
// Skin texture cache entry (public so static render helpers can access it)
|
|
struct SkinEntry {
|
|
std::atomic<int> textureId{-2}; // -2=not started, -1=downloading/failed, -3=file ready, >=0=texture ID
|
|
std::string filePath;
|
|
};
|
|
|
|
private:
|
|
std::shared_ptr<PendingKeyboardResult> m_pendingKBResult;
|
|
|
|
// Skin texture cache (key = UUID string)
|
|
std::unordered_map<std::string, std::shared_ptr<SkinEntry>> m_skinCache;
|
|
void EnsureSkinLoaded(const std::string& uuid);
|
|
|
|
public:
|
|
UIScene_MSAuth(int iPad, void* initData, UILayer* parentLayer);
|
|
~UIScene_MSAuth();
|
|
|
|
virtual EUIScene getSceneType() override { return eUIScene_MSAuth; }
|
|
virtual wstring getMoviePath() override { return L""; }
|
|
virtual bool hidesLowerScenes() override { return true; }
|
|
virtual bool blocksInput() override { return true; }
|
|
virtual bool hasFocus(int iPad) override { return bHasFocus; }
|
|
virtual bool needsReloaded() override { return false; }
|
|
|
|
virtual void updateTooltips() override;
|
|
virtual void tick() override;
|
|
virtual void render(S32 width, S32 height,
|
|
C4JRender::eViewportType viewport) override;
|
|
|
|
virtual void handleInput(int iPad, int key, bool repeat,
|
|
bool pressed, bool released,
|
|
bool& handled) override;
|
|
|
|
virtual void handlePress(F64 controlId, F64 childId) override;
|
|
|
|
#ifdef _WINDOWS64
|
|
virtual bool handleMouseClick(F32 x, F32 y) override;
|
|
#endif
|
|
};
|