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,168 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace PCL.Core.Utils.Diagnostics;
public static class StackHelper
{
private const string Unknown = "<unknown>";
// 返回“直接调用者”的可读名称(例如 Namespace.Type.Method(paramTypes)
// includeNamespace: 是否包含命名空间
// includeParameters: 是否包含参数类型列表
// skipAppFrames: 额外跳过的应用层帧数量(例如你自己的日志包装器)
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetDirectCallerName(
bool includeNamespace = true,
bool includeParameters = false,
int skipAppFrames = 0)
{
var st = new StackTrace(skipFrames: 1, fNeedFileInfo: false);
var frame = st.GetFrame(skipAppFrames);
var method = frame?.GetMethod();
if (method is null) return Unknown;
method = _TryMapAsyncOrIterator(method);
return _FormatMethod(method, includeNamespace, includeParameters);
}
// 获取前 maxFrames 层调用栈,返回格式化后的每一帧字符串
// needFileInfo=true 时会解析 PDB 以拿到文件名与行号(昂贵)
[MethodImpl(MethodImplOptions.NoInlining)]
public static IReadOnlyList<string> GetStack(
int maxFrames = 10,
bool includeNamespace = true,
bool includeParameters = false,
bool needFileInfo = false)
{
if (maxFrames <= 0) return [];
var st = new StackTrace(skipFrames: 1, fNeedFileInfo: needFileInfo);
var list = new List<string>(capacity: Math.Min(maxFrames, st.FrameCount));
for (int i = 0, added = 0; i < st.FrameCount && added < maxFrames; i++)
{
var method = st.GetFrame(i)?.GetMethod();
if (method is null) continue;
method = _TryMapAsyncOrIterator(method);
var sig = _FormatMethod(method, includeNamespace, includeParameters);
if (needFileInfo)
{
var f = st.GetFrame(i);
var file = f?.GetFileName();
var line = f?.GetFileLineNumber() ?? 0;
if (!string.IsNullOrEmpty(file) && line > 0)
{
sig = $"{sig} ({System.IO.Path.GetFileName(file)}:{line})";
}
}
list.Add(sig);
added++;
}
return list;
}
// 将 async/iterator 的 MoveNext 映射回原始方法名(尽力而为的启发式)
private static MethodBase _TryMapAsyncOrIterator(MethodBase method)
{
if (method.Name != "MoveNext") return method;
var dt = method.DeclaringType;
if (dt is null) return method;
// 典型生成类型名:<MethodName>d__12 或 <MethodName>g__Local|12_0
var name = dt.Name;
var lt = name.IndexOf('<');
var gt = name.IndexOf('>');
if (lt >= 0 && gt > lt + 1)
{
var originalName = name.Substring(lt + 1, gt - lt - 1);
// 在声明类型的外层类型里查找同名方法(可能存在重载,选第一个匹配)
var parent = dt.DeclaringType ?? dt; // 迭代器常为嵌套到原类型里
foreach (var m in parent.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
if (string.Equals(m.Name, originalName, StringComparison.Ordinal))
return m;
}
}
return method;
}
private static string _FormatMethod(MethodBase method, bool includeNamespace, bool includeParameters, bool includeParameterNamespace = false)
{
var sb = new StringBuilder();
var type = method.DeclaringType;
if (type is not null)
{
sb.Append(_FormatTypeName(type, includeNamespace));
sb.Append('.');
}
sb.Append(method.Name);
if (method is MethodInfo { IsGenericMethod: true } mi)
{
sb.Append('[');
var args = mi.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(args[i], false));
}
sb.Append(']');
}
if (includeParameters)
{
var ps = method.GetParameters();
sb.Append('(');
for (var i = 0; i < ps.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(ps[i].ParameterType, includeParameterNamespace));
if (!string.IsNullOrEmpty(ps[i].Name))
{
sb.Append(' ').Append(ps[i].Name);
}
}
sb.Append(')');
}
return sb.ToString();
}
private static string _FormatTypeName(Type t, bool includeNamespace)
{
if (t.IsGenericType)
{
var def = t.GetGenericTypeDefinition();
var name = def.Name;
var tick = name.IndexOf('`');
if (tick >= 0) name = name[..tick];
var ns = includeNamespace ? (def.Namespace is null ? "" : def.Namespace + ".") : "";
var sb = new StringBuilder(ns).Append(name).Append('[');
var args = t.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(args[i], false));
}
sb.Append(']');
return sb.ToString();
}
return includeNamespace && t.Namespace is not null
? t.Namespace + "." + t.Name
: t.Name;
}
}