---@class HotfixCodeGenHandler 热更层代码生成器 local HotfixCodeGenHandler = {} --- 执行生成热更层代码 ---@param handler CS.FairyEditor.PublishHandler ---@param codeGenConfig CodeGenConfig function HotfixCodeGenHandler.Do(handler, codeGenConfig) local codePkgName = handler:ToFilename(handler.pkg.name); --convert chinese to pinyin, remove special chars etc. --- 从自定义配置中读取路径和命名空间 local exportCodePath = codeGenConfig.HotfixCodeOutPutPath .. '/' .. codePkgName local namespaceName = codeGenConfig.HotfixNameSpace --- 初始化自定义组件名前缀 local classNamePrefix = codeGenConfig.ClassNamePrefix --- 初始化自定义成员变量名前缀 local memberVarNamePrefix = codeGenConfig.MemerVarNamePrefix --- 从FGUI编辑器中读取配置 ---@type CS.FairyEditor.GlobalPublishSettings.CodeGenerationConfig local settings = handler.project:GetSettings("Publish").codeGeneration local getMemberByName = settings.getMemberByName --- 所有将要导出的类(当前包的所有设置为导出的组件,以及当前包所有被引用的组件) ---@type CS.FairyEditor.PublishHandler.ClassInfo[] local classes = handler:CollectClasses(codeGenConfig.CodeStrip, codeGenConfig.CodeStrip, nil) handler:SetupCodeFolder(exportCodePath, "cs") --check if target folder exists, and delete old files local classCnt = classes.Count local writer = CodeWriter.new() for i = 0, classCnt - 1 do local classInfo = classes[i] local members = classInfo.members writer:reset() writer:writeln('using FairyGUI;') writer:writeln('using Cysharp.Threading.Tasks;') writer:writeln() writer:writeln('namespace %s', namespaceName) writer:startBlock() --- 组装自定义组件前缀 local className = classNamePrefix .. classInfo.className -- 1 writer:writeln([[public class %sAwakeSystem : AwakeSystem<%s, GObject> { public override void Awake(%s self, GObject go) { self.Awake(go); } } ]], className, className, className) writer:writeln([[public class %sDestroySystem : DestroySystem<%s> { public override void Destroy(%s self) { self.Destroy(); } } ]], className, className, className) writer:writeln([[public sealed class %s : FUI { public const string UIPackageName = "%s"; public const string UIResName = "%s"; /// /// {uiResName}的组件类型(GComponent、GButton、GProcessBar等),它们都是GObject的子类。 /// public %s self; ]], className, codePkgName, classInfo.resName, classInfo.superClassName) local memberCnt = members.Count -- 是否为自定义类型组件标记数组 local customComponentFlagsArray = {} -- 是否为跨包组件标记数组 local crossPackageFlagsArray = {} for j = 0, memberCnt - 1 do local memberInfo = members[j] customComponentFlagsArray[j] = false crossPackageFlagsArray[j] = false -- 判断是不是我们自定义类型组件 local typeName = memberInfo.type for k = 0, classCnt - 1 do if typeName == classes[k].className then typeName = classNamePrefix .. classes[k].className customComponentFlagsArray[j] = true break end end -- 判断是不是跨包类型组件 if memberInfo.res ~= nil then --- 组装自定义组件前缀 typeName = classNamePrefix .. memberInfo.res.name crossPackageFlagsArray[j] = true end --- 组装自定义成员前缀 writer:writeln('\tpublic %s %s;', typeName, memberVarNamePrefix .. memberInfo.varName) end writer:writeln('\tpublic const string URL = "ui://%s%s";', handler.pkg.id, classInfo.resId) writer:writeln() writer:writeln([[ private static GObject CreateGObject() { return UIPackage.CreateObject(UIPackageName, UIResName); } private static void CreateGObjectAsync(UIPackage.CreateObjectCallback result) { UIPackage.CreateObjectAsync(UIPackageName, UIResName, result); } ]]) writer:writeln([[ public static %s CreateInstance(Entity parent) { return parent.AddChild<%s, GObject>(CreateGObject()); } ]], className, className) writer:writeln([[ public static UniTask<%s> CreateInstanceAsync(Entity parent) { UniTaskCompletionSource<%s> tcs = new UniTaskCompletionSource<%s>(); CreateGObjectAsync((go) => { tcs.TrySetResult(parent.AddChild<%s, GObject>(go)); }); return tcs.Task; } ]], className, className, className, className) writer:writeln([[ /// /// 仅用于go已经实例化情况下的创建(例如另一个组件引用了此组件) /// /// /// /// public static %s Create(Entity parent, GObject go) { return parent.AddChild<%s, GObject>(go); } ]], className, className) writer:writeln([[ /// /// 通过此方法获取的FUI,在Dispose时不会释放GObject,需要自行管理(一般在配合FGUI的Pool机制时使用)。 /// public static %s GetFormPool(Entity domain, GObject go) { var fui = go.Get<%s>(); if(fui == null) { fui = Create(domain, go); } fui.isFromFGUIPool = true; return fui; } ]], className, className) writer:writeln([[ public void Awake(GObject go) { if(go == null) { return; } GObject = go; if (string.IsNullOrWhiteSpace(Name)) { Name = Id.ToString(); } self = (%s)go; self.Add(this); var com = go.asCom; if(com != null) { ]], classInfo.superClassName) for j = 0, memberCnt - 1 do local memberInfo = members[j] --- 组装自定义成员前缀 local memberVarName = memberVarNamePrefix .. memberInfo.varName if memberInfo.group == 0 then if getMemberByName then if customComponentFlagsArray[j] then --- 组装自定义组件前缀 writer:writeln('\t\t\t%s = %s.Create(this, com.GetChild("%s"));', memberVarName, classNamePrefix .. memberInfo.type, memberInfo.name) elseif crossPackageFlagsArray[j] then --- 组装自定义组件前缀 writer:writeln('\t\t\t%s = %s.Create(this, com.GetChild("%s"));', memberVarName, classNamePrefix .. memberInfo.res.name, memberInfo.name) else writer:writeln('\t\t\t%s = (%s)com.GetChild("%s");', memberVarName, memberInfo.type, memberInfo.name) end else if customComponentFlagsArray[j] then --- 组装自定义组件前缀 writer:writeln('\t\t\t%s = %s.Create(this, com.GetChildAt(%s));', memberVarName, classNamePrefix .. memberInfo.type, memberInfo.index) elseif crossPackageFlagsArray[j] then --- 组装自定义组件前缀 writer:writeln('\t\t\t%s = %s.Create(this, com.GetChildAt(%s));', memberVarName, classNamePrefix .. memberInfo.res.name, memberInfo.index) else writer:writeln('\t\t\t%s = (%s)com.GetChildAt(%s);', memberVarName, memberInfo.type, memberInfo.index) end end elseif memberInfo.group == 1 then if getMemberByName then writer:writeln('\t\t\t%s = com.GetController("%s");', memberVarName, memberInfo.name) else writer:writeln('\t\t\t%s = com.GetControllerAt(%s);', memberVarName, memberInfo.index) end else if getMemberByName then writer:writeln('\t\t\t%s = com.GetTransition("%s");', memberVarName, memberInfo.name) else writer:writeln('\t\t\t%s = com.GetTransitionAt(%s);', memberVarName, memberInfo.index) end end end writer:writeln('\t\t}') writer:writeln('\t}') writer:writeln([[ public override void Destroy() { base.Destroy(); self.Remove(); self = null; ]]) for j = 0, memberCnt - 1 do local memberInfo = members[j] --- 组装自定义成员前缀 local memberVarName = memberVarNamePrefix .. memberInfo.varName if memberInfo.group == 0 then if customComponentFlagsArray[j] or crossPackageFlagsArray[j] then writer:writeln('\t\t%s.Dispose();', memberVarName) end writer:writeln('\t\t%s = null;', memberVarName) elseif memberInfo.group == 1 then writer:writeln('\t\t%s = null;', memberVarName) else writer:writeln('\t\t%s = null;', memberVarName) end end writer:writeln('\t}') writer:writeln('}') writer:endBlock() writer:save(exportCodePath .. '/' .. className .. '.cs') end -- 写入fuipackage writer:reset() writer:writeln('namespace %s', namespaceName) writer:startBlock() writer:writeln('public static partial class FUIPackage') writer:startBlock() writer:writeln('public const string %s = "%s";', codePkgName, codePkgName) for i = 0, classCnt - 1 do local classInfo = classes[i] writer:writeln('public const string %s_%s = "ui://%s/%s";', codePkgName, classInfo.resName, codePkgName, classInfo.resName) end writer:endBlock() --class writer:endBlock() --namespace local binderPackageName = 'Package' .. codePkgName writer:save(exportCodePath .. '/' .. binderPackageName .. '.cs') end return HotfixCodeGenHandler