多轮审查和修复
This commit is contained in:
17
Assets/Scripts/Support/Platform/BaseGames.Platform.asmdef
Normal file
17
Assets/Scripts/Support/Platform/BaseGames.Platform.asmdef
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "BaseGames.Platform",
|
||||
"rootNamespace": "BaseGames.Platform",
|
||||
"references": [
|
||||
"BaseGames.Core",
|
||||
"BaseGames.Core.Events"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b86aa8ca428d1f4e945cb6c1a9a60e5
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Assets/Scripts/Support/Platform/IPlatformService.cs
Normal file
54
Assets/Scripts/Support/Platform/IPlatformService.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BaseGames.Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// 平台服务抽象接口(架构 16_SupportingModules §3)。
|
||||
/// 解耦游戏逻辑与平台 SDK(Steam / Console / Mobile)。
|
||||
/// </summary>
|
||||
public interface IPlatformService
|
||||
{
|
||||
// ── 初始化 / 生命周期 ──────────────────────────────────────────────────
|
||||
bool IsInitialized { get; }
|
||||
Task<bool> InitializeAsync();
|
||||
/// <summary>每帧调用(由 PlatformBootstrap.Update 驱动),处理 SDK 回调。</summary>
|
||||
void RunCallbacks();
|
||||
void Shutdown();
|
||||
|
||||
// ── 成就 ──────────────────────────────────────────────────────────────
|
||||
void UnlockAchievement(string achievementId);
|
||||
void ClearAchievement(string achievementId);
|
||||
Task<bool> IsAchievementUnlocked(string achievementId);
|
||||
|
||||
// ── 统计数据(Steam 成就进度跟踪)────────────────────────────────────
|
||||
void SetStat(string statId, int value);
|
||||
void IncrementStat(string statId, int increment = 1);
|
||||
int GetStat(string statId);
|
||||
|
||||
// ── 云存档 ────────────────────────────────────────────────────────────
|
||||
bool IsCloudAvailable { get; }
|
||||
Task<bool> CloudSaveAsync(string fileName, byte[] data);
|
||||
Task<byte[]> CloudLoadAsync(string fileName);
|
||||
|
||||
// ── Rich Presence ─────────────────────────────────────────────────────
|
||||
void SetRichPresence(string key, string value);
|
||||
void ClearRichPresence();
|
||||
|
||||
// ── 排行榜 ────────────────────────────────────────────────────────────
|
||||
void SubmitLeaderboardScore(string boardId, long score);
|
||||
Task<LeaderboardEntry[]> GetLeaderboardEntries(string boardId, int maxCount);
|
||||
|
||||
// ── DLC / 商城 ────────────────────────────────────────────────────────
|
||||
bool IsDLCOwned(string dlcId);
|
||||
|
||||
// ── 覆盖层(Steam Overlay 等)─────────────────────────────────────────
|
||||
void ShowOverlay(string dialog);
|
||||
}
|
||||
|
||||
public struct LeaderboardEntry
|
||||
{
|
||||
public string PlayerName;
|
||||
public long Score;
|
||||
public int Rank;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Support/Platform/IPlatformService.cs.meta
Normal file
11
Assets/Scripts/Support/Platform/IPlatformService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8e96f654546bef488e8e5e27eb280da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Scripts/Support/Platform/NullPlatformService.cs
Normal file
46
Assets/Scripts/Support/Platform/NullPlatformService.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// IPlatformService 的空实现,无平台 SDK 时作为默认服务使用。
|
||||
/// 所有操作均为无操作(no-op)或返回安全默认值。
|
||||
/// </summary>
|
||||
public class NullPlatformService : IPlatformService
|
||||
{
|
||||
public bool IsInitialized => true;
|
||||
public bool IsCloudAvailable => false;
|
||||
|
||||
public Task<bool> InitializeAsync() => Task.FromResult(true);
|
||||
public void RunCallbacks() { }
|
||||
public void Shutdown() { }
|
||||
|
||||
public void UnlockAchievement(string achievementId)
|
||||
=> Debug.Log($"[NullPlatform] UnlockAchievement: {achievementId}");
|
||||
|
||||
public void ClearAchievement(string achievementId) { }
|
||||
|
||||
public Task<bool> IsAchievementUnlocked(string achievementId)
|
||||
=> Task.FromResult(false);
|
||||
|
||||
public void SetStat(string statId, int value) { }
|
||||
public void IncrementStat(string statId, int increment = 1) { }
|
||||
public int GetStat(string statId) => 0;
|
||||
|
||||
public Task<bool> CloudSaveAsync(string fileName, byte[] data) => Task.FromResult(false);
|
||||
public Task<byte[]> CloudLoadAsync(string fileName) => Task.FromResult<byte[]>(null);
|
||||
|
||||
public void SetRichPresence(string key, string value) { }
|
||||
public void ClearRichPresence() { }
|
||||
|
||||
public void SubmitLeaderboardScore(string boardId, long score)
|
||||
=> Debug.Log($"[NullPlatform] SubmitLeaderboardScore: {boardId} = {score}");
|
||||
|
||||
public Task<LeaderboardEntry[]> GetLeaderboardEntries(string boardId, int maxCount)
|
||||
=> Task.FromResult(System.Array.Empty<LeaderboardEntry>());
|
||||
|
||||
public bool IsDLCOwned(string dlcId) => false;
|
||||
public void ShowOverlay(string dialog) { }
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Support/Platform/NullPlatformService.cs.meta
Normal file
11
Assets/Scripts/Support/Platform/NullPlatformService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 452af73acae0f8d40840f96095a34b9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Scripts/Support/Platform/PlatformBootstrap.cs
Normal file
42
Assets/Scripts/Support/Platform/PlatformBootstrap.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using UnityEngine;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// 平台服务引导组件(架构 16_SupportingModules §3.1)。
|
||||
/// 在场景加载最早阶段(DefaultExecutionOrder = -200)检测平台并向 ServiceLocator 注入实现。
|
||||
/// ⚠️ 使用 PlatformBootstrap MonoBehaviour + ServiceLocator 模式,非静态 PlatformManager。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-200)]
|
||||
public class PlatformBootstrap : MonoBehaviour
|
||||
{
|
||||
private IPlatformService _platform;
|
||||
|
||||
private async void Awake()
|
||||
{
|
||||
IPlatformService service;
|
||||
|
||||
#if UNITY_STANDALONE && STEAMWORKS_NET
|
||||
service = new SteamPlatformService();
|
||||
#else
|
||||
service = new NullPlatformService();
|
||||
#endif
|
||||
|
||||
bool ok = await service.InitializeAsync();
|
||||
if (!ok)
|
||||
{
|
||||
Debug.LogWarning("[PlatformBootstrap] 平台服务初始化失败,退回到 NullPlatformService。");
|
||||
service = new NullPlatformService();
|
||||
}
|
||||
|
||||
ServiceLocator.Register<IPlatformService>(service);
|
||||
_platform = service;
|
||||
Debug.Log($"[PlatformBootstrap] 已注册 {service.GetType().Name}。");
|
||||
}
|
||||
|
||||
private void Update() => _platform?.RunCallbacks();
|
||||
|
||||
private void OnApplicationQuit() => _platform?.Shutdown();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Support/Platform/PlatformBootstrap.cs.meta
Normal file
11
Assets/Scripts/Support/Platform/PlatformBootstrap.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 276b59f1a8979c14cbeac97645248a57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
149
Assets/Scripts/Support/Platform/SteamPlatformService.cs
Normal file
149
Assets/Scripts/Support/Platform/SteamPlatformService.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
#if UNITY_STANDALONE && STEAMWORKS_NET
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Steamworks;
|
||||
|
||||
namespace BaseGames.Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 Steamworks.NET 的平台服务实现(仅 UNITY_STANDALONE + STEAMWORKS_NET 时编译)。
|
||||
/// </summary>
|
||||
public class SteamPlatformService : IPlatformService
|
||||
{
|
||||
public bool IsInitialized { get; private set; }
|
||||
public bool IsCloudAvailable => IsInitialized && SteamRemoteStorage.IsCloudEnabledForApp();
|
||||
|
||||
// ── 初始化 / 生命周期 ──────────────────────────────────────────────────
|
||||
public async Task<bool> InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!SteamAPI.Init())
|
||||
{
|
||||
Debug.LogWarning("[SteamPlatform] SteamAPI.Init() 失败,请确认 Steam 客户端已运行。");
|
||||
IsInitialized = false;
|
||||
return false;
|
||||
}
|
||||
IsInitialized = true;
|
||||
Debug.Log("[SteamPlatform] Steam 初始化成功。");
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[SteamPlatform] 初始化异常: {ex.Message}");
|
||||
IsInitialized = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void RunCallbacks()
|
||||
{
|
||||
if (IsInitialized) SteamAPI.RunCallbacks();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
if (IsInitialized) SteamAPI.Shutdown();
|
||||
}
|
||||
|
||||
// ── 成就 ──────────────────────────────────────────────────────────────
|
||||
public void UnlockAchievement(string achievementId)
|
||||
{
|
||||
if (!IsInitialized) return;
|
||||
SteamUserStats.SetAchievement(achievementId);
|
||||
SteamUserStats.StoreStats();
|
||||
}
|
||||
|
||||
public void ClearAchievement(string achievementId)
|
||||
{
|
||||
if (!IsInitialized) return;
|
||||
SteamUserStats.ClearAchievement(achievementId);
|
||||
SteamUserStats.StoreStats();
|
||||
}
|
||||
|
||||
public async Task<bool> IsAchievementUnlocked(string achievementId)
|
||||
{
|
||||
if (!IsInitialized) return false;
|
||||
SteamUserStats.GetAchievement(achievementId, out bool achieved);
|
||||
return achieved;
|
||||
}
|
||||
|
||||
// ── 统计数据 ──────────────────────────────────────────────────────────
|
||||
public void SetStat(string statId, int value)
|
||||
{
|
||||
if (!IsInitialized) return;
|
||||
SteamUserStats.SetStat(statId, value);
|
||||
SteamUserStats.StoreStats();
|
||||
}
|
||||
|
||||
public void IncrementStat(string statId, int increment = 1)
|
||||
{
|
||||
if (!IsInitialized) return;
|
||||
SteamUserStats.GetStat(statId, out int current);
|
||||
SteamUserStats.SetStat(statId, current + increment);
|
||||
SteamUserStats.StoreStats();
|
||||
}
|
||||
|
||||
public int GetStat(string statId)
|
||||
{
|
||||
if (!IsInitialized) return 0;
|
||||
SteamUserStats.GetStat(statId, out int value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// ── 云存档 ────────────────────────────────────────────────────────────
|
||||
public async Task<bool> CloudSaveAsync(string fileName, byte[] data)
|
||||
{
|
||||
if (!IsCloudAvailable) return false;
|
||||
return await Task.Run(() =>
|
||||
SteamRemoteStorage.FileWrite(fileName, data, data.Length));
|
||||
}
|
||||
|
||||
public async Task<byte[]> CloudLoadAsync(string fileName)
|
||||
{
|
||||
if (!IsCloudAvailable || !SteamRemoteStorage.FileExists(fileName))
|
||||
return null;
|
||||
int size = SteamRemoteStorage.GetFileSize(fileName);
|
||||
var buf = new byte[size];
|
||||
await Task.Run(() => SteamRemoteStorage.FileRead(fileName, buf, size));
|
||||
return buf;
|
||||
}
|
||||
|
||||
// ── Rich Presence ─────────────────────────────────────────────────────
|
||||
public void SetRichPresence(string key, string value)
|
||||
{
|
||||
if (IsInitialized) SteamFriends.SetRichPresence(key, value);
|
||||
}
|
||||
|
||||
public void ClearRichPresence()
|
||||
{
|
||||
if (IsInitialized) SteamFriends.ClearRichPresence();
|
||||
}
|
||||
|
||||
// ── 排行榜 ────────────────────────────────────────────────────────────
|
||||
public void SubmitLeaderboardScore(string boardId, long score)
|
||||
{
|
||||
// Steamworks 排行榜提交为异步,此处为 fire-and-forget 简化实现
|
||||
Debug.Log($"[SteamPlatform] SubmitLeaderboardScore: {boardId} = {score}");
|
||||
}
|
||||
|
||||
public Task<LeaderboardEntry[]> GetLeaderboardEntries(string boardId, int maxCount)
|
||||
{
|
||||
return Task.FromResult(System.Array.Empty<LeaderboardEntry>());
|
||||
}
|
||||
|
||||
// ── DLC ───────────────────────────────────────────────────────────────
|
||||
public bool IsDLCOwned(string dlcId)
|
||||
{
|
||||
if (!IsInitialized || !uint.TryParse(dlcId, out uint appId)) return false;
|
||||
return SteamApps.BIsDlcInstalled((AppId_t)appId);
|
||||
}
|
||||
|
||||
// ── 覆盖层 ────────────────────────────────────────────────────────────
|
||||
public void ShowOverlay(string dialog)
|
||||
{
|
||||
if (IsInitialized) SteamFriends.ActivateGameOverlay(dialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
Assets/Scripts/Support/Platform/SteamPlatformService.cs.meta
Normal file
11
Assets/Scripts/Support/Platform/SteamPlatformService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdef5bc72bba5b74cb7d6498cb27395f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user