zoukankan      html  css  js  c++  java
  • 解决单个虚幻脚本虚拟机处理多个LUA状态机

    前几回初步实现了在LUA层中新建UClass,让C++自动调用Lua函数,但因为虚幻是单个脚本状态机,不方便处理多个LUA状态机,本文尝试初步解决该问题。
    参考UnLua插件后暂时搁置在LUA中新建UClass的方式,改用Hook的形式修改先修的UClass里面的函数实现C++自动调用LUA函数。
    这里的hook仅仅实在脚本层面的API拦截,并不在C++层面;通过修改UFunction的NativeFunc实现调用LUA函数。

    让单个虚幻脚本虚拟机处理多个LUA状态机,需要一个TMap记录每个GameInstance关联的LUA状态机,这个在调用LUA函数时,
    根据当前UObject所属的GameInstance去调用对应的LUA状态机;这对于所有Actor类型自然毫无问题,但对于某些与GameInstance无关的类就没法处理,
    比如UEngine类,后续再想想更合适方式。

    首先HOOk就是用一个LUA表里面的函数代替虚幻脚本函数,实现如下:

    int FastLuaHelper::HookUFunction(lua_State* InL)
    {
    	static bool bIsBindToReset = false;
    	if (bIsBindToReset == false)
    	{
    		FastLuaUnrealWrapper::OnLuaUnrealReset.AddLambda([InL](lua_State* InLua)
    			{
                                    if(InL != InLUA) return;
    				UnhookAllUFunction(InLua);
    			});
    
    		bIsBindToReset = true;
    	}
    
    	luaL_newmetatable(InL, "HookUE");
    	int32 HookTableIndex = lua_gettop(InL);
    
    	UClass* Cls = FLuaObjectWrapper::FetchObject(InL, 1, false)->GetClass();
    	FString ClsName = Cls->GetName();
    	const char* ClsName_C = TCHAR_TO_UTF8(*ClsName);
    	{
    		lua_getglobal(InL, "require");
    		lua_pushstring(InL, ClsName_C);
    		int32 status = lua_pcall(InL, 1, 1, 0);  /* call 'require(name)' */
    		if (status != LUA_OK)
    		{
    			FString ErrStr = UTF8_TO_TCHAR(lua_tostring(InL, -1));
    			UE_LOG(LogTemp, Warning, TEXT("%s"), *ErrStr);
    		}
    	}
    
    	if (!lua_istable(InL, -1))
    	{
    		lua_pushnil(InL);
    		return 1;
    	}
    
    	lua_pushnil(InL);
    	while (lua_next(InL, -2))
    	{
    		FString FuncName = lua_tostring(InL, -2);
    		UFunction* FoundFunc = Cls->FindFunctionByName(*FuncName, EIncludeSuperFlag::ExcludeSuper);
    		if (FoundFunc)
    		{
    			FoundFunc->FunctionFlags |= FUNC_Native;
    			FoundFunc->SetNativeFunc(FastLuaHelper::CallLuaFunction);
    		}
    		else
    		{
                            //不要修改父类的方法,这并不是期望的结果;修改当前对象所属类的方法即可
    			UFunction* FoundSuperFunc = Cls->FindFunctionByName(*FuncName, EIncludeSuperFlag::IncludeSuper);
    			if (FoundSuperFunc)
    			{
    				FoundFunc = Cast<UFunction>(StaticDuplicateObject(FoundSuperFunc, Cls, *FuncName, RF_Public | RF_Transient));
    				FoundFunc->FunctionFlags |= FUNC_Native;
    				FoundFunc->SetNativeFunc(FastLuaHelper::CallLuaFunction);
    				Cls->AddFunctionToFunctionMap(FoundFunc, *FuncName);
    				FoundFunc->Bind();
    				FoundFunc->StaticLink(true);
    			}
    		}
    
    		if (FoundFunc)
    		{
                            //记录到HookUE表格中,方便结束时恢复原状,这里直接使用UFunction指针关联LUA函数;
                            //相比UnLua插件的方式,在调用LUA时可略微减少查表次数
    			lua_rawsetp(InL, HookTableIndex, FoundFunc);
    		}
    		else
    		{
    			lua_pop(InL, 1);
    		}
    	}
    
    	lua_pushboolean(InL, 1);
    	return 1;
    }
    //重启LUA时解除Hook,否则重启LUA后。UClass里面任然是上次hook的LUA函数
    static void UnhookAllUFunction(lua_State* InL)
    {
    	if (InL == nullptr)
    	{
    		return;
    	}
    
    	luaL_getmetatable(InL, "HookUE");
    	if (lua_istable(InL, -1) == false)
    	{
    		return;
    	}
    
    	lua_pushnil(InL);
    	while (lua_next(InL, -2))
    	{
    		UFunction* Func = (UFunction*)lua_touserdata(InL, -2);
    		if (Func)
    		{
    			Func->SetNativeFunc(nullptr);
    		}
    		if (Func && Func->HasAnyFlags(RF_Transient))
    		{
                            //这里只是粗略处理一下,若要精准恢复原样需要额外的数据记录其他信息,先略过
    			Func->GetOwnerClass()->RemoveFunctionFromFunctionMap(Func);
    			Func->MarkPendingKill();
    
    			lua_pushnil(InL);
    			lua_rawsetp(InL, -4, Func);
    		}
    
    		lua_pop(InL, 1);
    	}
    }
    
    
    void FastLuaHelper::CallLuaFunction(UObject* Context, FFrame& TheStack, RESULT_DECL)
    {
    	UClass* TmpClass = Context->GetClass();
            //对于非Actor类型,这一步很可能会失败
    	UGameInstance* GI = UGameplayStatics::GetGameInstance(Context);
    
    	lua_State* LuaState = *FastLuaUnrealWrapper::LuaStateMap.Find(GI);
    	if (LuaState == nullptr)
    	{
    		return;
    	}
    	int32 tp = lua_gettop(LuaState);
    	luaL_getmetatable(LuaState, "HookUE");
    	if (lua_istable(LuaState, -1) == false)
    	{
    		return;
    	}
    	lua_rawgetp(LuaState, -1, TheStack.Node);
    	if (lua_isfunction(LuaState, -1))
    	{
    		int32 ParamsNum = 0;
    		FProperty* ReturnParam = nullptr;
    		//store param from UE script VM stack
    		FStructOnScope FuncTmpMem(TheStack.Node);
    		//push self
    		FLuaObjectWrapper::PushObject(LuaState, Context);
    		++ParamsNum;
    
    		for (TFieldIterator<FProperty> It(TheStack.Node); It; ++It)
    		{
    			//get function return Param
    			FProperty* CurrentParam = *It;
    			void* LocalValue = CurrentParam->ContainerPtrToValuePtr<void>(FuncTmpMem.GetStructMemory());
    			TheStack.StepCompiledIn<FProperty>(LocalValue);
    			if (CurrentParam->HasAnyPropertyFlags(CPF_ReturnParm))
    			{
    				ReturnParam = CurrentParam;
    			}
    			else
    			{
    				//set params for lua function
    				FastLuaHelper::PushProperty(LuaState, CurrentParam, FuncTmpMem.GetStructMemory(), 0);
    				++ParamsNum;
    			}
    		}
    
    		//call lua function
    		int32 CallRet = lua_pcall(LuaState, ParamsNum, ReturnParam ? 1 : 0, 0);
    		if (CallRet)
    		{
    			UE_LOG(LogTemp, Warning, TEXT("%s"), UTF8_TO_TCHAR(lua_tostring(LuaState, -1)));
    		}
    
    		if (ReturnParam)
    		{
    			//get function return Value, in common
    			FastLuaHelper::FetchProperty(LuaState, ReturnParam, FuncTmpMem.GetStructMemory(), -1);
    		}
    	}
    
    	lua_settop(LuaState, tp);
    }
    

    测试用法

    --BP_RPGCharacter_C.lua
    BP_RPGCharacter_C = BP_RPGCharacter_C or {}
    
    
    
    function BP_RPGCharacter_C:ReceiveBeginPlay()
        print(1)
    end
    
    
    
    function BP_RPGCharacter_C:ReceiveTick(InDeltaTime)
        
    end
    
    function BP_RPGCharacter_C:ReceiveEndPlay(InReason)
        print(3)
    end
    
    return BP_RPGCharacter_C
    
    --Main.lua
        local PlayerCtrl = GameplayStatics:GetPlayerController(G_GameInstance, 0)
        local Pawn = PlayerCtrl:K2_GetPawn()
        Unreal.LuaHookUFunction(Pawn)
    
  • 相关阅读:
    Java中有哪些无锁技术来解决并发问题?如何使用?
    什么是活锁和饥饿?
    如何避免死锁?
    什么是死锁?
    synchronized锁的升级原理是什么?
    Java中的锁之间的区别是什么?
    可重入锁与不可重入锁之间的区别与性能差异?
    数据库分库的策略
    QPS、PV和需要部署机器数量计算公式(转)
    LVS Nginx HAProxy 优缺点
  • 原文地址:https://www.cnblogs.com/rpg3d/p/12707206.html
Copyright © 2011-2022 走看看