Unity中Lua更新运行时代码

效果展示:

代码:

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】更新运行时代码 - 知乎

Lua热更新原理(1) - require机制 – asqbtcupid – gameplayer

【Lua运行时热重载①】检测Lua文件发生变化 - 知乎

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值