feat: polish VR gameplay and sync tools
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
@@ -25,14 +26,20 @@ public class NasPublisher : MonoBehaviour
|
||||
private void LoadConfig()
|
||||
{
|
||||
string path = Path.Combine(Application.streamingAssetsPath, "nas_config.json");
|
||||
if (!File.Exists(path)) { Debug.LogWarning("[NasPublisher] nas_config.json not found: " + path); return; }
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
NormalizeSettings();
|
||||
return;
|
||||
}
|
||||
var cfg = JsonUtility.FromJson<NasConfig>(File.ReadAllText(path));
|
||||
if (cfg == null) return;
|
||||
_password = cfg.password ?? "";
|
||||
if (!string.IsNullOrEmpty(cfg.host)) nasBaseUrl = cfg.host;
|
||||
if (!string.IsNullOrEmpty(cfg.account)) nasAccount = cfg.account;
|
||||
if (!string.IsNullOrEmpty(cfg.rootPath)) nasRootPath = cfg.rootPath;
|
||||
if (!string.IsNullOrEmpty(cfg.staticUrl)) staticBaseUrl = cfg.staticUrl;
|
||||
if (!string.IsNullOrWhiteSpace(cfg.host)) nasBaseUrl = cfg.host.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(cfg.account)) nasAccount = cfg.account.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(cfg.rootPath)) nasRootPath = cfg.rootPath.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(cfg.staticUrl)) staticBaseUrl = cfg.staticUrl.Trim();
|
||||
|
||||
NormalizeSettings();
|
||||
}
|
||||
|
||||
[Serializable] private class NasConfig
|
||||
@@ -52,6 +59,8 @@ public class NasPublisher : MonoBehaviour
|
||||
Action onComplete,
|
||||
Action<string> onError)
|
||||
{
|
||||
NormalizeSettings();
|
||||
|
||||
bool failed = false;
|
||||
void OnErr(string e) { onError?.Invoke(e); failed = true; }
|
||||
|
||||
@@ -92,31 +101,53 @@ public class NasPublisher : MonoBehaviour
|
||||
|
||||
private IEnumerator Login(Action<string> onError)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_password))
|
||||
{
|
||||
onError?.Invoke("NAS password missing. Create Assets/StreamingAssets/nas_config.json with host, account, rootPath, staticUrl, and password.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
string url = $"{nasBaseUrl}/webapi/auth.cgi" +
|
||||
$"?api=SYNO.API.Auth&version=6&method=login" +
|
||||
$"&account={UnityWebRequest.EscapeURL(nasAccount)}" +
|
||||
$"&passwd={UnityWebRequest.EscapeURL(_password)}" +
|
||||
$"&session=FileStation&format=sid&enable_syno_token=yes";
|
||||
|
||||
using var req = UnityWebRequest.Get(url);
|
||||
yield return req.SendWebRequest();
|
||||
|
||||
if (req.result != UnityWebRequest.Result.Success)
|
||||
UnityWebRequest req;
|
||||
try
|
||||
{
|
||||
onError?.Invoke($"DSM login failed: {req.error}");
|
||||
req = UnityWebRequest.Get(url);
|
||||
}
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
onError?.Invoke($"DSM login URL invalid: '{nasBaseUrl}' — {e.Message}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
string resp = req.downloadHandler.text;
|
||||
_sid = ParseJsonString(resp, "sid");
|
||||
_synoToken = ParseJsonString(resp, "synotoken");
|
||||
string resp;
|
||||
using (req)
|
||||
{
|
||||
yield return req.SendWebRequest();
|
||||
|
||||
if (req.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
onError?.Invoke($"DSM login failed: {req.error}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
resp = req.downloadHandler.text;
|
||||
_sid = ParseJsonString(resp, "sid");
|
||||
_synoToken = ParseJsonString(resp, "synotoken");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_sid))
|
||||
onError?.Invoke("DSM sid parse failed — check credentials.");
|
||||
onError?.Invoke($"DSM sid parse failed. Check NAS account/password/permissions. Response: {Shorten(resp)}");
|
||||
}
|
||||
|
||||
private IEnumerator Logout()
|
||||
{
|
||||
NormalizeSettings();
|
||||
|
||||
string url = $"{nasBaseUrl}/webapi/auth.cgi" +
|
||||
$"?api=SYNO.API.Auth&version=1&method=logout&session=FileStation&_sid={_sid}";
|
||||
using var req = UnityWebRequest.Get(url);
|
||||
@@ -133,6 +164,8 @@ public class NasPublisher : MonoBehaviour
|
||||
private IEnumerator UploadBytes(byte[] bytes, string fileName,
|
||||
string nasFolder, Action<string> onError)
|
||||
{
|
||||
NormalizeSettings();
|
||||
|
||||
string uploadUrl = $"{nasBaseUrl}/webapi/entry.cgi" +
|
||||
$"?api=SYNO.FileStation.Upload&version=2&method=upload" +
|
||||
$"&_sid={UnityWebRequest.EscapeURL(_sid)}";
|
||||
@@ -179,6 +212,8 @@ public class NasPublisher : MonoBehaviour
|
||||
|
||||
private IEnumerator PatchSongsJson(SongInfo newSong, Action<string> onError)
|
||||
{
|
||||
NormalizeSettings();
|
||||
|
||||
SongsList list = null;
|
||||
|
||||
using (var req = UnityWebRequest.Get($"{staticBaseUrl}/songs.json"))
|
||||
@@ -201,12 +236,16 @@ public class NasPublisher : MonoBehaviour
|
||||
|
||||
private static string ParseJsonString(string json, string key)
|
||||
{
|
||||
string search = $"\"{key}\":\"";
|
||||
int start = json.IndexOf(search, StringComparison.Ordinal);
|
||||
if (start < 0) return null;
|
||||
start += search.Length;
|
||||
int end = json.IndexOf('"', start);
|
||||
return end > start ? json.Substring(start, end - start) : null;
|
||||
if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key))
|
||||
return null;
|
||||
|
||||
Match match = Regex.Match(
|
||||
json,
|
||||
$"\"{Regex.Escape(key)}\"\\s*:\\s*\"(?<value>(?:\\\\.|[^\"])*)\"");
|
||||
|
||||
return match.Success
|
||||
? Regex.Unescape(match.Groups["value"].Value)
|
||||
: null;
|
||||
}
|
||||
|
||||
private static void AssignMapFile(SongInfo song, string diff, string fileName)
|
||||
@@ -214,4 +253,38 @@ public class NasPublisher : MonoBehaviour
|
||||
var info = song.difficulties.Get(diff);
|
||||
if (info != null) info.mapFile = $"maps/{fileName}";
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
NormalizeSettings();
|
||||
}
|
||||
|
||||
private void NormalizeSettings()
|
||||
{
|
||||
nasBaseUrl = NormalizeBaseUrl(nasBaseUrl);
|
||||
staticBaseUrl = NormalizeBaseUrl(staticBaseUrl);
|
||||
nasAccount = nasAccount?.Trim() ?? "";
|
||||
nasRootPath = NormalizeRootPath(nasRootPath);
|
||||
}
|
||||
|
||||
private static string NormalizeBaseUrl(string value)
|
||||
{
|
||||
return (value ?? "").Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
private static string NormalizeRootPath(string value)
|
||||
{
|
||||
value = (value ?? "").Trim().Replace('\\', '/');
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return "/";
|
||||
return value.StartsWith("/") ? value.TrimEnd('/') : "/" + value.TrimEnd('/');
|
||||
}
|
||||
|
||||
private static string Shorten(string value, int maxLength = 240)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
|
||||
return value ?? "";
|
||||
|
||||
return value.Substring(0, maxLength) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user