feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -0,0 +1,133 @@
|
||||
using System.Text.Json;
|
||||
using Ananta.Server.Configuration;
|
||||
using Ananta.Server.Protocol.Client4229938;
|
||||
|
||||
namespace Ananta.Server.ClientData.Client4229938;
|
||||
|
||||
/// <summary>
|
||||
/// 改装店改装件的落盘(<c>save/vehicles/<pid>.json</c>)。
|
||||
///
|
||||
/// <para>为什么需要:donor 最初把购买结果只放在 <c>WorldEntryState</c> 的内存字典里,
|
||||
/// 服务端一重启买过的件全没了(donor backlog #2 的原话)。买一次就要写一次盘 —— 没有可靠的
|
||||
/// session-end 钩子,所以走「每次购买后立刻写」。</para>
|
||||
///
|
||||
/// <para>开关 <c>gameplay.repairShop.persistTuning</c>(默认 false = 与 donor 默认一致):
|
||||
/// 关掉时本类只读不写,行为完全等同 donor 未接 store 时的样子。</para>
|
||||
///
|
||||
/// <para>写盘用 tmp + move 的原子替换,避免写一半崩掉留下截断的 json。</para>
|
||||
/// </summary>
|
||||
internal static class VehicleTuningStore
|
||||
{
|
||||
private static readonly object Gate = new();
|
||||
private static readonly JsonSerializerOptions Options = new() { WriteIndented = true };
|
||||
|
||||
private static string DirectoryPath =>
|
||||
PrivateServerConfigStore.ResolveProjectPath(Path.Combine("save", "vehicles"));
|
||||
|
||||
private static string PathFor(ulong pid) => Path.Combine(DirectoryPath, $"{pid}.json");
|
||||
|
||||
/// <summary>把当前会话的改装状态写盘。开关关闭或 world 为空时直接返回。</summary>
|
||||
internal static void Save(ulong pid, WorldEntryState? world)
|
||||
{
|
||||
if (!PrivateServerConfigStore.Current.Gameplay.RepairShop.PersistTuning)
|
||||
return;
|
||||
if (world is null)
|
||||
return;
|
||||
|
||||
List<VehicleDto> vehicles;
|
||||
lock (world.SyncRoot)
|
||||
{
|
||||
var configs = new HashSet<uint>();
|
||||
foreach (var key in world.VehiclePartsByConfig.Keys) configs.Add(key);
|
||||
foreach (var key in world.VehicleSuitByConfig.Keys) configs.Add(key);
|
||||
|
||||
vehicles = new List<VehicleDto>(configs.Count);
|
||||
foreach (var configId in configs)
|
||||
{
|
||||
var parts = world.VehiclePartsByConfig.TryGetValue(configId, out var list)
|
||||
? list.Select(p => new PartDto { Type = p.Type, ConfigId = p.ConfigId }).ToList()
|
||||
: [];
|
||||
vehicles.Add(new VehicleDto
|
||||
{
|
||||
ConfigId = configId,
|
||||
SuitId = world.VehicleSuitByConfig.GetValueOrDefault(configId),
|
||||
Parts = parts,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
var dir = DirectoryPath;
|
||||
System.IO.Directory.CreateDirectory(dir);
|
||||
var path = PathFor(pid);
|
||||
var tmp = path + ".tmp";
|
||||
File.WriteAllText(tmp, JsonSerializer.Serialize(new SaveFile { Vehicles = vehicles }, Options));
|
||||
File.Move(tmp, path, overwrite: true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[REPAIRSHOP] tuning save failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>进世界时把盘上的改装状态读回内存。开关关闭或文件不存在时什么也不做。</summary>
|
||||
internal static void Load(ulong pid, WorldEntryState world)
|
||||
{
|
||||
if (!PrivateServerConfigStore.Current.Gameplay.RepairShop.PersistTuning)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
SaveFile? file;
|
||||
var path = PathFor(pid);
|
||||
lock (Gate)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return;
|
||||
file = JsonSerializer.Deserialize<SaveFile>(File.ReadAllText(path));
|
||||
}
|
||||
if (file?.Vehicles is null)
|
||||
return;
|
||||
|
||||
lock (world.SyncRoot)
|
||||
{
|
||||
foreach (var v in file.Vehicles)
|
||||
{
|
||||
if (v.ConfigId == 0)
|
||||
continue;
|
||||
if (v.SuitId != 0)
|
||||
world.VehicleSuitByConfig[v.ConfigId] = v.SuitId;
|
||||
if (v.Parts is { Count: > 0 })
|
||||
world.VehiclePartsByConfig[v.ConfigId] =
|
||||
v.Parts.Where(p => p.ConfigId != 0).Select(p => (p.Type, p.ConfigId)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[REPAIRSHOP] tuning load failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SaveFile
|
||||
{
|
||||
public List<VehicleDto> Vehicles { get; set; } = [];
|
||||
}
|
||||
|
||||
private sealed class VehicleDto
|
||||
{
|
||||
public uint ConfigId { get; set; }
|
||||
public uint SuitId { get; set; }
|
||||
public List<PartDto> Parts { get; set; } = [];
|
||||
}
|
||||
|
||||
private sealed class PartDto
|
||||
{
|
||||
public uint Type { get; set; }
|
||||
public uint ConfigId { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user