Files
Ananta-ps/Ananta.Server/Ananta.Handlers/Game/AutonomousDriveSpeedSettings.cs

84 lines
3.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.Json;
using Ananta.Server.Configuration;
namespace Ananta.Server.Handlers.Game;
/// <summary>
/// 普通自动驾驶巡航速度的<b>独立</b>设置(2026-09-30)。
///
/// ★★ 为什么单独一个 JSON 而不写进 <c>private-server.json</c>:
/// ① GM 面板的 <c>/api/settings</c> 是<b>整份回写</b>,两个写者容易互相覆盖;
/// ② 主配置改完要走 <c>/api/reload-config</c>,而调速希望「保存即可用」;
/// ③ 回滚只要删这一个文件,不动任何别的开关。
/// ⇒ 本类<b>绝不</b>改写 <c>private-server.json</c>。
///
/// 单位:对外(GM / JSON)统一 <b>km/h</b>;服务端下发的 Cruise 参数是 <b>m/s</b>,
/// 换算在 <c>GameRouter.VehicleNav.cs</c> 的 <c>EffectiveCruiseSpeedMs</c> 里做,别在这里除。
/// 默认 43.2 km/h = 12 m/s,与 <c>gameplay.vehicleNavigation.cruiseSpeed</c> 的默认值一致。
/// </summary>
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;
/// <summary>读取当前速度(km/h)。文件不存在 = 用默认值。</summary>
internal static float Read()
{
lock (Gate)
{
if (!File.Exists(FilePath)) return DefaultKmh;
try
{
var value = JsonSerializer.Deserialize<Settings>(
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 的速度。");
}
}
/// <summary>保存速度(km/h),原子写(临时文件 + move),返回实际落盘值。</summary>
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;
}
}