feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
CI / Go Backend (push) Canceled after 0s

初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

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

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

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
xyou
2026-08-08 14:07:56 +08:00
parent 9500c4c80a
commit f70b061d1a
1972 changed files with 159760 additions and 6 deletions
@@ -0,0 +1,189 @@
using System.Globalization;
using System.IO;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Logging;
using PCL.Core.UI;
namespace PCL;
internal sealed class CrashDialogPresenter(CrashAnalysisContext context)
{
private readonly CrashReportExporter _exporter = new();
private readonly CrashResultFormatter _formatter = new();
public void Output(
bool isHandAnalyze,
List<string>? extraFiles)
{
ModMain.frmMain.ShowWindowToTop();
var crashContent = _formatter.Format(context, isHandAnalyze);
var resultText = crashContent.Text;
var directFile = context.DirectOpenFile;
var openInstanceSettings =
context.Instance is not null &&
crashContent.SuggestedAction == CrashSuggestedAction.OpenInstanceSettings;
var title = isHandAnalyze
? Lang.Text("Crash.Dialog.Title.Manual")
: Lang.Text("Crash.Dialog.Title.Auto");
var secondButtonText = _GetSecondButtonText(
isHandAnalyze,
directFile,
openInstanceSettings);
var thirdButtonText = isHandAnalyze
? ""
: Lang.Text("Crash.Dialog.Button.ExportReport");
var secondButtonAction = _GetSecondButtonAction(
isHandAnalyze,
directFile,
openInstanceSettings);
var selectedButton = MsgBoxWrapper.ShowWithCustomButtons(
resultText,
title,
MsgBoxTheme.Info,
true,
new MsgBoxButtonInfo(Lang.Text("Common.Action.Confirm"), 1),
new MsgBoxButtonInfo(secondButtonText, 2, secondButtonAction),
new MsgBoxButtonInfo(thirdButtonText, 3));
switch (selectedButton)
{
case 2:
if (openInstanceSettings)
_OpenModLoaderInstallPage();
else if (directFile is not null)
_OpenDirectFile(directFile);
break;
case 3:
_ExportReport(extraFiles);
break;
}
}
private static string _GetSecondButtonText(
bool isHandAnalyze,
CrashLogEntry? directFile,
bool openInstanceSettings)
{
if (isHandAnalyze || directFile is null)
return "";
return openInstanceSettings
? Lang.Text("Crash.Dialog.Button.GoToModify")
: Lang.Text("Crash.Dialog.Button.OpenLog");
}
private static Action? _GetSecondButtonAction(
bool isHandAnalyze,
CrashLogEntry? directFile,
bool openInstanceSettings)
{
if (isHandAnalyze ||
directFile is null ||
openInstanceSettings)
return null;
return () => _OpenDirectFile(directFile);
}
private void _OpenModLoaderInstallPage()
{
PageInstanceLeft.McInstance = context.Instance;
ModBase.RunInUi(() => ModMain.frmMain.PageChange(
FormMain.PageType.InstanceSetup,
FormMain.PageSubType.VersionInstall));
}
private static void _OpenDirectFile(CrashLogEntry directFile)
{
if (File.Exists(directFile.FullPath))
{
Basics.OpenPath(directFile.FullPath);
return;
}
var filePath = Path.Combine(Paths.Temp, "Crash.txt");
CrashFileIo.WriteText(filePath, string.Join("\r\n", directFile.Lines));
Basics.OpenPath(filePath);
}
private void _ExportReport(List<string>? extraFiles)
{
string? fileAddress = null;
try
{
fileAddress = _SelectReportSavePath();
if (string.IsNullOrEmpty(fileAddress))
return;
_exporter.Export(context, fileAddress, extraFiles);
HintWrapper.Show(
Lang.Text("Crash.Report.Export.Success"),
HintTheme.Success);
Basics.OpenPath(Path.GetDirectoryName(fileAddress) ?? fileAddress);
}
catch (Exception ex)
{
LogWrapper.Error(ex, "Crash", "导出错误报告失败");
var message = _CreateExportFailureMessage(fileAddress, ex);
MsgBoxWrapper.ShowWithCustomButtons(
message,
Lang.Text("Crash.Export.Failed.Title"),
MsgBoxTheme.Error,
false,
new MsgBoxButtonInfo(Lang.Text("Common.Action.Confirm"), 1),
new MsgBoxButtonInfo(
Lang.Text("Crash.Export.Failed.CopyDetails"),
2,
() => ModBase.ClipboardSet(message, false)));
}
}
private static string _CreateExportFailureMessage(
string? targetZipPath,
Exception exception)
{
var summary = string.IsNullOrWhiteSpace(targetZipPath)
? Lang.Text("Crash.Export.Failed.MessageWithoutPath")
: Lang.Text("Crash.Export.Failed.Message", targetZipPath);
return ExceptionDetails.Compose(summary, exception);
}
private static string? _SelectReportSavePath()
{
string? fileAddress = null;
ModBase.RunInUiWait(() => fileAddress = SystemDialogs.SelectSaveFile(
Lang.Text("Crash.Report.SaveDialog.Title"),
_GetDefaultReportFileName(),
Lang.Text("Crash.Report.SaveDialog.Filter")));
return fileAddress;
}
private static string _GetDefaultReportFileName()
{
var time = DateTime.Now
.ToString("G", CultureInfo.InvariantCulture)
.Replace("/", "-")
.Replace(":", ".")
.Replace(" ", "_");
return Lang.Text("Crash.Report.SaveDialog.DefaultFileName", time);
}
}
@@ -0,0 +1,38 @@
namespace PCL;
internal enum CrashSuggestedAction
{
None,
OpenInstanceSettings
}
internal sealed class CrashMessageSpec(
string reasonKey,
object?[] reasonArgs,
string? suggestionKey,
object?[] suggestionArgs,
bool appendReportHint,
bool appendHelpHint,
CrashSuggestedAction suggestedAction = CrashSuggestedAction.None)
{
public string ReasonKey { get; } = reasonKey;
public object?[] ReasonArgs { get; } = reasonArgs;
public string? SuggestionKey { get; } = suggestionKey;
public object?[] SuggestionArgs { get; } = suggestionArgs;
public bool AppendReportHint { get; } = appendReportHint;
public bool AppendHelpHint { get; } = appendHelpHint;
public CrashSuggestedAction SuggestedAction { get; } = suggestedAction;
}
internal sealed class CrashDialogContent(string text, CrashSuggestedAction suggestedAction)
{
public string Text { get; } = text;
public CrashSuggestedAction SuggestedAction { get; } = suggestedAction;
}
@@ -0,0 +1,525 @@
using PCL.Core.App.Localization;
using PCL.Core.Logging;
using PCL.Core.Utils;
using PCL.Core.Utils.Exts;
namespace PCL;
internal sealed class CrashResultFormatter
{
public CrashDialogContent Format(
CrashAnalysisContext context,
bool isHandAnalyze)
{
var result = context.Result ?? new CrashAnalysisResult();
if (!result.Any)
return _CreateUnknownContent(isHandAnalyze);
var specs = result.Findings
.Select(finding => _CreateMessageSpec(finding, context.LogAll))
.ToList();
var text = _JoinParagraphs(specs.Select(_Render));
if (!isHandAnalyze)
text = _AppendFollowUps(text, specs);
return new CrashDialogContent(
_NormalizeLineEndings(text).Trim('\r', '\n'),
specs[0].SuggestedAction);
}
private static CrashDialogContent _CreateUnknownContent(bool isHandAnalyze)
{
var spec = isHandAnalyze
? _Spec("Crash.Reason.Unknown.Manual")
: _Spec("Crash.Reason.Unknown.Auto", "Crash.Suggestion.ExportReport");
return new CrashDialogContent(
_NormalizeLineEndings(_Render(spec)).Trim('\r', '\n'),
CrashSuggestedAction.None);
}
private static string _AppendFollowUps(
string text,
IReadOnlyCollection<CrashMessageSpec> specs)
{
var followUps = new List<string>();
if (specs.Any(spec => spec.AppendReportHint))
followUps.Add(Lang.Text("Crash.Suggestion.ViewReport"));
if (specs.Any(spec => spec.AppendHelpHint))
followUps.Add(Lang.Text("Crash.Suggestion.ExportReport"));
var launcherOutdatedSuggestion = _GetLauncherOutdatedSuggestion();
if (!string.IsNullOrWhiteSpace(launcherOutdatedSuggestion))
followUps.Add(launcherOutdatedSuggestion);
return followUps.Count == 0
? text
: Lang.Text(
"Crash.Presentation.WithFollowUps",
text,
_JoinParagraphs(followUps));
}
private static string _Render(CrashMessageSpec spec)
{
var reason = Lang.Text(spec.ReasonKey, spec.ReasonArgs);
if (spec.SuggestionKey is null)
return Lang.Text("Crash.Presentation.ReasonOnly", reason);
return Lang.Text(
"Crash.Presentation.WithSuggestion",
reason,
Lang.Text(spec.SuggestionKey, spec.SuggestionArgs));
}
private static CrashMessageSpec _CreateMessageSpec(
CrashFinding finding,
string combinedLogText)
{
var additional = finding.Details.ToList();
switch (finding.Cause)
{
case CrashCause.ExtractedModFile:
return _Spec(
"Crash.Reason.ExtractedModFile",
"Crash.Suggestion.ExtractedModFile");
case CrashCause.OutOfMemory:
return _Spec(
"Crash.Reason.OutOfMemory",
"Crash.Suggestion.OutOfMemory",
help: true);
case CrashCause.UsingOpenJ9:
return _Spec(
"Crash.Reason.UsingOpenJ9",
"Crash.Suggestion.UsingOpenJ9");
case CrashCause.UsingJdk:
return _Spec(
"Crash.Reason.UsingJdk",
"Crash.Suggestion.UsingJdk");
case CrashCause.JavaTooNew:
return _Spec(
"Crash.Reason.JavaTooNew",
"Crash.Suggestion.JavaTooNew");
case CrashCause.JavaIncompatible:
return _Spec(
"Crash.Reason.JavaIncompatible",
"Crash.Suggestion.JavaIncompatible");
case CrashCause.InvalidModFileName:
return _Spec(
"Crash.Reason.InvalidModFileName",
"Crash.Suggestion.InvalidModFileName");
case CrashCause.MissingMixinBootstrap:
return _Spec(
"Crash.Reason.MissingMixinBootstrap",
"Crash.Suggestion.MissingMixinBootstrap");
case CrashCause.X86JavaMemoryLimit:
return Environment.Is64BitOperatingSystem
? _Spec(
"Crash.Reason.X86JavaMemoryLimit.OnX64Os",
"Crash.Suggestion.X86JavaMemoryLimit.OnX64Os")
: _Spec(
"Crash.Reason.X86JavaMemoryLimit.OnX86Os",
"Crash.Suggestion.X86JavaMemoryLimit.OnX86Os",
help: true);
case CrashCause.MissingDependencyOrWrongMcVersion:
return _FormatMissingDependency(additional);
case CrashCause.StackKeywordFound:
return additional.Count == 1
? _Spec(
"Crash.Reason.StackKeyword.Single",
"Crash.Suggestion.StackKeyword", _Args(additional[0]),
help: true)
: _Spec(
"Crash.Reason.StackKeyword.Multiple",
"Crash.Suggestion.StackKeyword",
_Args(_BulletList(additional)),
help: true);
case CrashCause.StackModNameFound or CrashCause.SuspectedModCrash:
return additional.Count == 1
? _Spec(
"Crash.Reason.SuspectedMod.Single",
"Crash.Suggestion.DisableThisMod",
_Args(additional[0]),
report: true,
help: true)
: _Spec(
"Crash.Reason.SuspectedMod.Multiple",
"Crash.Suggestion.DisableTheseMods",
_Args(_BulletList(additional)),
report: true,
help: true);
case CrashCause.ConfirmedModCrash:
return additional.Count == 1
? _Spec(
"Crash.Reason.ConfirmedMod.Single",
"Crash.Suggestion.DisableThisMod",
_Args(additional[0]),
report: true,
help: true)
: _Spec(
"Crash.Reason.ConfirmedMod.Multiple",
"Crash.Suggestion.DisableTheseMods",
_Args(_BulletList(additional)),
report: true,
help: true);
case CrashCause.ModMixinFailed:
if (additional.Count == 0)
return _Spec(
"Crash.Reason.ModMixinFailed.None",
"Crash.Suggestion.ModMixinFailed",
report: true,
help: true);
return additional.Count == 1
? _Spec(
"Crash.Reason.ModMixinFailed.Single",
"Crash.Suggestion.DisableThisMod",
_Args(additional[0]),
report: true,
help: true)
: _Spec(
"Crash.Reason.ModMixinFailed.Multiple",
"Crash.Suggestion.DisableTheseMods",
_Args(_BulletList(additional)),
report: true,
help: true);
case CrashCause.ModConfigCrash:
return additional.Count > 1 && additional[1] is not null
? _Spec(
"Crash.Reason.ModConfigCrash.WithConfig",
"Crash.Suggestion.ModConfigCrash",
_Args(additional[0], additional[1]))
: _Spec(
"Crash.Reason.ModConfigCrash.Simple",
"Crash.Suggestion.DisableThisMod",
_Args(additional.FirstOrDefault() ?? string.Empty),
report: true,
help: true);
case CrashCause.ModInitializationFailed:
return additional.Count == 1
? _Spec(
"Crash.Reason.ModInitializationFailed.Single",
"Crash.Suggestion.DisableThisMod",
_Args(additional[0]),
report: true,
help: true)
: _Spec(
"Crash.Reason.ModInitializationFailed.Multiple",
"Crash.Suggestion.DisableTheseMods",
_Args(_BulletList(additional)),
report: true,
help: true);
case CrashCause.SpecificBlockCrash:
return additional.Count == 1
? _Spec(
"Crash.Reason.SpecificBlock.Single",
"Crash.Suggestion.SpecificBlock.Single",
_Args(additional[0]),
help: true)
: _Spec(
"Crash.Reason.SpecificBlock.Multiple",
"Crash.Suggestion.SpecificBlock.Multiple",
help: true);
case CrashCause.DuplicateMods:
return additional.Count >= 2
? _Spec(
"Crash.Reason.DuplicateMods.Known",
"Crash.Suggestion.DuplicateMods",
_Args(_BulletList(additional)))
: _Spec(
"Crash.Reason.DuplicateMods.Unknown",
"Crash.Suggestion.DuplicateMods",
report: true,
help: true);
case CrashCause.SpecificEntityCrash:
return additional.Count == 1
? _Spec(
"Crash.Reason.SpecificEntity.Single",
"Crash.Suggestion.SpecificEntity.Single",
_Args(additional[0]),
help: true)
: _Spec(
"Crash.Reason.SpecificEntity.Multiple",
"Crash.Suggestion.SpecificEntity.Multiple",
help: true);
case CrashCause.OptiFineForgeIncompatible:
return _Spec(
"Crash.Reason.OptiFineForgeIncompatible",
"Crash.Suggestion.OptiFineForgeIncompatible");
case CrashCause.ShadersModWithOptiFine:
return _Spec(
"Crash.Reason.ShadersModWithOptiFine",
"Crash.Suggestion.ShadersModWithOptiFine");
case CrashCause.OldForgeNewJavaIncompatible:
return _Spec(
"Crash.Reason.OldForgeNewJavaIncompatible",
"Crash.Suggestion.OldForgeNewJavaIncompatible");
case CrashCause.MultipleForgeInInstanceJson:
return _Spec(
"Crash.Reason.MultipleForgeInInstanceJson",
"Crash.Suggestion.MultipleForgeInInstanceJson");
case CrashCause.ManualDebugCrash:
return _Spec("Crash.Reason.ManualDebugCrash");
case CrashCause.ModRequiresJava11:
return _Spec(
"Crash.Reason.ModRequiresJava11",
"Crash.Suggestion.ModRequiresJava11");
case CrashCause.VeryShortOutput:
return _Spec(
"Crash.Reason.VeryShortOutput",
"Crash.Suggestion.ExportReport",
_Args(additional.FirstOrDefault() ?? string.Empty));
case CrashCause.OptiFineWorldLoadCrash:
return _Spec(
"Crash.Reason.OptiFineWorldLoadCrash",
"Crash.Suggestion.OptiFineWorldLoadCrash",
help: true);
case CrashCause.PixelFormatNotSupported
or CrashCause.IntelDriverAccessViolation
or CrashCause.AmdDriverAccessViolation
or CrashCause.NvidiaDriverAccessViolation
or CrashCause.UnsupportedOpenGl:
return combinedLogText.Contains("hd graphics ")
? _Spec(
"Crash.Reason.GraphicsDriver.IntelOrIntegrated",
"Crash.Suggestion.GraphicsDriver.IntelOrIntegrated",
help: true)
: _Spec(
"Crash.Reason.GraphicsDriver.Generic",
"Crash.Suggestion.GraphicsDriver.Generic",
help: true);
case CrashCause.ResourcePackTooLarge:
return _Spec(
"Crash.Reason.ResourcePackTooLarge",
"Crash.Suggestion.ResourcePackTooLarge",
help: true);
case CrashCause.NightConfigBug:
return _Spec(
"Crash.Reason.NightConfigBug",
"Crash.Suggestion.NightConfigBug",
help: true);
case CrashCause.OpenGl1282:
return _Spec(
"Crash.Reason.OpenGl1282",
"Crash.Suggestion.OpenGl1282",
help: true);
case CrashCause.TooManyModsIdLimit:
return _Spec(
"Crash.Reason.TooManyModsIdLimit",
"Crash.Suggestion.TooManyModsIdLimit");
case CrashCause.FileOrContentValidationFailed:
return _Spec(
"Crash.Reason.FileOrContentValidationFailed",
"Crash.Suggestion.FileOrContentValidationFailed",
help: true);
case CrashCause.IncompleteForgeInstallation:
return _Spec(
"Crash.Reason.IncompleteForgeInstallation",
"Crash.Suggestion.IncompleteForgeInstallation",
help: true);
case CrashCause.FabricError:
return additional.Count == 1
? _Spec(
"Crash.Reason.FabricError.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(additional[0]))
: _Spec(
"Crash.Reason.FabricError.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
case CrashCause.IncompatibleMods:
return _FormatIncompatibleMods(additional);
case CrashCause.ModLoaderError:
return additional.Count == 1
? _Spec(
"Crash.Reason.ModLoaderError.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(additional[0]))
: _Spec(
"Crash.Reason.ModLoaderError.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
case CrashCause.FabricSolutionProvided:
return additional.Count == 1
? _Spec(
"Crash.Reason.FabricSolution.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(additional[0]))
: _Spec(
"Crash.Reason.FabricSolution.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
case CrashCause.ForgeError:
return additional.Count == 1
? _Spec(
"Crash.Reason.ForgeError.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(additional[0]))
: _Spec(
"Crash.Reason.ForgeError.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
case CrashCause.NoAnalyzableFile:
return _Spec(
"Crash.Reason.NoAnalyzableFile",
"Crash.Suggestion.ExportReport",
help: true);
default:
return _Spec(
"Crash.Reason.UnknownFinding",
"Crash.Suggestion.ExportReport",
_Args(finding.Cause.ToString()),
help: true);
}
}
private static CrashMessageSpec _FormatMissingDependency(List<string> additional)
{
if (additional.Count == 0)
return _Spec(
"Crash.Reason.MissingDependency.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
var info = _BulletList(additional);
return info.IsMatch(RegexPatterns.IncompatibleModLoaderErrorHint)
? _Spec(
"Crash.Reason.ModLoaderIncompatible",
"Crash.Suggestion.ModLoaderIncompatible",
_Args(info),
action: CrashSuggestedAction.OpenInstanceSettings)
: _Spec(
"Crash.Reason.MissingDependency.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(info));
}
private static CrashMessageSpec _FormatIncompatibleMods(List<string> additional)
{
if (additional.Count != 1)
return _Spec(
"Crash.Reason.IncompatibleMods.Generic",
"Crash.Suggestion.FollowLoaderInstructions",
help: true);
var info = additional[0];
return info.IsMatch(RegexPatterns.IncompatibleModLoaderErrorHint)
? _Spec(
"Crash.Reason.ModLoaderIncompatible",
"Crash.Suggestion.ModLoaderIncompatible",
_Args(info),
action: CrashSuggestedAction.OpenInstanceSettings)
: _Spec(
"Crash.Reason.IncompatibleMods.WithDetail",
"Crash.Suggestion.FollowLoaderInstructions",
_Args(info));
}
private static CrashMessageSpec _Spec(
string reasonKey,
string? suggestionKey = null,
object?[]? reasonArgs = null,
object?[]? suggestionArgs = null,
bool report = false,
bool help = false,
CrashSuggestedAction action = CrashSuggestedAction.None)
{
return new CrashMessageSpec(
reasonKey,
reasonArgs ?? [],
suggestionKey,
suggestionArgs ?? [],
report,
help,
action);
}
private static object?[] _Args(params object?[] args)
{
return args;
}
private static string _BulletList(IEnumerable<string> items)
{
return string.Join(
Environment.NewLine,
items.Select(item => Lang.Text("Crash.Presentation.ListItem", item)));
}
private static string _JoinParagraphs(IEnumerable<string> items)
{
return string.Join(
Environment.NewLine + Environment.NewLine,
items.Where(item => !string.IsNullOrWhiteSpace(item)));
}
private static string _NormalizeLineEndings(string text)
{
return text
.Replace("\r\n", "\r")
.Replace("\n", "\r")
.Replace("\r", "\r\n");
}
private static string? _GetLauncherOutdatedSuggestion()
{
try
{
return UpdateManager.GetVersionStatus() == UpdateEnums.VersionStatus.Latest
? null
: Lang.Text("Crash.Suggestion.LauncherOutdated");
}
catch (Exception ex)
{
LogWrapper.Error(ex, "Crash", "确认启动器更新失败");
return null;
}
}
}