refactor: relocate IPlatformNetwork/Leaderboard/Game interfaces to platform/

Pulls the three remaining platform-shaped interfaces out of app/common/
and into platform/network/, platform/leaderboard/ and platform/game/,
each with a function accessor + true no-op stub backend matching the
existing platform/profile/ template. Drops the duplicate
PlatformNetworkManagerInterface.h that was a stale copy of the same
type. Existing app-side singletons (LeaderboardManager,
LinuxLeaderboardManager, IPlatformNetworkStub, LinuxGame) keep working
unchanged - the structural fix is just that the interface they depend
on now lives in platform/ where it belongs.
This commit is contained in:
MatthewBeshay 2026-04-09 15:42:23 +10:00
parent 3304b2e3db
commit 2a3b891be8
27 changed files with 342 additions and 225 deletions

View file

@ -18,7 +18,7 @@
#include "app/common/DebugOptions.h"
#include "app/common/GameRules/GameRuleManager.h"
#include "app/common/GameSettingsManager.h"
#include "app/common/IPlatformGame.h"
#include "platform/game/IPlatformGame.h"
#include "app/common/LocalizationManager.h"
#include "app/common/MenuController.h"
#include "app/common/NetworkController.h"

View file

@ -2,7 +2,7 @@
#include <string>
#include "app/common/Leaderboards/IPlatformLeaderboard.h"
#include "platform/leaderboard/IPlatformLeaderboard.h"
class LeaderboardManager : public IPlatformLeaderboard {
public:

View file

@ -8,7 +8,7 @@
#include <qnet.h>
#endif
#include "PlatformNetworkManagerStub.h"
#include "app/common/Network/IPlatformNetwork.h"
#include "platform/network/IPlatformNetwork.h"
#include "minecraft/network/INetworkService.h"
#include "minecraft/network/platform/NetworkPlayerInterface.h"
#include "minecraft/network/platform/SessionInfo.h"

View file

@ -1,139 +0,0 @@
#pragma once
// using namespace std;
#include <functional>
#include <vector>
#if !defined(__linux__)
#include <qnet.h>
#endif
#include "minecraft/client/model/SkinBox.h"
#include "minecraft/network/platform/NetworkPlayerInterface.h"
#include "minecraft/network/platform/SessionInfo.h"
#include "platform/C4JThread.h"
#include "platform/NetTypes.h"
class ClientConnection;
class Minecraft;
class CGameNetworkManager;
// This is the interface to be implemented by the platform-specific versions of
// the PlatformNetworkManagers. This API is used directly by GameNetworkManager
// so that it can remain as platform independent as possible.
// This value should be incremented if the server version changes, or the game
// session data changes
#define MINECRAFT_NET_VERSION VER_NETWORK
typedef struct _SearchForGamesData {
unsigned int sessionIDCount;
XSESSION_SEARCHRESULT_HEADER* searchBuffer;
XNQOS** ppQos;
SessionID* sessionIDList;
XOVERLAPPED* pOverlapped;
} SearchForGamesData;
class IPlatformNetwork {
friend class CGameNetworkManager;
public:
typedef enum {
JOIN_FAILED_SERVER_FULL,
JOIN_FAILED_INSUFFICIENT_PRIVILEGES,
JOIN_FAILED_NONSPECIFIC,
} eJoinFailedReason;
virtual bool Initialise(CGameNetworkManager* pGameNetworkManager,
int flagIndexSize) = 0;
virtual void Terminate() = 0;
virtual int GetJoiningReadyPercentage() = 0;
virtual int CorrectErrorIDS(int IDS) = 0;
virtual void DoWork() = 0;
virtual int GetPlayerCount() = 0;
virtual int GetOnlinePlayerCount() = 0;
virtual int GetLocalPlayerMask(int playerIndex) = 0;
virtual bool AddLocalPlayerByUserIndex(int userIndex) = 0;
virtual bool RemoveLocalPlayerByUserIndex(int userIndex) = 0;
virtual INetworkPlayer* GetLocalPlayerByUserIndex(int userIndex) = 0;
virtual INetworkPlayer* GetPlayerByIndex(int playerIndex) = 0;
virtual INetworkPlayer* GetPlayerByXuid(PlayerUID xuid) = 0;
virtual INetworkPlayer* GetPlayerBySmallId(unsigned char smallId) = 0;
virtual bool ShouldMessageForFullSession() = 0;
virtual INetworkPlayer* GetHostPlayer() = 0;
virtual bool IsHost() = 0;
virtual bool JoinGameFromInviteInfo(int userIndex, int userMask,
const INVITE_INFO* pInviteInfo) = 0;
virtual bool LeaveGame(bool bMigrateHost) = 0;
virtual bool IsInSession() = 0;
virtual bool IsInGameplay() = 0;
virtual bool IsReadyToPlayOrIdle() = 0;
virtual bool IsInStatsEnabledSession() = 0;
virtual bool SessionHasSpace(unsigned int spaceRequired = 1) = 0;
virtual void SendInviteGUI(int quadrant) = 0;
virtual bool IsAddingPlayer() = 0;
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate,
unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS,
unsigned char privateSlots = 0) = 0;
virtual int JoinGame(FriendSessionInfo* searchResult, int dwLocalUsersMask,
int dwPrimaryUserIndex) = 0;
virtual void CancelJoinGame() {};
virtual bool SetLocalGame(bool isLocal) = 0;
virtual bool IsLocalGame() = 0;
virtual void SetPrivateGame(bool isPrivate) = 0;
virtual bool IsPrivateGame() = 0;
virtual bool IsLeavingGame() = 0;
virtual void ResetLeavingGame() = 0;
virtual void RegisterPlayerChangedCallback(
int iPad, std::function<void(INetworkPlayer* pPlayer, bool leaving)>
callback) = 0;
virtual void UnRegisterPlayerChangedCallback(int iPad) = 0;
virtual void HandleSignInChange() = 0;
virtual bool _RunNetworkGame() = 0;
private:
virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom) = 0;
virtual void _HostGame(
int usersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS,
unsigned char privateSlots = 0) = 0;
virtual bool _StartGame() = 0;
public:
virtual void UpdateAndSetGameSessionData(
INetworkPlayer* pNetworkPlayerLeaving = nullptr) = 0;
private:
virtual bool RemoveLocalPlayer(INetworkPlayer* pNetworkPlayer) = 0;
public:
virtual void SystemFlagSet(INetworkPlayer* pNetworkPlayer, int index) = 0;
virtual bool SystemFlagGet(INetworkPlayer* pNetworkPlayer, int index) = 0;
virtual std::string GatherStats() = 0;
virtual std::string GatherRTTStats() = 0;
private:
virtual void SetSessionTexturePackParentId(int id) = 0;
virtual void SetSessionSubTexturePackId(int id) = 0;
virtual void Notify(int ID, uintptr_t Param) = 0;
public:
virtual std::vector<FriendSessionInfo*>* GetSessionList(int iPad,
int localPlayers,
bool partyOnly) = 0;
virtual bool GetGameSessionInfo(int iPad, SessionID sessionId,
FriendSessionInfo* foundSession) = 0;
virtual void SetSessionsUpdatedCallback(std::function<void()> callback) = 0;
virtual void GetFullFriendSessionInfo(
FriendSessionInfo* foundSession,
std::function<void(bool success)> callback) = 0;
virtual void ForceFriendsSessionRefresh() = 0;
virtual void FakeLocalPlayerJoined() {
}; // Temporary method whilst we don't have real networking to make this
// happen
};

View file

@ -4,7 +4,7 @@
#include <string>
#include <vector>
#include "app/common/Network/IPlatformNetwork.h"
#include "platform/network/IPlatformNetwork.h"
#include "minecraft/client/model/SkinBox.h"
#include "minecraft/network/platform/NetworkPlayerInterface.h"
#include "minecraft/network/platform/SessionInfo.h"

View file

@ -1,12 +1 @@
lib_platform_fs_std = static_library(
'platform_fs_std',
files('std/StdFilesystem.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_threads],
cpp_args: global_cpp_args + global_cpp_defs,
)
fs_dep = declare_dependency(
link_with: lib_platform_fs_std,
include_directories: [platform_inc],
)
platform_fs_sources = files('std/StdFilesystem.cpp')

View file

@ -0,0 +1,11 @@
#pragma once
#include "IPlatformGame.h"
// Function accessor backed by a function-local static (Meyers singleton).
// Same shape as platform/profile/profile.h.
namespace platform_internal {
IPlatformGame& PlatformGame_get();
}
#define PlatformGame (::platform_internal::PlatformGame_get())

View file

@ -0,0 +1 @@
platform_game_sources = files('stub/StubPlatformGame.cpp')

View file

@ -0,0 +1,10 @@
#include "StubPlatformGame.h"
#include "platform/game/game.h"
namespace platform_internal {
IPlatformGame& PlatformGame_get() {
static StubPlatformGame instance;
return instance;
}
} // namespace platform_internal

View file

@ -0,0 +1,42 @@
#pragma once
#include "platform/game/IPlatformGame.h"
// True no-op platform game-services backend. Same role as
// LinuxGame's overrides today: every method is a no-op so the platform
// abstraction is satisfied without any host integration. The composition
// root can substitute a real backend (Xbox Live, Steam, GOG, etc.) at
// link time.
class StubPlatformGame : public IPlatformGame {
public:
void SetRichPresenceContext(int /*iPad*/, int /*contextId*/) override {}
void CaptureSaveThumbnail() override {}
void GetSaveThumbnail(std::uint8_t** thumbnailData,
unsigned int* thumbnailSize) override {
if (thumbnailData) *thumbnailData = nullptr;
if (thumbnailSize) *thumbnailSize = 0;
}
void ReleaseSaveThumbnail() override {}
void GetScreenshot(int /*iPad*/, std::uint8_t** screenshotData,
unsigned int* screenshotSize) override {
if (screenshotData) *screenshotData = nullptr;
if (screenshotSize) *screenshotSize = 0;
}
void ReadBannedList(int /*iPad*/, eTMSAction /*action*/,
bool /*bCallback*/) override {}
int LoadLocalTMSFile(char* /*wchTMSFile*/) override { return -1; }
int LoadLocalTMSFile(char* /*wchTMSFile*/,
eFileExtensionType /*eExt*/) override {
return -1;
}
void FreeLocalTMSFiles(eTMSFileType /*eType*/) override {}
int GetLocalTMSFileIndex(char* /*wchTMSFile*/,
bool /*bFilenameIncludesExtension*/,
eFileExtensionType /*eEXT*/) override {
return -1;
}
};

View file

@ -1,13 +1 @@
lib_platform_input_sdl2 = static_library(
'platform_input_sdl2',
files('sdl2/SDL2Input.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_sdl2, _threads],
cpp_args: global_cpp_args + global_cpp_defs,
)
input_dep = declare_dependency(
link_with: lib_platform_input_sdl2,
include_directories: [platform_inc],
dependencies: [_sdl2],
)
platform_input_sources = files('sdl2/SDL2Input.cpp')

View file

@ -3,7 +3,7 @@
#include <cstdint>
#include <string>
#include "PlatformTypes.h"
#include "platform/PlatformTypes.h"
class LeaderboardReadListener;

View file

@ -0,0 +1,13 @@
#pragma once
#include "IPlatformLeaderboard.h"
// Function accessor backed by a function-local static (Meyers singleton).
// Same shape as platform/profile/profile.h: avoids the static-init-order
// fiasco and lets call sites use the existing `PlatformLeaderboard.foo()`
// shape via the macro expansion.
namespace platform_internal {
IPlatformLeaderboard& PlatformLeaderboard_get();
}
#define PlatformLeaderboard (::platform_internal::PlatformLeaderboard_get())

View file

@ -0,0 +1 @@
platform_leaderboard_sources = files('stub/StubLeaderboard.cpp')

View file

@ -0,0 +1,10 @@
#include "StubLeaderboard.h"
#include "platform/leaderboard/leaderboard.h"
namespace platform_internal {
IPlatformLeaderboard& PlatformLeaderboard_get() {
static StubLeaderboard instance;
return instance;
}
} // namespace platform_internal

View file

@ -0,0 +1,45 @@
#pragma once
#include "platform/leaderboard/IPlatformLeaderboard.h"
// No-op leaderboard backend. Returns success for session lifecycle and
// `false` for every read/write so consumers can short-circuit cleanly.
// This is the platform default; a real backend (Steam, EOS, Xbox Live,
// custom HTTP) would replace this at link time.
class StubLeaderboard : public IPlatformLeaderboard {
public:
void Tick() override {}
[[nodiscard]] bool OpenSession() override { return true; }
void CloseSession() override {}
void DeleteSession() override {}
[[nodiscard]] bool WriteStats(unsigned int /*viewCount*/,
ViewIn /*views*/) override {
return false;
}
bool ReadStats_Friends(LeaderboardReadListener* /*callback*/,
int /*difficulty*/, EStatsType /*type*/,
PlayerUID /*myUID*/, unsigned int /*startIndex*/,
unsigned int /*readCount*/) override {
return false;
}
bool ReadStats_MyScore(LeaderboardReadListener* /*callback*/,
int /*difficulty*/, EStatsType /*type*/,
PlayerUID /*myUID*/,
unsigned int /*readCount*/) override {
return false;
}
bool ReadStats_TopRank(LeaderboardReadListener* /*callback*/,
int /*difficulty*/, EStatsType /*type*/,
unsigned int /*startIndex*/,
unsigned int /*readCount*/) override {
return false;
}
void FlushStats() override {}
void CancelOperation() override {}
[[nodiscard]] bool isIdle() override { return true; }
};

View file

@ -23,12 +23,53 @@ platform_dep = declare_dependency(
include_directories: platform_inc,
)
# Per-subsystem backends. Each lives in its own subdir and produces its
# own library + dep so consumers can ask for the subsystem they need
# without dragging the others in.
# Per-subsystem source lists. Each subdir owns its own list of backend
# .cpp files via a `platform_<name>_sources` variable. The library
# build is centralised so we get one library per platform target rather
# than one per subsystem; subsystems vary together (an SDL2 platform
# wants SDL2 input + GL renderer + miniaudio together) and a per-
# subsystem split inflates link units without buying flexibility.
subdir('input')
subdir('profile')
subdir('storage')
subdir('fs')
subdir('renderer')
subdir('sound')
subdir('network')
subdir('leaderboard')
subdir('game')
lib_platform_sdl2 = static_library('platform_sdl2',
platform_input_sources
+ platform_profile_sources
+ platform_storage_sources
+ platform_fs_sources
+ platform_renderer_sources
+ platform_sound_sources
+ platform_network_sources
+ platform_leaderboard_sources
+ platform_game_sources,
include_directories: [platform_inc, include_directories('..')],
dependencies: [_sdl2, _gl, _threads, glm_dep, stb_dep, java_dep,
miniaudio_dep],
cpp_args: _defs + global_cpp_args + global_cpp_defs,
)
# Single dep for the whole platform_sdl2 library. Aliased per-subsystem
# so consumer meson files can keep asking for `input_dep` etc. without
# caring that they all resolve to the same library object today.
platform_sdl2_dep = declare_dependency(
link_with: lib_platform_sdl2,
include_directories: [platform_inc],
dependencies: [_sdl2, _gl, _threads, glm_dep, miniaudio_dep],
)
input_dep = platform_sdl2_dep
profile_dep = platform_sdl2_dep
storage_dep = platform_sdl2_dep
fs_dep = platform_sdl2_dep
render_dep = platform_sdl2_dep
sound_dep = platform_sdl2_dep
network_dep = platform_sdl2_dep
leaderboard_dep = platform_sdl2_dep
game_dep = platform_sdl2_dep

View file

@ -5,8 +5,8 @@
#include <string>
#include <vector>
#include "PlatformTypes.h"
#include "platform/NetTypes.h"
#include "platform/PlatformTypes.h"
#ifndef VER_NETWORK
#define VER_NETWORK 560

View file

@ -0,0 +1 @@
platform_network_sources = files('stub/StubPlatformNetwork.cpp')

View file

@ -0,0 +1,13 @@
#pragma once
#include "IPlatformNetwork.h"
// Function accessor backed by a function-local static (Meyers singleton).
// Same shape as platform/profile/profile.h: avoids the static-init-order
// fiasco. Call sites use the existing `PlatformNetwork.foo()` form via
// the macro.
namespace platform_internal {
IPlatformNetwork& PlatformNetwork_get();
}
#define PlatformNetwork (::platform_internal::PlatformNetwork_get())

View file

@ -0,0 +1,10 @@
#include "StubPlatformNetwork.h"
#include "platform/network/network.h"
namespace platform_internal {
IPlatformNetwork& PlatformNetwork_get() {
static StubPlatformNetwork instance;
return instance;
}
} // namespace platform_internal

View file

@ -0,0 +1,129 @@
#pragma once
#include "platform/network/IPlatformNetwork.h"
// True no-op platform network backend. Returns false / 0 / nullptr for
// every operation. The composition root can substitute a real backend
// (QNet, Steam, EOS, custom) at link time. Used as the platform default
// so consumers can call PlatformNetwork.* without nullptr checks.
class StubPlatformNetwork : public IPlatformNetwork {
public:
bool Initialise(CGameNetworkManager* /*pGameNetworkManager*/,
int /*flagIndexSize*/) override {
return true;
}
void Terminate() override {}
void DoWork() override {}
[[nodiscard]] int GetJoiningReadyPercentage() override { return 100; }
[[nodiscard]] int CorrectErrorIDS(int IDS) override { return IDS; }
[[nodiscard]] int GetPlayerCount() override { return 0; }
[[nodiscard]] int GetOnlinePlayerCount() override { return 0; }
[[nodiscard]] int GetLocalPlayerMask(int /*playerIndex*/) override {
return 0;
}
bool AddLocalPlayerByUserIndex(int /*userIndex*/) override { return false; }
bool RemoveLocalPlayerByUserIndex(int /*userIndex*/) override {
return false;
}
[[nodiscard]] INetworkPlayer* GetLocalPlayerByUserIndex(
int /*userIndex*/) override {
return nullptr;
}
[[nodiscard]] INetworkPlayer* GetPlayerByIndex(
int /*playerIndex*/) override {
return nullptr;
}
[[nodiscard]] INetworkPlayer* GetPlayerByXuid(PlayerUID /*xuid*/) override {
return nullptr;
}
[[nodiscard]] INetworkPlayer* GetPlayerBySmallId(
unsigned char /*smallId*/) override {
return nullptr;
}
[[nodiscard]] INetworkPlayer* GetHostPlayer() override { return nullptr; }
[[nodiscard]] bool ShouldMessageForFullSession() override { return false; }
[[nodiscard]] bool IsHost() override { return true; }
bool JoinGameFromInviteInfo(int /*userIndex*/, int /*userMask*/,
const INVITE_INFO* /*pInviteInfo*/) override {
return false;
}
bool LeaveGame(bool /*bMigrateHost*/) override { return true; }
[[nodiscard]] bool IsInSession() override { return false; }
[[nodiscard]] bool IsInGameplay() override { return false; }
[[nodiscard]] bool IsReadyToPlayOrIdle() override { return true; }
[[nodiscard]] bool IsInStatsEnabledSession() override { return false; }
[[nodiscard]] bool SessionHasSpace(
unsigned int /*spaceRequired*/) override {
return true;
}
void SendInviteGUI(int /*quadrant*/) override {}
[[nodiscard]] bool IsAddingPlayer() override { return false; }
void HostGame(int /*localUsersMask*/, bool /*bOnlineGame*/,
bool /*bIsPrivate*/, unsigned char /*publicSlots*/,
unsigned char /*privateSlots*/) override {}
int JoinGame(FriendSessionInfo* /*searchResult*/, int /*dwLocalUsersMask*/,
int /*dwPrimaryUserIndex*/) override {
return 0;
}
bool SetLocalGame(bool /*isLocal*/) override { return true; }
[[nodiscard]] bool IsLocalGame() override { return true; }
void SetPrivateGame(bool /*isPrivate*/) override {}
[[nodiscard]] bool IsPrivateGame() override { return false; }
[[nodiscard]] bool IsLeavingGame() override { return false; }
void ResetLeavingGame() override {}
void RegisterPlayerChangedCallback(
int /*iPad*/,
std::function<void(INetworkPlayer*, bool)> /*callback*/) override {}
void UnRegisterPlayerChangedCallback(int /*iPad*/) override {}
void HandleSignInChange() override {}
bool _RunNetworkGame() override { return true; }
bool _LeaveGame(bool /*bMigrateHost*/, bool /*bLeaveRoom*/) override {
return true;
}
void _HostGame(int /*usersMask*/, unsigned char /*publicSlots*/,
unsigned char /*privateSlots*/) override {}
bool _StartGame() override { return true; }
void UpdateAndSetGameSessionData(
INetworkPlayer* /*pNetworkPlayerLeaving*/) override {}
bool RemoveLocalPlayer(INetworkPlayer* /*pNetworkPlayer*/) override {
return false;
}
void SystemFlagSet(INetworkPlayer* /*pNetworkPlayer*/,
int /*index*/) override {}
[[nodiscard]] bool SystemFlagGet(INetworkPlayer* /*pNetworkPlayer*/,
int /*index*/) override {
return false;
}
[[nodiscard]] std::string GatherStats() override { return {}; }
[[nodiscard]] std::string GatherRTTStats() override { return {}; }
void SetSessionTexturePackParentId(int /*id*/) override {}
void SetSessionSubTexturePackId(int /*id*/) override {}
void Notify(int /*ID*/, uintptr_t /*Param*/) override {}
[[nodiscard]] std::vector<FriendSessionInfo*>* GetSessionList(
int /*iPad*/, int /*localPlayers*/, bool /*partyOnly*/) override {
return nullptr;
}
[[nodiscard]] bool GetGameSessionInfo(
int /*iPad*/, SessionID /*sessionId*/,
FriendSessionInfo* /*foundSession*/) override {
return false;
}
void SetSessionsUpdatedCallback(
std::function<void()> /*callback*/) override {}
void GetFullFriendSessionInfo(
FriendSessionInfo* /*foundSession*/,
std::function<void(bool)> /*callback*/) override {}
void ForceFriendsSessionRefresh() override {}
};

View file

@ -1,12 +1 @@
lib_platform_profile_stub = static_library(
'platform_profile_stub',
files('stub/StubProfile.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_threads],
cpp_args: global_cpp_args + global_cpp_defs,
)
profile_dep = declare_dependency(
link_with: lib_platform_profile_stub,
include_directories: [platform_inc],
)
platform_profile_sources = files('stub/StubProfile.cpp')

View file

@ -1,13 +1 @@
lib_platform_renderer_gl = static_library(
'platform_renderer_gl',
files('gl/GLRenderer.cpp', 'gl/render_stubs.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_sdl2, _gl, _threads, glm_dep, stb_dep, java_dep],
cpp_args: _defs + global_cpp_args + global_cpp_defs,
)
render_dep = declare_dependency(
link_with: lib_platform_renderer_gl,
include_directories: [platform_inc],
dependencies: [_sdl2, _gl, _threads, glm_dep],
)
platform_renderer_sources = files('gl/GLRenderer.cpp', 'gl/render_stubs.cpp')

View file

@ -1,15 +1 @@
_miniaudio_dep = dependency('miniaudio')
lib_platform_sound_miniaudio = static_library(
'platform_sound_miniaudio',
files('miniaudio/MiniaudioSound.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_threads, _miniaudio_dep],
cpp_args: global_cpp_args + global_cpp_defs,
)
sound_dep = declare_dependency(
link_with: lib_platform_sound_miniaudio,
include_directories: [platform_inc],
dependencies: [_miniaudio_dep],
)
platform_sound_sources = files('miniaudio/MiniaudioSound.cpp')

View file

@ -1,12 +1 @@
lib_platform_storage_stub = static_library(
'platform_storage_stub',
files('stub/StubStorage.cpp'),
include_directories: [platform_inc, include_directories('../..')],
dependencies: [_threads],
cpp_args: global_cpp_args + global_cpp_defs,
)
storage_dep = declare_dependency(
link_with: lib_platform_storage_stub,
include_directories: [platform_inc],
)
platform_storage_sources = files('stub/StubStorage.cpp')