299 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Threading.Tasks;
using Logic.CrashSight;
using MemoryPack;
using RuntimeData;
#if STEAM_CHANNEL || STEAMWORKS_NET
using Steamworks;
#endif
using TH1_Logic.Collect;
using TH1_Logic.Config;
using TH1_Logic.Core;
using TH1_Logic.GameArchive;
using TH1_Logic.Net;
using TH1_Logic.Tools;
using UnityEngine;
namespace TH1_Logic.Oss
{
public class OssManager
{
public static OssManager Instance = new OssManager();
private const string FunctionUrl = "https://get-sts-token-qltjykaafr.cn-shanghai.fcapp.run";
private readonly StsTokenService _stsService;
private readonly OssUploadService _uploadService;
private StsCredentials _cachedCredentials;
private DateTime _credentialsExpireTime;
private StsCredentials _cachedCollectCredentials;
private DateTime _collectCredentialsExpireTime;
public const int MaxBugReportUploadBytes = PlayerBugReportService.MaxBugReportUploadBytes;
public const int MaxMultilingualReportUploadBytes = PlayerMultilingualReportService.MaxMultilingualReportUploadBytes;
private const int SteamAuthWarmupRetrySeconds = 30;
private const int SteamAuthWarmupRefreshSeconds = 4 * 60;
private DateTime _nextSteamAuthWarmupTime = DateTime.MinValue;
private bool _isSteamAuthWarmupRunning;
public OssManager()
{
_stsService = new StsTokenService(FunctionUrl);
_uploadService = new OssUploadService();
}
public void UploadMapData(string steamId, MapData endMap)
{
if (endMap.Net.Mode == NetMode.Multi && !LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
if (!GameArchiveManager.Instance.TryLoadCurrentBeginArchive(endMap.Net.Mode, out var beginMap))
{
LogSystem.LogError($"UploadMapData Error beginMap is null : {endMap.Net.Mode} {endMap.MapID}");
return;
}
var ossData = new OssData();
ossData.StartMap = beginMap;
ossData.Actions = endMap.Net.Actions;
ossData.CollectData = CollectManager.Instance.CollectData;
ossData.CollectData.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
byte[] bytes = TH1Serialization.Serialize(ossData);
_ = UploadGameDataAsync(steamId, bytes);
}
public async Task<bool> UploadGameDataAsync(string steamId, byte[] data)
{
try
{
// 检查缓存的凭证是否有效提前60秒刷新
if (_cachedCredentials == null || DateTime.UtcNow >= _credentialsExpireTime.AddSeconds(-60))
{
if (!TryGetAuthTicket(out var authTicket)) return false;
_cachedCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "ossdata");
_credentialsExpireTime = DateTime.UtcNow.AddSeconds(_cachedCredentials.expiresIn);
LogSystem.LogInfo($"STS token obtained, expires at {_credentialsExpireTime}");
}
var result = await _uploadService.UploadFileAsync(_cachedCredentials, data);
if (result)
{
LogSystem.LogInfo($"Upload success: {_cachedCredentials.objectKey}");
}
return result;
}
catch (Exception ex)
{
LogSystem.LogError($"Upload failed: {ex.Message}");
_cachedCredentials = null;
return false;
}
}
public void UploadCollectData(string steamId, MapData endMap)
{
if (endMap.Net.Mode == NetMode.Multi && !LobbyManager.Instance.Lobby.IsLobbyOwner()) return;
var collectData = CollectManager.Instance.CollectData;
collectData.MemberId = LobbyManager.Instance.Lobby.GetSelfMemberId();
collectData.Version = ConfigManager.Instance.VersionCfg.CurVersionInfo.Version;
_ = UploadCollectDataAsync(steamId, collectData);
}
public async Task<bool> UploadCollectDataAsync(string steamId, CollectData collectData)
{
try
{
// 检查缓存的凭证是否有效提前60秒刷新
if (_cachedCollectCredentials == null || DateTime.UtcNow >= _collectCredentialsExpireTime.AddSeconds(-60))
{
if (!TryGetAuthTicket(out var authTicket)) return false;
_cachedCollectCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "collectdata");
_collectCredentialsExpireTime = DateTime.UtcNow.AddSeconds(_cachedCollectCredentials.expiresIn);
LogSystem.LogInfo($"Collect STS token obtained, expires at {_collectCredentialsExpireTime}");
}
byte[] data = TH1Serialization.Serialize(collectData);
var result = await _uploadService.UploadFileAsync(_cachedCollectCredentials, data);
if (result)
{
LogSystem.LogInfo($"CollectData upload success: {_cachedCollectCredentials.objectKey}");
}
return result;
}
catch (Exception ex)
{
LogSystem.LogError($"CollectData upload failed: {ex.Message}");
_cachedCollectCredentials = null;
return false;
}
}
public async Task<(bool success, string objectKey)> UploadPlayerBugReportAsync(string steamId, byte[] packageData,
string version)
{
try
{
if (packageData == null || packageData.Length == 0)
{
LogSystem.LogError("PlayerBugReport upload failed: package data is empty");
return (false, null);
}
if (packageData.Length > MaxBugReportUploadBytes)
{
LogSystem.LogError($"PlayerBugReport upload failed: package size {packageData.Length} exceeds {MaxBugReportUploadBytes}");
return (false, null);
}
if (!TryGetAuthTicket(out var authTicket)) return (false, null);
var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "bugreport", version);
var result = await _uploadService.UploadFileAsync(credentials, packageData, "application/zip",
MaxBugReportUploadBytes);
if (result)
{
LogSystem.LogInfo($"PlayerBugReport upload success: {credentials.objectKey}");
return (true, credentials.objectKey);
}
return (false, credentials.objectKey);
}
catch (Exception ex)
{
LogSystem.LogError($"PlayerBugReport upload failed: {ex.Message}");
return (false, null);
}
}
public async Task<(bool success, string objectKey)> UploadPlayerMultilingualReportAsync(string steamId,
byte[] packageData, string version)
{
try
{
if (packageData == null || packageData.Length == 0)
{
LogSystem.LogError("PlayerMultilingualReport upload failed: package data is empty");
return (false, null);
}
if (packageData.Length > MaxMultilingualReportUploadBytes)
{
LogSystem.LogError(
$"PlayerMultilingualReport upload failed: package size {packageData.Length} exceeds {MaxMultilingualReportUploadBytes}");
return (false, null);
}
if (!TryGetAuthTicket(out var authTicket)) return (false, null);
var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "multilingualreport",
version);
var result = await _uploadService.UploadFileAsync(credentials, packageData, "application/zip",
MaxMultilingualReportUploadBytes);
if (result)
{
LogSystem.LogInfo($"PlayerMultilingualReport upload success: {credentials.objectKey}");
return (true, credentials.objectKey);
}
return (false, credentials.objectKey);
}
catch (Exception ex)
{
LogSystem.LogError($"PlayerMultilingualReport upload failed: {ex.Message}");
return (false, null);
}
}
public void UpdateSteamAuthWarmup()
{
if (_isSteamAuthWarmupRunning) return;
if (DateTime.UtcNow < _nextSteamAuthWarmupTime) return;
if (!TryGetSteamId(out var steamId))
{
_nextSteamAuthWarmupTime = DateTime.UtcNow.AddSeconds(15);
return;
}
_ = WarmupSteamAuthAsync(steamId);
}
public async Task<bool> WarmupSteamAuthAsync(string steamId)
{
_isSteamAuthWarmupRunning = true;
try
{
if (!TryGetAuthTicket(out var authTicket)) return false;
var response = await _stsService.WarmupSteamAuthAsync(steamId, authTicket);
var refreshSeconds = response != null && response.expiresIn > 90
? Math.Min(SteamAuthWarmupRefreshSeconds, response.expiresIn - 60)
: SteamAuthWarmupRetrySeconds;
_nextSteamAuthWarmupTime = DateTime.UtcNow.AddSeconds(refreshSeconds);
LogSystem.LogInfo($"Steam auth warmup success, cached={response?.cached}, expiresIn={response?.expiresIn}s");
return true;
}
catch (Exception ex)
{
_nextSteamAuthWarmupTime = DateTime.UtcNow.AddSeconds(SteamAuthWarmupRetrySeconds);
LogSystem.LogWarning($"Steam auth warmup failed: {ex.Message}");
return false;
}
finally
{
_isSteamAuthWarmupRunning = false;
}
}
private static bool TryGetSteamId(out string steamId)
{
steamId = "";
#if STEAM_CHANNEL || STEAMWORKS_NET
try
{
if (!SteamUser.BLoggedOn()) return false;
var id = SteamUser.GetSteamID().m_SteamID;
if (id == 0) return false;
steamId = id.ToString();
return true;
}
catch
{
return false;
}
#else
return false;
#endif
}
private static bool TryGetAuthTicket(out string authTicket)
{
authTicket = null;
#if STEAM_CHANNEL || STEAMWORKS_NET
try
{
var ticket = new byte[1024];
var identity = new SteamNetworkingIdentity();
SteamUser.GetAuthSessionTicket(ticket, 1024, out var ticketSize, ref identity);
if (ticketSize == 0)
{
LogSystem.LogWarning("Steam auth ticket is empty");
return false;
}
authTicket = BitConverter.ToString(ticket, 0, (int)ticketSize).Replace("-", "").ToLower();
return true;
}
catch (Exception ex)
{
LogSystem.LogWarning($"Steam auth ticket unavailable: {ex.Message}");
return false;
}
#else
return false;
#endif
}
}
}