271 lines
7.4 KiB
C#
271 lines
7.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using GameServer.Client;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
public static class DlcRemoteManifestSyncService
|
|
{
|
|
[Serializable]
|
|
private sealed class RemoteManifestEnvelope
|
|
{
|
|
public bool success;
|
|
public string message;
|
|
public RemoteManifestItem[] dlcs;
|
|
}
|
|
|
|
[Serializable]
|
|
private sealed class RemoteManifestItem
|
|
{
|
|
public string dlc_key;
|
|
public string display_name;
|
|
public string version;
|
|
public string manifest_file_name;
|
|
public string manifest_json;
|
|
public string uploaded_at;
|
|
public string updated_at;
|
|
}
|
|
|
|
private sealed class DlcRemoteManifestSyncRunner : MonoBehaviour
|
|
{
|
|
private void Start()
|
|
{
|
|
if (autoSyncOnStart)
|
|
{
|
|
SyncPublishedDlcs();
|
|
}
|
|
}
|
|
}
|
|
|
|
private const string DefaultServerUrl = "http://47.112.187.172:8080";
|
|
private const string RemoteManifestFilePrefix = "remote_";
|
|
private const string ManifestApiPath = "/api/dlcs/manifests";
|
|
|
|
private static DlcRemoteManifestSyncRunner runner;
|
|
private static bool autoSyncOnStart = true;
|
|
private static bool syncRequestedWhileBusy;
|
|
private static bool initialSyncPending = true;
|
|
|
|
public static bool IsSyncing { get; private set; }
|
|
public static event Action<bool> SyncCompleted;
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
private static void EnsureRunner()
|
|
{
|
|
if (runner != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GameObject go = new GameObject(nameof(DlcRemoteManifestSyncService));
|
|
UnityEngine.Object.DontDestroyOnLoad(go);
|
|
runner = go.AddComponent<DlcRemoteManifestSyncRunner>();
|
|
}
|
|
|
|
public static void SetAutoSyncOnStart(bool enabled)
|
|
{
|
|
autoSyncOnStart = enabled;
|
|
if (!enabled)
|
|
{
|
|
initialSyncPending = false;
|
|
}
|
|
}
|
|
|
|
public static bool ShouldDeferInitialRefresh()
|
|
{
|
|
return autoSyncOnStart && initialSyncPending && !OnlineModeSettings.IsLocalOnlyMode;
|
|
}
|
|
|
|
public static void SyncPublishedDlcs()
|
|
{
|
|
EnsureRunner();
|
|
if (runner == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsSyncing)
|
|
{
|
|
syncRequestedWhileBusy = true;
|
|
return;
|
|
}
|
|
|
|
runner.StartCoroutine(SyncPublishedDlcsRoutine());
|
|
}
|
|
|
|
private static IEnumerator SyncPublishedDlcsRoutine()
|
|
{
|
|
if (IsSyncing)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
if (OnlineModeSettings.IsLocalOnlyMode)
|
|
{
|
|
initialSyncPending = false;
|
|
DlcRemoteContentService.RefreshInstalledDlc();
|
|
SyncCompleted?.Invoke(false);
|
|
yield break;
|
|
}
|
|
|
|
string url = BuildManifestApiUrl();
|
|
if (string.IsNullOrWhiteSpace(url))
|
|
{
|
|
initialSyncPending = false;
|
|
DlcRemoteContentService.RefreshInstalledDlc();
|
|
SyncCompleted?.Invoke(false);
|
|
yield break;
|
|
}
|
|
|
|
IsSyncing = true;
|
|
syncRequestedWhileBusy = false;
|
|
bool success = false;
|
|
|
|
using (UnityWebRequest request = UnityWebRequest.Get(url))
|
|
{
|
|
request.timeout = 10;
|
|
yield return request.SendWebRequest();
|
|
|
|
if (request.result == UnityWebRequest.Result.Success)
|
|
{
|
|
string responseText = request.downloadHandler != null ? request.downloadHandler.text : string.Empty;
|
|
success = TryApplyRemoteManifestPayload(responseText);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("[DLC] Remote manifest sync failed: " + request.error);
|
|
}
|
|
}
|
|
|
|
initialSyncPending = false;
|
|
|
|
if (success)
|
|
{
|
|
DlcRemoteContentService.RefreshInstalledDlc();
|
|
}
|
|
else
|
|
{
|
|
DlcRemoteContentService.RefreshInstalledDlc();
|
|
}
|
|
|
|
IsSyncing = false;
|
|
SyncCompleted?.Invoke(success);
|
|
|
|
if (syncRequestedWhileBusy)
|
|
{
|
|
SyncPublishedDlcs();
|
|
}
|
|
}
|
|
|
|
private static bool TryApplyRemoteManifestPayload(string json)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RemoteManifestEnvelope envelope;
|
|
try
|
|
{
|
|
envelope = JsonUtility.FromJson<RemoteManifestEnvelope>(json);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning("[DLC] Failed to parse remote manifest payload: " + ex.Message);
|
|
return false;
|
|
}
|
|
|
|
if (envelope == null || !envelope.success)
|
|
{
|
|
Debug.LogWarning("[DLC] Remote manifest payload reported failure.");
|
|
return false;
|
|
}
|
|
|
|
string manifestRoot = Path.Combine(Application.persistentDataPath, "DLC", "manifests");
|
|
try
|
|
{
|
|
Directory.CreateDirectory(manifestRoot);
|
|
DeleteExistingRemoteManifestFiles(manifestRoot);
|
|
|
|
RemoteManifestItem[] items = envelope.dlcs ?? Array.Empty<RemoteManifestItem>();
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
RemoteManifestItem item = items[i];
|
|
if (item == null || string.IsNullOrWhiteSpace(item.manifest_json))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string safeKey = SanitizeFileName(item.dlc_key);
|
|
if (string.IsNullOrWhiteSpace(safeKey))
|
|
{
|
|
safeKey = "package_" + i;
|
|
}
|
|
|
|
string fileName = RemoteManifestFilePrefix + safeKey + ".json";
|
|
string path = Path.Combine(manifestRoot, fileName);
|
|
File.WriteAllText(path, item.manifest_json);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning("[DLC] Failed to write remote manifests: " + ex.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static void DeleteExistingRemoteManifestFiles(string manifestRoot)
|
|
{
|
|
string[] files = Directory.GetFiles(manifestRoot, RemoteManifestFilePrefix + "*.json", SearchOption.TopDirectoryOnly);
|
|
for (int i = 0; i < files.Length; i++)
|
|
{
|
|
try
|
|
{
|
|
File.Delete(files[i]);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning("[DLC] Failed to delete old remote manifest '" + files[i] + "': " + ex.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string BuildManifestApiUrl()
|
|
{
|
|
string baseUrl = DefaultServerUrl;
|
|
NetworkManager manager = NetworkManager.Instance;
|
|
if (manager != null && !string.IsNullOrWhiteSpace(manager.ServerUrl))
|
|
{
|
|
baseUrl = manager.ServerUrl.Trim();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(baseUrl))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return baseUrl.TrimEnd('/') + ManifestApiPath;
|
|
}
|
|
|
|
private static string SanitizeFileName(string value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
char[] invalidChars = Path.GetInvalidFileNameChars();
|
|
string result = value.Trim();
|
|
for (int i = 0; i < invalidChars.Length; i++)
|
|
{
|
|
result = result.Replace(invalidChars[i], '_');
|
|
}
|
|
|
|
return result.Replace(' ', '_');
|
|
}
|
|
}
|