Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

1063 lines
46 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.Globalization;
using System.IO;
using System.IO.Compression;
using System.Text.RegularExpressions;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Utils;
namespace PCL;
public class McInstance
{
/// <summary>
/// 显示的描述文本。
/// </summary>
public string Desc = Lang.Text("Select.Instance.Description.NotLoaded");
/// <summary>
/// 强制实例分类,0 为未启用,1 为隐藏,2 及以上为其他普通分类。
/// </summary>
public McInstanceCardType displayType = McInstanceCardType.Auto;
public bool IsLoaded;
/// <summary>
/// 是否已初始化从 JAR 中读取 version.json。
/// </summary>
private bool _jsonVersionInited;
/// <summary>
/// 是否为收藏的实例。
/// </summary>
public bool IsStar;
/// <summary>
/// 显示的实例图标。
/// </summary>
public string Logo;
/// <summary>
/// 实例的发布时间。
/// </summary>
public DateTime releaseTime = new(1970, 1, 1, 15, 0, 0);
/// <summary>
/// 该实例的列表检查原始结果,不受自定义影响。
/// </summary>
public McInstanceState state = McInstanceState.Error;
/// <summary></summary>
/// <param name="name">实例名,或实例文件夹的完整路径(不规定是否以 \ 结尾)。</param>
public McInstance(string name)
{
PathInstance = (name.Contains(":") ? name : Path.Combine(ModFolder.mcFolderSelected, "versions", name)) + (name.EndsWithF(@"\") ? "" : @"\");
}
/// <summary>
/// 该实例的实例文件夹,以“\”结尾。
/// </summary>
public string PathInstance { get; }
/// <summary>
/// 应用版本隔离后,该实例所对应的 Minecraft 根文件夹,以“\”结尾。
/// </summary>
public string PathIndie
{
get
{
if (Config.Instance.IndieV2Config.IsDefault(PathInstance))
{
if (!IsLoaded)
Load();
// 决定该实例是否应该被隔离
bool ShouldBeIndie()
{
// 从老的实例独立设置中迁移:-1 未决定,0 使用全局设置,1 手动开启,2 手动关闭
if (!Config.Instance.IndieV1Config.IsDefault(PathInstance) && Config.Instance.IndieV1[PathInstance] > 0)
{
ModBase.Log($"[Minecraft] 版本隔离初始化({Name}):从老的实例独立设置中迁移");
return Config.Instance.IndieV1[PathInstance] == 1;
}
// 若实例文件夹下包含 mods 或 saves 文件夹,则自动开启版本隔离
var modFolder = new DirectoryInfo(PathInstance + @"mods\");
var saveFolder = new DirectoryInfo(PathInstance + @"saves\");
if ((modFolder.Exists && modFolder.EnumerateFiles().Any()) ||
(saveFolder.Exists && saveFolder.EnumerateDirectories().Any()))
{
ModBase.Log($"[Minecraft] 版本隔离初始化({Name}):实例文件夹下存在 mods 或 saves 文件夹,自动开启");
return true;
}
// 根据全局的默认设置决定是否隔离
var isRelease = state != McInstanceState.Fool && state != McInstanceState.Old &&
state != McInstanceState.Snapshot;
ModBase.Log(
$"[Minecraft] 版本隔离初始化({Name}):从全局默认设置中({Config.Launch.IndieSolutionV2})判断,State {ModBase.GetStringFromEnum(state)}IsRelease {isRelease}Modable {Modable}");
return Config.Launch.IndieSolutionV2 switch
{
0 => false, // 关闭
1 => Info.HasLabyMod || Modable, // 仅隔离可安装 Mod 的实例
2 => !isRelease, // 仅隔离非正式版
3 => Info.HasLabyMod || Modable || !isRelease, // 隔离非正式版与可安装 Mod 的实例
_ => true // 隔离所有实例
};
}
Config.Instance.IndieV2[PathInstance] = ShouldBeIndie();
}
return Config.Instance.IndieV2[PathInstance] ? PathInstance : ModFolder.mcFolderSelected;
}
}
/// <summary>
/// 该实例的实例文件夹名称。
/// </summary>
public string Name
{
get
{
if (field is null && !string.IsNullOrEmpty(PathInstance))
field = ModBase.GetFolderNameFromPath(PathInstance);
return field;
}
}
/// <summary>
/// 该实例是否可以安装 Mod。
/// </summary>
public bool Modable
{
get
{
if (!IsLoaded)
Load();
return Info.HasFabric || Info.HasLegacyFabric || Info.HasQuilt || Info.HasForge || Info.HasLiteLoader ||
Info.HasNeoForge || Info.HasCleanroom || displayType == McInstanceCardType.API; // #223
}
}
/// <summary>
/// 实例信息。
/// </summary>
public McInstanceInfo Info
{
get
{
if (field is not null)
return field;
field = new McInstanceInfo();
#region 获取游戏版本
try
{
// 获取发布时间并判断是否为老版本
try
{
if (JsonObject["releaseTime"] is null)
releaseTime = new DateTime(1970, 1, 1, 15, 0, 0); // 未知版本也可能显示为 1970 年
else
releaseTime = JsonObject["releaseTime"].ToObject<DateTime>();
if (releaseTime.Year > 2000 && releaseTime.Year < 2013)
{
field.VanillaName = "Old";
goto VersionSearchFinish;
}
}
catch
{
releaseTime = new DateTime(1970, 1, 1, 15, 0, 0);
}
// 实验性快照
if ((string)(JsonObject["type"] ?? "") == "pending")
{
field.VanillaName = "pending";
goto VersionSearchFinish;
}
// 从 PCL 下载的版本信息中获取版本号
if (JsonObject["clientVersion"] is not null)
{
field.VanillaName = (string)JsonObject["clientVersion"];
goto VersionSearchFinish;
}
// 从 HMCL 下载的版本信息中获取版本号
if (JsonObject["patches"] is not null)
foreach (var patchNode in JsonObject["patches"].AsArray()) { var patch = patchNode.AsObject();
if ((patch["id"] ?? "").ToString() == "game" && patch["version"] is not null)
{
field.VanillaName = patch["version"].ToString();
goto VersionSearchFinish;
} }
// 从 Forge / NeoForge / LabyMod Arguments 中获取版本号
if (JsonObject["arguments"] is not null)
{
if (JsonObject["arguments"]["game"] is not null)
{
var mark = false;
foreach (var Argument in JsonObject["arguments"]["game"].AsArray())
{
if (mark)
{
field.VanillaName = Argument.ToString();
goto VersionSearchFinish;
}
if (Argument.ToString() == "--fml.mcVersion")
mark = true;
}
}
if (JsonObject["arguments"]["jvm"] is not null)
foreach (var Argument in JsonObject["arguments"]["jvm"].AsArray())
{
var regexArgument = Argument.ToString().RegexSeek(RegexPatterns.LabyModVersion);
if (regexArgument is not null)
{
field.VanillaName = regexArgument;
goto VersionSearchFinish;
}
}
}
// 从继承实例中获取版本号
if (!string.IsNullOrEmpty(InheritInstanceName))
{
field.VanillaName = (JsonObject["jar"] ?? "").ToString(); // LiteLoader 优先使用 Jar
if (string.IsNullOrEmpty(field.VanillaName))
field.VanillaName = InheritInstanceName;
goto VersionSearchFinish;
}
// 从下载地址中获取版本号
var regex = (JsonObject["downloads"] ?? "").ToString()
.RegexSeek(RegexPatterns.MinecraftDownloadUrlVersion);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 从 Forge 版本中获取版本号
var librariesString = JsonObject["libraries"].ToString();
regex = librariesString.RegexSeek(RegexPatterns.ForgeLibVersion);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 从 OptiFine 版本中获取版本号
regex = librariesString.RegexSeek(RegexPatterns.OptiFineLibVersion);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 从 Fabric / Quilt / Legacy Fabric 版本中获取版本号
regex = librariesString.RegexSeek(RegexPatterns.FabricLikeLibVersion);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 从 jar 项中获取版本号
if (JsonObject["jar"] is not null)
{
field.VanillaName = JsonObject["jar"].ToString();
goto VersionSearchFinish;
}
// 从 jar 文件的 version.json 中获取版本号
if (JsonVersion?["name"] is not null)
{
var jsonVerName = JsonVersion["name"].ToString();
if (jsonVerName.Length < 32) // 因为 wiki 说这玩意儿可能是个 hash,虽然我没发现
{
field.VanillaName = jsonVerName;
ModBase.Log("[Minecraft] 从版本 jar 中的 version.json 获取到版本号:" + jsonVerName);
goto VersionSearchFinish;
}
}
// 从 JSON 的 ID 中获取
regex = ((string)JsonObject["id"]).RegexSeek(RegexPatterns.MinecraftJsonVersion,
RegexOptions.IgnoreCase);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 非准确的版本判断警告
ModBase.Log("[Minecraft] 无法完全确认 MC 版本号的版本:" + Name);
field.Reliable = false;
// 从文件夹名中获取
regex = Name.RegexSeek(RegexPatterns.MinecraftJsonVersion, RegexOptions.IgnoreCase);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 从 JSON 出现的版本号中获取
var jsonRaw = (JsonObject)JsonObject.DeepClone();
jsonRaw.Remove("libraries");
var jsonRawText = jsonRaw.ToString();
regex = jsonRawText.RegexSeek(RegexPatterns.MinecraftJsonVersion, RegexOptions.IgnoreCase);
if (regex is not null)
{
field.VanillaName = regex;
goto VersionSearchFinish;
}
// 无法获取
field.VanillaName = "Unknown";
Desc = Lang.Text("Select.Instance.Description.UnknownMcVersion");
}
catch (Exception ex)
{
ModBase.Log(ex, "识别 Minecraft 版本时出错");
field.VanillaName = "Unknown";
Desc = Lang.Text("Minecraft.Error.Unrecognizable", ex.Message);
}
#endregion
VersionSearchFinish: ;
if (field.VanillaName.StartsWithF("20.") || field.VanillaName.StartsWithF("21."))
{
field.VanillaName = "1." + field.VanillaName;
}
field.VanillaName = field.VanillaName.Replace("_unobfuscated", "").Replace(" Unobfuscated", "");
// 获取版本号
if (field.VanillaName.StartsWithF("1."))
{
var segments = field.VanillaName.Split(" _-.".ToCharArray());
field.vanilla = new Version((int)Math.Round(ModBase.Val(segments.Count() >= 2 ? segments[1] : "0")),
0, (int)Math.Round(ModBase.Val(segments.Count() >= 3 ? segments[2] : "0")));
}
else if (field.VanillaName.RegexCheck(@"^[2-9][0-9]\."))
{
var segments = field.VanillaName.Split(" _-.".ToCharArray());
field.vanilla = new Version((int)Math.Round(ModBase.Val(segments[0])),
(int)Math.Round(ModBase.Val(segments.Count() >= 2 ? segments[1] : "0")),
(int)Math.Round(ModBase.Val(segments.Count() >= 3 ? segments[2] : "0")));
}
else
{
field.vanilla = new Version(9999, 0, 0);
}
return field;
}
set { field = value; }
}
/// <summary>
/// 该实例的 JSON 文本。
/// </summary>
public string JsonText
{
get
{
// 快速检查 JSON 是否以 { 开头、} 结尾;忽略空白字符
bool FastJsonCheck(string json)
{
var trimedJson = json.Trim();
return trimedJson.StartsWithF("{") && trimedJson.EndsWithF("}");
}
;
if (field is null)
{
var jsonPath = PathInstance + Name + ".json";
if (!File.Exists(jsonPath))
{
// 如果文件夹下只有一个 JSON 文件,则将其作为实例 JSON
var jsonFiles = Directory.GetFiles(PathInstance, "*.json");
if (jsonFiles.Count() == 1)
{
jsonPath = jsonFiles[0];
ModBase.Log("[Minecraft] 未找到同名实例 JSON,自动换用 " + jsonPath, ModBase.LogLevel.Debug);
}
else
{
throw new Exception(Lang.Text("Minecraft.Error.InstanceJsonNotFound",
$"{PathInstance}{Name}.json"));
}
}
field = ModBase.ReadFile(jsonPath);
// 如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
if (!FastJsonCheck(field))
{
if (ModBase.RunInUi())
{
ModBase.Log($"[Minecraft] 实例 JSON 文件为空或有误,将进行短暂重试({jsonPath}", ModBase.LogLevel.Debug);
Thread.Sleep(200);
field = ModBase.ReadFile(jsonPath);
}
else
{
ModBase.Log($"[Minecraft] 实例 JSON 文件为空或有误,将在 2s 后重试读取({jsonPath}", ModBase.LogLevel.Debug);
Thread.Sleep(2000);
field = ModBase.ReadFile(jsonPath);
}
if (!FastJsonCheck(field))
ModBase.GetJson(field);
}
}
return field;
}
set => field = value;
}
/// <summary>
/// 该实例的 JSON 对象。
/// 若 JSON 存在问题,在获取该属性时即会抛出异常。
/// </summary>
public JsonObject JsonObject
{
get
{
if (field is null)
{
var text = JsonText; // 触发 JsonText 的 Get 事件
try
{
field = (JsonObject)ModBase.GetJson(text);
// 转换 HMCL 关键项
if (field.ContainsKey("patches") && !field.ContainsKey("time"))
{
IsHmclFormatJson = true;
// 合并 JSON
// Dim HasOptiFine As Boolean = False, HasForge As Boolean = False
JsonObject currentObject = null;
var subjsonList = new List<JsonObject>();
foreach (var SubjsonNode in field["patches"].AsArray()) { var subjson = SubjsonNode.AsObject();
subjsonList.Add(subjson); }
subjsonList.Sort((left, right) =>
{
var leftVal = ModBase.Val((left["priority"] ?? "0").ToString());
var rightVal = ModBase.Val((right["priority"] ?? "0").ToString());
return leftVal.CompareTo(rightVal);
});
foreach (var Subjson in subjsonList)
{
var id = (string)Subjson["id"];
if (id is not null)
{
// 合并 JSON
ModBase.Log("[Minecraft] 合并 HMCL 分支项:" + id);
if (currentObject is not null)
currentObject.Merge(Subjson);
else
currentObject = Subjson;
}
else
{
ModBase.Log("[Minecraft] 存在为空的 HMCL 分支项");
}
}
field = currentObject;
// 修改附加项
field["id"] = Name;
if (field.ContainsKey("inheritsFrom"))
field.Remove("inheritsFrom");
}
// 与继承实例合并
object inheritInstanceName = null;
do
{
try
{
inheritInstanceName = field["inheritsFrom"] is null
? ""
: field["inheritsFrom"].ToString();
if (Equals(inheritInstanceName, Name))
{
ModBase.Log("[Minecraft] 自引用的继承实例:" + Name, ModBase.LogLevel.Debug);
inheritInstanceName = "";
break;
}
Recheck: ;
if (!Equals(inheritInstanceName, ""))
{
var inheritInstance = new McInstance(inheritInstanceName?.ToString() ?? "");
// 继续循环
if (Equals(inheritInstance.InheritInstanceName,
inheritInstanceName))
throw new Exception(Lang.Text("Minecraft.Error.DependencyRecursion",
inheritInstanceName));
inheritInstanceName = inheritInstance.InheritInstanceName;
// 合并
inheritInstance.JsonObject.Merge(field);
field = inheritInstance.JsonObject;
goto Recheck;
}
}
catch (Exception ex)
{
ModBase.Log(ex, "合并实例依赖项 JSON 失败(" + (inheritInstanceName ?? "null") + "");
}
} while (false);
}
catch (Exception ex)
{
throw new Exception(Lang.Text("Minecraft.Error.InitInstanceJsonFailed", Name ?? "null"), ex);
}
}
return field;
}
set => field = value;
}
/// <summary>
/// 是否为旧版 JSON 格式。
/// </summary>
public bool IsOldJson => JsonObject["minecraftArguments"] is not null &&
(string)JsonObject["minecraftArguments"] != "";
/// <summary>
/// JSON 是否为 HMCL 格式。
/// </summary>
public bool IsHmclFormatJson { get; set; }
/// <summary>
/// 实例 JAR 中的 version.json 文件对象。
/// 若没有则返回 Nothing。
/// </summary>
public JsonObject JsonVersion
{
get
{
if (!_jsonVersionInited)
{
_jsonVersionInited = true;
do
{
try
{
if (!File.Exists(PathInstance + Name + ".jar"))
break;
using (var jarArchive = new ZipArchive(new FileStream(PathInstance + Name + ".jar",
FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
var versionJson = jarArchive.GetEntry("version.json");
if (versionJson is not null)
using (var versionJsonStream = new StreamReader(versionJson.Open()))
{
field = (JsonObject)ModBase.GetJson(versionJsonStream.ReadToEnd());
}
}
}
catch (Exception ex)
{
ModBase.Log(ex, $"从实例 JAR 中读取 version.json 失败 ({PathInstance}{Name}.jar)");
}
} while (false);
}
return field;
}
}
/// <summary>
/// 该实例的依赖实例。若无依赖实例则为空字符串。
/// </summary>
public string InheritInstanceName
{
get
{
if (field is null)
{
field = (JsonObject["inheritsFrom"] ?? "").ToString();
// 由于过老的 LiteLoader 中没有 Inherits(例如 1.5.2),需要手动判断以获取真实继承实例
// 此外,由于这里的加载早于实例种类判断,所以需要手动判断是否为 LiteLoader
// 如果实例提供了不同的 JAR,代表所需的 JAR 可能已被更改,则跳过 Inherit 替换
if (JsonText.Contains("liteloader") && (Info.VanillaName ?? "") != (Name ?? "") &&
!JsonText.Contains("logging"))
if (((JsonObject["jar"] ?? Info.VanillaName).ToString() ?? "") == (Info.VanillaName ?? ""))
field = Info.VanillaName;
// HMCL 实例无 JSON
if (IsHmclFormatJson)
field = "";
}
return field;
}
}
/// <summary>
/// 检查 Minecraft 版本,若检查通过 State 则为 Original 且返回 True。
/// </summary>
public bool Check()
{
// 检查文件夹
if (!Directory.Exists(PathInstance))
{
state = McInstanceState.Error;
Desc = Lang.Text("Select.Instance.Description.NotFound", Name);
return false;
}
// 检查权限
try
{
Directory.CreateDirectory(PathInstance + @"PCL\");
ModBase.CheckPermissionWithException(PathInstance + @"PCL\");
}
catch (Exception ex)
{
state = McInstanceState.Error;
Desc = Lang.Text("Select.Instance.Description.NoPermission");
ModBase.Log(ex, "没有访问实例文件夹的权限");
return false;
}
// 确认 JSON 可用性
try
{
var jsonObjCheck = JsonObject;
}
catch (Exception ex)
{
ModBase.Log(ex, "实例 JSON 可用性检查失败(" + PathInstance + "");
JsonText = "";
JsonObject = null;
Desc = ex.Message;
state = McInstanceState.Error;
return false;
}
// 检查版本号获取
try
{
if (string.IsNullOrEmpty(Info.VanillaName))
throw new Exception(Lang.Text("Minecraft.Error.VersionNumberEmpty"));
}
catch (Exception ex)
{
ModBase.Log(ex, "版本号获取失败(" + Name + "");
state = McInstanceState.Error;
Desc = Lang.Text("Minecraft.Error.VersionNumberFetchFailed", ex);
return false;
}
// 检查依赖实例
try
{
if (!string.IsNullOrEmpty(InheritInstanceName))
if (!File.Exists(Path.Combine(ModBase.GetPathFromFullPath(PathInstance), InheritInstanceName, InheritInstanceName + ".json")))
{
state = McInstanceState.Error;
Desc = Lang.Text("Select.Instance.Description.NeedInherit", InheritInstanceName);
return false;
}
}
catch (Exception ex)
{
ModBase.Log(ex, "依赖实例检查出错(" + Name + "");
state = McInstanceState.Error;
Desc = Lang.Text("Select.Instance.Description.UnknownError") + ": " + ex;
return false;
}
state = McInstanceState.Original;
return true;
}
/// <summary>
/// 加载 Minecraft 实例的详细信息。不使用其缓存,且会更新缓存。
/// </summary>
public McInstance Load()
{
try
{
// 检查实例,若出错则跳过数据确定阶段
if (!Check())
goto ExitDataLoad;
#region 确定实例分类
switch (Info.VanillaName ?? "") // 在获取 Version.Original 对象时会完成它的加载
{
case "Unknown":
{
state = McInstanceState.Error;
break;
}
case "Old":
{
state = McInstanceState.Old; // 根据 API 进行筛选
break;
}
default:
{
var realJson = JsonObject is not null ? JsonObject.ToString() : JsonText;
// 愚人节与快照版本
if ((JsonObject["type"] ?? "").ToString() == "fool" ||
!string.IsNullOrEmpty(McVersionClassifier.GetMcFoolName(Info.VanillaName)))
state = McInstanceState.Fool;
else if (IsSnapshot()) state = McInstanceState.Snapshot;
// OptiFine
if (realJson.Contains("optifine"))
{
state = McInstanceState.OptiFine;
Info.HasOptiFine = true;
Info.OptiFine = realJson.RegexSeek(RegexPatterns.OptiFineVersion) ??
Lang.Text("Minecraft.Version.Unknown");
}
// LiteLoader
if (realJson.Contains("liteloader"))
{
state = McInstanceState.LiteLoader;
Info.HasLiteLoader = true;
}
// Fabric、Forge、Quilt、LabyMod、Legacy Fabric
if (realJson.Contains("labymod_data"))
{
state = McInstanceState.LabyMod;
Info.HasLabyMod = true;
Info.LabyMod = (string)JsonObject["labymod_data"]["version"];
}
else if (realJson.Contains("net.legacyfabric:intermediary"))
{
state = McInstanceState.LegacyFabric;
Info.HasLegacyFabric = true;
Info.LegacyFabric =
(realJson.RegexSeek(RegexPatterns.LegacyFabricVersion) ??
Lang.Text("Minecraft.Version.Unknown"))
.Replace("+build", "");
}
else if (realJson.Contains("net.fabricmc:fabric-loader"))
{
state = McInstanceState.Fabric;
Info.HasFabric = true;
Info.Fabric =
(realJson.RegexSeek(RegexPatterns.FabricVersion) ??
Lang.Text("Minecraft.Version.Unknown")).Replace("+build", "");
}
else if (realJson.Contains("org.quiltmc:quilt-loader"))
{
state = McInstanceState.Quilt;
Info.HasQuilt = true;
Info.Quilt =
(realJson.RegexSeek(RegexPatterns.QuiltVersion) ??
Lang.Text("Minecraft.Version.Unknown")).Replace("+build", "");
}
else if (realJson.Contains("com.cleanroommc:cleanroom:"))
{
state = McInstanceState.Cleanroom;
Info.HasCleanroom = true;
Info.Cleanroom =
(realJson.RegexSeek(RegexPatterns.CleanroomVersion) ??
Lang.Text("Minecraft.Version.Unknown")).Replace("+build", "");
}
else if (realJson.Contains("minecraftforge") && !realJson.Contains("net.neoforge"))
{
state = McInstanceState.Forge;
Info.HasForge = true;
Info.Forge = realJson.RegexSeek(RegexPatterns.ForgeMainVersion) ??
realJson.RegexSeek(RegexPatterns.ForgeLibVersion) ??
Lang.Text("Minecraft.Version.Unknown");
}
else if (realJson.Contains("net.neoforge"))
{
// 1.20.1 JSON 范例:"--fml.forgeVersion", "47.1.99"
// 1.20.2+ JSON 范例:"--fml.neoForgeVersion", "20.6.119-beta"
state = McInstanceState.NeoForge;
Info.HasNeoForge = true;
Info.NeoForge = realJson.RegexSeek(RegexPatterns.NeoForgeVersion) ??
Lang.Text("Minecraft.Version.Unknown");
}
break;
}
}
#endregion
ExitDataLoad: ;
// 确定实例图标
Logo = States.Instance.LogoPath[PathInstance];
if (string.IsNullOrEmpty(Logo) || !States.Instance.IsLogoCustom[PathInstance])
switch (state)
{
case McInstanceState.Original:
{
Logo = ModBase.pathImage + "Blocks/Grass.png";
break;
}
case McInstanceState.Snapshot:
{
Logo = ModBase.pathImage + "Blocks/CommandBlock.png";
break;
}
case McInstanceState.Old:
{
Logo = ModBase.pathImage + "Blocks/CobbleStone.png";
break;
}
case McInstanceState.Forge:
{
Logo = ModBase.pathImage + "Blocks/Anvil.png";
break;
}
case McInstanceState.NeoForge:
{
Logo = ModBase.pathImage + "Blocks/NeoForge.png";
break;
}
case McInstanceState.Cleanroom:
{
Logo = ModBase.pathImage + "Blocks/Cleanroom.png";
break;
}
case McInstanceState.Fabric:
{
Logo = ModBase.pathImage + "Blocks/Fabric.png";
break;
}
case McInstanceState.LegacyFabric:
{
Logo = ModBase.pathImage + "Blocks/Fabric.png";
break;
}
case McInstanceState.Quilt:
{
Logo = ModBase.pathImage + "Blocks/Quilt.png";
break;
}
case McInstanceState.OptiFine:
{
Logo = ModBase.pathImage + "Blocks/GrassPath.png";
break;
}
case McInstanceState.LiteLoader:
{
Logo = ModBase.pathImage + "Blocks/Egg.png";
break;
}
case McInstanceState.Fool:
{
Logo = ModBase.pathImage + "Blocks/GoldBlock.png";
break;
}
case McInstanceState.LabyMod:
{
Logo = ModBase.pathImage + "Blocks/LabyMod.png";
break;
}
default:
{
Logo = ModBase.pathImage + "Blocks/RedstoneBlock.png";
break;
}
}
// 确定实例描述
if (state == McInstanceState.Error)
{
Desc = Desc;
}
else
{
Desc = States.Instance.CustomInfo[PathInstance];
if ((Desc ?? "") == (GetDefaultDescription() ?? ""))
Desc = "";
}
// 确定实例收藏状态
IsStar = States.Instance.Starred[PathInstance];
// 确定实例显示种类
displayType = (McInstanceCardType)States.Instance.CardType[PathInstance];
// 写入缓存
if (Directory.Exists(PathInstance))
{
States.Instance.State[PathInstance] = (int)state;
States.Instance.Info[PathInstance] = Desc;
States.Instance.LogoPath[PathInstance] = Logo;
}
if (state != McInstanceState.Error)
{
States.Instance.ReleaseTime[PathInstance] = releaseTime.ToString("yyyy'-'MM'-'dd HH':'mm", CultureInfo.InvariantCulture);
States.Instance.FabricVersion[PathInstance] = Info.Fabric;
States.Instance.LegacyFabricVersion[PathInstance] = Info.LegacyFabric;
States.Instance.QuiltVersion[PathInstance] = Info.Quilt;
States.Instance.LabyModVersion[PathInstance] = Info.LabyMod;
States.Instance.OptiFineVersion[PathInstance] = Info.OptiFine;
States.Instance.HasLiteLoader[PathInstance] = Info.HasLiteLoader;
States.Instance.ForgeVersion[PathInstance] = Info.Forge;
States.Instance.NeoForgeVersion[PathInstance] = Info.NeoForge;
States.Instance.CleanroomVersion[PathInstance] = Info.Cleanroom;
States.Instance.VanillaVersionName[PathInstance] = Info.VanillaName;
States.Instance.VanillaVersion[PathInstance] = Info.vanilla.ToString();
}
}
catch (Exception ex)
{
Desc = Lang.Text("Select.Instance.Description.UnknownError") + ": " + ex;
Logo = ModBase.pathImage + "Blocks/RedstoneBlock.png";
state = McInstanceState.Error;
ModBase.Log(
ex,
Lang.Text("Select.Instance.Error.Load", Name),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Instance.Error.Load", Name));
}
finally
{
IsLoaded = true;
}
return this;
}
private bool IsSnapshot()
{
return new[] { "w", "snapshot", "rc", "pre", "experimental", "-" }.Any(s =>
Info.VanillaName.ContainsF(s, true)) || Name.ContainsF("combat", true) ||
(JsonObject["type"] ?? "").ToString() == "snapshot" ||
(JsonObject["type"] ?? "").ToString() == "pending";
}
/// <summary>
/// 获取实例的默认描述。
/// </summary>
public string GetDefaultDescription()
{
// Mod Loader 信息
var modLoaderInfo = "";
if (this.Info.HasForge)
modLoaderInfo += ", Forge" + (this.Info.Forge == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.Forge);
if (this.Info.HasNeoForge)
modLoaderInfo += ", NeoForge" + (this.Info.NeoForge == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.NeoForge);
if (this.Info.HasCleanroom)
modLoaderInfo += ", Cleanroom" + (this.Info.Cleanroom == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.Cleanroom);
if (this.Info.HasLabyMod)
modLoaderInfo += ", LabyMod" + (this.Info.LabyMod == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.LabyMod);
if (this.Info.HasFabric)
modLoaderInfo += ", Fabric" + (this.Info.Fabric == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.Fabric);
if (this.Info.HasQuilt)
modLoaderInfo += ", Quilt" + (this.Info.Quilt == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.Quilt);
if (this.Info.HasLegacyFabric)
modLoaderInfo += ", Legacy Fabric" +
(this.Info.LegacyFabric == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.LegacyFabric);
if (this.Info.HasOptiFine)
modLoaderInfo += ", OptiFine" + (this.Info.OptiFine == Lang.Text("Minecraft.Version.Unknown")
? ""
: " " + this.Info.OptiFine.Replace("-", " ").Replace("_", " "));
if (this.Info.HasLiteLoader)
modLoaderInfo += ", LiteLoader";
// 基础信息
string info;
switch (state)
{
case McInstanceState.Snapshot:
case McInstanceState.Original:
case McInstanceState.Forge:
case McInstanceState.NeoForge:
case McInstanceState.Fabric:
case McInstanceState.OptiFine:
case McInstanceState.LiteLoader:
{
if (this.Info.VanillaName.ContainsF("pre", true))
info = Lang.Text("Select.Instance.Description.PreRelease", this.Info.VanillaName);
else if (this.Info.VanillaName.ContainsF("rc", true))
info = Lang.Text("Select.Instance.Description.ReleaseCandidate", this.Info.VanillaName);
else if (this.Info.VanillaName.Contains("experimental"))
info = Lang.Text("Select.Instance.Description.ExperimentalSnapshot", this.Info.VanillaName);
else if (this.Info.VanillaName == "pending")
info = Lang.Text("Select.Instance.Description.ExperimentalSnapshot.Pending");
else if (IsSnapshot())
info = this.Info.Reliable ? Lang.Text("Select.Instance.Description.Snapshot", this.Info.VanillaName.Replace("-snapshot", "")) : Lang.Text("Select.Instance.Description.Snapshot.Unknown");
else
info = this.Info.Reliable ? Lang.Text("Select.Instance.Description.Release", this.Info.VanillaName) : Lang.Text("Select.Instance.Description.Release.Unknown");
break;
}
case McInstanceState.Old:
{
info = Lang.Text("Select.Instance.Description.Old");
break;
}
case McInstanceState.Fool:
{
info = Lang.Text("Select.Instance.Description.AprilFools", this.Info.VanillaName);
break;
}
case McInstanceState.Error:
{
return Desc; // 已有错误信息
}
default:
{
return Lang.Text("Select.Instance.Description.ReportUnknownError");
}
}
return (info + modLoaderInfo).Replace("_", "-");
}
// 运算符支持
public override bool Equals(object obj)
{
var instance = obj as McInstance;
return instance is not null && (PathInstance ?? "") == (instance.PathInstance ?? "");
}
public static bool operator ==(McInstance? a, McInstance? b)
{
if (a is null && b is null)
return true;
if (a is null || b is null)
return false;
return (a.PathInstance ?? "") == (b.PathInstance ?? "");
}
public static bool operator !=(McInstance a, McInstance b)
{
return !(a == b);
}
}