using System.Text.Json;
using Ananta.Server.Configuration;
namespace Ananta.Server.Handlers.Game;
///
/// 普通自动驾驶巡航速度的独立设置(2026-09-30)。
///
/// ★★ 为什么单独一个 JSON 而不写进 private-server.json:
/// ① GM 面板的 /api/settings 是整份回写,两个写者容易互相覆盖;
/// ② 主配置改完要走 /api/reload-config,而调速希望「保存即可用」;
/// ③ 回滚只要删这一个文件,不动任何别的开关。
/// ⇒ 本类绝不改写 private-server.json。
///
/// 单位:对外(GM / JSON)统一 km/h;服务端下发的 Cruise 参数是 m/s,
/// 换算在 GameRouter.VehicleNav.cs 的 EffectiveCruiseSpeedMs 里做,别在这里除。
/// 默认 43.2 km/h = 12 m/s,与 gameplay.vehicleNavigation.cruiseSpeed 的默认值一致。
///
internal static class AutonomousDriveSpeedSettings
{
internal const float DefaultKmh = 43.2f, MinKmh = 10f, MaxKmh = 120f;
private static readonly object Gate = new();
// ⛑ 用最朴素的可序列化形状(internal 类 + public 无参构造 + 可写属性),
// 不用私有嵌套 record:后者要靠参数化构造绑定,出了问题是运行时才炸。
internal sealed class Settings
{
public float SpeedKmh { get; set; } = DefaultKmh;
}
internal static string FilePath =>
Path.Combine(PrivateServerConfigStore.ProjectRoot, "config", "autonomous-driving.json");
internal static bool Valid(float kmh) => float.IsFinite(kmh) && kmh >= MinKmh && kmh <= MaxKmh;
/// 读取当前速度(km/h)。文件不存在 = 用默认值。
internal static float Read()
{
lock (Gate)
{
if (!File.Exists(FilePath)) return DefaultKmh;
try
{
var value = JsonSerializer.Deserialize(
File.ReadAllText(FilePath),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (value is not null && Valid(value.SpeedKmh)) return value.SpeedKmh;
}
catch (JsonException)
{
// 落到下面统一抛:让 GM 显示「请重新保存」而不是静默用默认值。
}
throw new InvalidDataException(
$"自动驾驶速度配置无效({FilePath}),请通过 GM 保存 {MinKmh:0.#}–{MaxKmh:0.#} km/h 的速度。");
}
}
/// 保存速度(km/h),原子写(临时文件 + move),返回实际落盘值。
internal static float Save(float kmh)
{
if (!Valid(kmh))
throw new ArgumentOutOfRangeException(nameof(kmh), $"速度必须为 {MinKmh:0.#}–{MaxKmh:0.#} km/h。");
kmh = MathF.Round(kmh, 1);
lock (Gate)
{
var path = FilePath;
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
var temp = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
try
{
File.WriteAllText(temp, JsonSerializer.Serialize(new Settings { SpeedKmh = kmh },
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }));
File.Move(temp, path, overwrite: true);
}
finally
{
if (File.Exists(temp)) File.Delete(temp);
}
}
return kmh;
}
}