using System;
using System.IO;
using System.Security.Cryptography;
using Ashfall.Core;
using UnityEngine;
namespace Ashfall.Runtime
{
public sealed class UnityWorldCodec : IWorldCodec
{
public string Encode(WorldState world) => JsonUtility.ToJson(world);
public WorldState Decode(string json) => JsonUtility.FromJson<WorldState>(json);
}
[Serializable]
public sealed class JoinIdentity
{
public string playerId, token, name;
public Species species;
public const string Protocol = "ashfall-prototype-1";
public string protocol = Protocol;
}
[Serializable]
public sealed class WorldPacket
{
public bool full;
public WorldState world;
public string recipient;
}
[Serializable]
public sealed class CommandReply { public long sequence; public bool success; public string message; }
public static class LocalProfile
{
public static JoinIdentity Load(string profile)
{
string safe = "";
foreach (char c in profile) if (char.IsLetterOrDigit(c) || c == '-') safe += c;
if (safe == "") safe = "default";
string directory = Path.Combine(Application.persistentDataPath, "Profiles"); Directory.CreateDirectory(directory);
string path = Path.Combine(directory, safe + ".json");
if (File.Exists(path))
{
var identity = JsonUtility.FromJson<JoinIdentity>(File.ReadAllText(path));
if (identity != null && Guid.TryParse(identity.playerId, out _) && identity.token?.Length == 64) return identity;
throw new InvalidDataException("本地身份文件无效;请备份 Profiles 目录后使用新的 --profile 名称。");
}
byte[] secret = new byte[32]; using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(secret);
var value = new JoinIdentity { playerId = Guid.NewGuid().ToString("N"), token = BitConverter.ToString(secret).Replace("-", ""), name = "幸存者" };
File.WriteAllText(path, JsonUtility.ToJson(value)); return value;
}
public static string TokenHash(string token)
{ using (var hash = SHA256.Create()) return Convert.ToBase64String(hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(token ?? ""))); }
}
}
using System;
using System.IO;
using System.Security.Cryptography;
using Ashfall.Core;
using UnityEngine;
namespace Ashfall.Runtime
{
public sealed class UnityWorldCodec : IWorldCodec
{
public string Encode(WorldState world) => JsonUtility.ToJson(world);
public WorldState Decode(string json) => JsonUtility.FromJson<WorldState>(json);
}
[Serializable]
public sealed class JoinIdentity
{
public string playerId, token, name;
public Species species;
public const string Protocol = "ashfall-prototype-1";
public string protocol = Protocol;
}
[Serializable]
public sealed class WorldPacket
{
public bool full;
public WorldState world;
public string recipient;
}
[Serializable]
public sealed class CommandReply { public long sequence; public bool success; public string message; }
public static class LocalProfile
{
public static JoinIdentity Load(string profile)
{
string safe = "";
foreach (char c in profile) if (char.IsLetterOrDigit(c) || c == '-') safe += c;
if (safe == "") safe = "default";
string directory = Path.Combine(Application.persistentDataPath, "Profiles"); Directory.CreateDirectory(directory);
string path = Path.Combine(directory, safe + ".json");
if (File.Exists(path))
{
var identity = JsonUtility.FromJson<JoinIdentity>(File.ReadAllText(path));
if (identity != null && Guid.TryParse(identity.playerId, out _) && identity.token?.Length == 64) return identity;
throw new InvalidDataException("本地身份文件无效;请备份 Profiles 目录后使用新的 --profile 名称。");
}
byte[] secret = new byte[32]; using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(secret);
var value = new JoinIdentity { playerId = Guid.NewGuid().ToString("N"), token = BitConverter.ToString(secret).Replace("-", ""), name = "幸存者" };
File.WriteAllText(path, JsonUtility.ToJson(value)); return value;
}
public static string TokenHash(string token)
{ using (var hash = SHA256.Create()) return Convert.ToBase64String(hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(token ?? ""))); }
}
}