初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace PCL.Core.Minecraft.Saves.Editing;
|
||||
|
||||
/// <summary>
|
||||
/// 可编辑值的标记联合 —— 表示"不修改"或"修改为某值"。
|
||||
/// 值类型实现,避免在 <see cref="Editing.SaveChanges"/> 中产生堆分配。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值的类型(必须为值类型)。</typeparam>
|
||||
public readonly record struct Editable<T> where T : struct
|
||||
{
|
||||
private readonly T _value;
|
||||
private readonly bool _hasValue;
|
||||
|
||||
/// <summary>创建一个"修改为指定值"的实例。</summary>
|
||||
public Editable(T value)
|
||||
{
|
||||
_value = value;
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
/// <summary>是否显式设置了新值。</summary>
|
||||
public bool HasValue => _hasValue;
|
||||
|
||||
/// <summary>新值。如果 <see cref="HasValue"/> 为 false,则抛出异常。</summary>
|
||||
public T Value => _hasValue ? _value : throw new InvalidOperationException("Editable 没有值。");
|
||||
|
||||
/// <summary>有值则返回新值,否则返回 <paramref name="defaultValue"/>。</summary>
|
||||
public T GetValueOrDefault(T defaultValue) => _hasValue ? _value : defaultValue;
|
||||
|
||||
/// <summary>尝试获取新值。</summary>
|
||||
public bool TryGetValue(out T value)
|
||||
{
|
||||
value = _value;
|
||||
return _hasValue;
|
||||
}
|
||||
|
||||
/// <summary>返回值的字符串表示,无值时返回 "<unspecified>"。</summary>
|
||||
public override string ToString() => _hasValue ? _value!.ToString() ?? "" : "<unspecified>";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using fNbt;
|
||||
|
||||
namespace PCL.Core.Minecraft.Saves.Editing;
|
||||
|
||||
/// <summary>
|
||||
/// 存档编辑器接口 —— 负责将修改写入 level.dat 的 Data 复合标签(内存操作)。
|
||||
/// 实现类需声明自己支持的 DataVersion 范围。
|
||||
/// </summary>
|
||||
public interface ISaveEditor
|
||||
{
|
||||
/// <summary>返回此编辑器能否处理指定 DataVersion 的存档。</summary>
|
||||
bool CanHandle(int? dataVersion);
|
||||
|
||||
/// <summary>
|
||||
/// 将 <paramref name="changes"/> 中的修改写入 <paramref name="data"/> 复合标签。
|
||||
/// 返回 true 表示至少有一项修改被成功写入。
|
||||
/// </summary>
|
||||
bool ApplyChanges(NbtCompound data, SaveChanges changes);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using fNbt;
|
||||
|
||||
namespace PCL.Core.Minecraft.Saves.Editing.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// 26.1 之前的存档编辑器(含整个 1.x 版本体系)。
|
||||
/// 仅操作内存中的 NbtCompound,文件 IO 由 <see cref="SaveManager"/> 统一处理。
|
||||
/// </summary>
|
||||
internal sealed class Pre261SaveEditor : ISaveEditor
|
||||
{
|
||||
public bool CanHandle(int? dataVersion)
|
||||
=> dataVersion is null || dataVersion < DataVersionBoundaries._261snapshot6;
|
||||
|
||||
public bool ApplyChanges(NbtCompound data, SaveChanges changes)
|
||||
{
|
||||
if (changes.IsEmpty)
|
||||
return false;
|
||||
|
||||
var changed = false;
|
||||
changed |= WriteAllowCommands(data, changes);
|
||||
changed |= WriteDifficulty(data, changes);
|
||||
changed |= WriteDifficultyLocked(data, changes);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>写入 Data.allowCommands(字节型:0/1)。仅当该字段原本存在时才写入,避免向 pre-1.3.1 存档添加新字段。</summary>
|
||||
internal static bool WriteAllowCommands(NbtCompound data, SaveChanges changes)
|
||||
{
|
||||
if (!changes.AllowCommands.HasValue || !data.Contains("allowCommands"))
|
||||
return false;
|
||||
data["allowCommands"] = new NbtByte("allowCommands", (byte)(changes.AllowCommands.Value ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>写入 Data.Difficulty(字节型:0=和平, 1=简单, 2=普通, 3=困难)。仅当该字段原本存在时才写入。</summary>
|
||||
internal static bool WriteDifficulty(NbtCompound data, SaveChanges changes)
|
||||
{
|
||||
if (!changes.Difficulty.HasValue || !data.Contains("Difficulty"))
|
||||
return false;
|
||||
data["Difficulty"] = new NbtByte("Difficulty", (byte)changes.Difficulty.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>写入 Data.DifficultyLocked(字节型:0/1)。仅当该字段原本存在时才写入。</summary>
|
||||
internal static bool WriteDifficultyLocked(NbtCompound data, SaveChanges changes)
|
||||
{
|
||||
if (!changes.LockDifficulty.HasValue || !data.Contains("DifficultyLocked"))
|
||||
return false;
|
||||
data["DifficultyLocked"] = new NbtByte("DifficultyLocked", (byte)(changes.LockDifficulty.Value ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using fNbt;
|
||||
|
||||
namespace PCL.Core.Minecraft.Saves.Editing.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// 26.1-snapshot-6 及之后的存档编辑器(2026 新版本号体系)。
|
||||
/// 仅操作内存中的 NbtCompound,文件 IO 由 <see cref="SaveManager"/> 统一处理。
|
||||
/// </summary>
|
||||
internal sealed class Version261PlusSaveEditor : ISaveEditor
|
||||
{
|
||||
public bool CanHandle(int? dataVersion)
|
||||
=> dataVersion >= DataVersionBoundaries._261snapshot6;
|
||||
|
||||
public bool ApplyChanges(NbtCompound data, SaveChanges changes)
|
||||
{
|
||||
if (changes.IsEmpty)
|
||||
return false;
|
||||
|
||||
// 确保 difficulty_settings 复合标签存在
|
||||
if (!data.TryGet<NbtCompound>("difficulty_settings", out var ds) || ds is null)
|
||||
{
|
||||
ds = new NbtCompound("difficulty_settings");
|
||||
data.Add(ds);
|
||||
}
|
||||
|
||||
var changed = false;
|
||||
changed |= Pre261SaveEditor.WriteAllowCommands(data, changes);
|
||||
changed |= WriteDifficulty(ds!, changes);
|
||||
changed |= WriteLocked(ds!, changes);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>写入 difficulty_settings.difficulty(字符串型)。</summary>
|
||||
internal static bool WriteDifficulty(NbtCompound difficultySettings, SaveChanges changes)
|
||||
{
|
||||
if (!changes.Difficulty.HasValue)
|
||||
return false;
|
||||
var val = changes.Difficulty.Value switch
|
||||
{
|
||||
Difficulty.Peaceful => "peaceful",
|
||||
Difficulty.Easy => "easy",
|
||||
Difficulty.Normal => "normal",
|
||||
Difficulty.Hard => "hard",
|
||||
_ => "normal",
|
||||
};
|
||||
difficultySettings["difficulty"] = new NbtString("difficulty", val);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>写入 difficulty_settings.locked(字节型:0/1)。</summary>
|
||||
internal static bool WriteLocked(NbtCompound difficultySettings, SaveChanges changes)
|
||||
{
|
||||
if (!changes.LockDifficulty.HasValue)
|
||||
return false;
|
||||
difficultySettings["locked"] = new NbtByte("locked", (byte)(changes.LockDifficulty.Value ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace PCL.Core.Minecraft.Saves.Editing;
|
||||
|
||||
/// <summary>
|
||||
/// 用户希望对存档应用的修改。使用 <c>default</c> 表示"无任何修改"。
|
||||
/// 仅当 <see cref="Editable{T}.HasValue"/> 为 true 的字段才会被写入。
|
||||
/// </summary>
|
||||
public record struct SaveChanges
|
||||
{
|
||||
/// <summary>是否允许作弊命令的修改。</summary>
|
||||
public Editable<bool> AllowCommands { get; set; }
|
||||
|
||||
/// <summary>游戏难度的修改。</summary>
|
||||
public Editable<Difficulty> Difficulty { get; set; }
|
||||
|
||||
/// <summary>是否锁定难度的修改。</summary>
|
||||
public Editable<bool> LockDifficulty { get; set; }
|
||||
|
||||
/// <summary>此结构体是否不包含任何待写入的修改。</summary>
|
||||
public bool IsEmpty => !AllowCommands.HasValue && !Difficulty.HasValue && !LockDifficulty.HasValue;
|
||||
}
|
||||
Reference in New Issue
Block a user