效果展示:
代码:
using LuaFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Policy;
using UnityEditor;
using UnityEngine;
namespace LuaDevelopTool
{
public class ReloadLuaFilesWindow : EditorWindow
{
public static string LUA_PATH;
public static FileSystemWatcher watcher = null;
public static bool autoReload = false;
public static bool reloaded = false;
public static Dictionary<string, long> luaFiles = new Dictionary<string, long>();
[MenuItem("Lua/Reload Lua Window", false, 200)]
public static void ShowWindow()
{
GetWindow(typeof(ReloadLuaFilesWindow), false, "Lua热重载");
}
private void OnEnable()
{
if (watcher == null)
{
LUA_PATH = Application.dataPath + "/Starwars/Lua/"; //lua文件存放地点
watcher = new FileSystemWatcher();
watcher.Filter = "*.lua";
watcher.Path = LUA_PATH;
watcher.EnableRaisingEvents = true;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime | NotifyFilters.Size;
watcher.IncludeSubdirectories = true;
EditorApplication.playModeStateChanged += OnPlayerModeStateChanged;
}
}
private void OnDestroy()
{
watcher.Dispose();
watcher = null;
EditorApplication.playModeStateChanged -= OnPlayerModeStateChanged;
}
private static void OnPlayerModeStateChanged(PlayModeStateChange playModeState)
{
if (playModeState == PlayModeStateChange.EnteredPlayMode)
{
watcher.Changed += new FileSystemEventHandler(OnFileCreateOrChanged);
watcher.Created += new FileSystemEventHandler(OnFileCreateOrChanged);
}
if (playModeState == PlayModeStateChange.ExitingPlayMode)
{
watcher.Changed -= new FileSystemEventHandler(OnFileCreateOrChanged);
watcher.Created -= new FileSystemEventHandler(OnFileCreateOrChanged);
luaFiles.Clear();
reloaded = true;
}
}
private void OnGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button("Clear Lua Files"))
{
luaFiles.Clear();
reloaded = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Watcher State : " + (watcher == null ? "Not Running" : "Running"));
autoReload = GUILayout.Toggle(autoReload, "自动加载");
GUILayout.EndHorizontal();
EditorGUILayout.BeginFoldoutHeaderGroup(true, "Lua文件列表");
foreach (var item in luaFiles)
{
GUILayout.BeginHorizontal();
GUILayout.Label(item.Key);
if (GUILayout.Button("Reload"))
{
if (!EditorApplication.isPlaying)
{
Debug.LogError("运行后才可以使用");
}
else
{
luaFiles.Remove(item.Key);
ReloadLuaFile(item.Key);
}
}
GUILayout.EndHorizontal();
}
if (autoReload)
{
if (!reloaded)
{
foreach (var item in luaFiles)
{
ReloadLuaFile(item.Key);
}
luaFiles.Clear();
reloaded = true;
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
public static void ReloadLuaFile(string luaFile)
{
if (EditorApplication.isPlaying)
{
TestCode(luaFile);
}
}
public static void OnFileCreateOrChanged(object source, FileSystemEventArgs e)
{
reloaded = false;
var luaFile = e.FullPath.Replace("\\", "/").Replace(LUA_PATH, "");
if (luaFiles.ContainsKey(luaFile))
{
luaFiles[luaFile] = GetTimeStamp();
}
else
{
luaFiles.Add(luaFile, GetTimeStamp());
}
luaFiles = luaFiles.OrderByDescending(p => p.Value).ToDictionary(p => p.Key, p => p.Value);
}
public static long GetTimeStamp()
{
return (long)(System.DateTime.UtcNow - new System.DateTime(1970, 1, 1)).TotalSeconds;
}
[MenuItem("Lua/Reload Lua Window(TestCode)", false, 200)]
public static void TestCode(string url)
{
var packageName = url.Split(".").First();
LuaManager mgr = AppFacade.Instance.GetManager<LuaManager>(ManagerName.Lua);
mgr.DoString(LuaCode.HotFixLuaCode);
mgr.CallFunction("LuaDevelopToolReload", new object[] { packageName });
}
}
class LuaCode
{
public const string HotFixLuaCode = @"
function LuaDevelopToolReload(moduleName)
local loadedName = string.gsub(moduleName, '/', '.');
local oldModule = package.loaded[loadedName];
local tempNameArray = string.split(moduleName, ""/"")
local tempName = tempNameArray[#tempNameArray]
local isTrue = oldModule == true
if isTrue then
oldModule = _G[tempName]
end
package.loaded[loadedName] = nil;
local ok,err = pcall(require, loadedName)
if not ok then
package.loaded[loadedName] = oldModule;
error('reload error : ' .. moduleName .. '\n' .. tostring(err));
return;
end
local newModule = package.loaded[loadedName];
if newModule == true then
newModule = _G[tempName]
end
if oldModule == nil then
oldModule = newModule;
else
local reload_tables = {};
LuaDevelopToolUpdateTable(oldModule, newModule, reload_tables);
end
if isTrue then
_G[tempName] = oldModule
else
package.loaded[loadedName] = oldModule;
end
logError('reload completed : '.. moduleName);
end
function LuaDevelopToolUpdateTable(old_table, new_table, reload_tables)
if type(old_table) ~= 'table' then
logError('LuaDevelopToolUpdateTable Error, old_table is '.. type(old_table));
return;
end
if type(new_table) ~= 'table' then
logError('LuaDevelopToolUpdateTable Error, new_table is '.. type(new_table));
return;
end
for key,new_value in pairs(new_table) do
if old_table[key] == nil then
old_table[key] = new_value;
else
local new_type = type(new_value);
if new_type == 'table' then
if reload_tables[new_value] == nil then
reload_tables[new_value] = true;
LuaDevelopToolUpdateTable(old_table[key], new_value, reload_tables);
end
elseif new_type == 'function' then
UpdateFunc(old_table[key], new_value ,key);
old_table[key] = new_value;
end
end
end
UpdateMetatable(old_table, new_table, reload_tables)
end
function UpdateMetatable(old_table, new_table, reload_tables)
if type(old_table) ~= 'table' then
logError('UpdateMetatable Error, old_table is '.. type(old_table));
return;
end
if type(new_table) ~= 'table' then
logError('UpdateMetatable Error, new_table is '.. type(new_table));
return;
end
local old_mt = debug.getmetatable(old_table);
local new_mt = debug.getmetatable(new_table);
if type(old_mt) == 'table' and type(new_mt) == 'table' then
LuaDevelopToolUpdateTable(old_mt, new_mt, reload_tables);
end
end
function UpdateFunc(old_func, new_func, key)
if type(old_func) ~= 'function' then
logError('UpdateFunc Error, old_func : ' .. key .. ' '.. type(old_func));
return;
end
if type(new_func) ~= 'function' then
logError('UpdateFunc Error, new_func : ' .. key .. ' '.. type(new_func));
return;
end
local old_upvalue = {}
for i = 1, 99999 do
local name, value = debug.getupvalue(old_func, i)
if not name then
break;
end
old_upvalue[name] = value;
end
for i = 1, 99999 do
local name, value = debug.getupvalue(new_func, i)
if not name then
break;
end
local old_value = old_upvalue[name];
if old_value ~= nil then
--logError('setupvalue ' .. name)
debug.setupvalue(new_func, i, old_value);
end
end
end
";
}
}
上面的代码有使用LuaFramework中的LuaManager的代码:
学习路径:【Lua】更新运行时代码 - 知乎