UE4 Unlua源码解析10 - Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的
Posted 珞珈大胖强TURBO
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4 Unlua源码解析10 - Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的相关的知识,希望对你有一定的参考价值。
Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的
时机发生在UUnLuaManager中绑定UObject和Lua Object的时候,具体到函数是BindInternal
648-649行是拿到所有的Lua方法,并且存储到LuaFunctions里,650-651行拿到所有的UE的所有BlueprintEvent和RepNotify方法,存起来,然后最重要的函数OverrideFunctions中,其实就是本小节的答案所在,在这个函数里用Lua的函数覆盖BlueprintImplementableEvent或
BlueprintNativeEvent的方法
/**
* Override candidate UFunctions
*/
void UUnLuaManager::OverrideFunctions(const TSet<FName> &LuaFunctions, TMap<FName, UFunction*> &UEFunctions, UClass *OuterClass, bool bCheckFuncNetMode)
for (const FName &LuaFuncName : LuaFunctions)
UFunction **Func = UEFunctions.Find(LuaFuncName);
if (Func)
UFunction *Function = *Func;
OverrideFunction(Function, OuterClass, LuaFuncName);
对于所有的LuaFunc,看能否找到同名的UEFunction,如果找到调用OverrideFunction
/**
* Override a UFunction
*/
void UUnLuaManager::OverrideFunction(UFunction *TemplateFunction, UClass *OuterClass, FName NewFuncName)
if (TemplateFunction->GetOuter() != OuterClass)
//#if UE_BUILD_SHIPPING || UE_BUILD_TEST
if (TemplateFunction->Script.Num() > 0 && TemplateFunction->Script[0] == EX_CallLua)
#if ENABLE_CALL_OVERRIDDEN_FUNCTION
TemplateFunction = GReflectionRegistry.FindOverriddenFunction(TemplateFunction);
#else
TemplateFunction = New2TemplateFunctions.FindChecked(TemplateFunction);
#endif
//#endif
AddFunction(TemplateFunction, OuterClass, NewFuncName); // add a duplicated UFunction to child UClass
else
ReplaceFunction(TemplateFunction, OuterClass); // replace thunk function and insert opcodes
如果方法是父类的方法,就给孩子添加覆盖函数,如果是自己的方法,就用Lua方法覆盖,也就是走到ReplaceFunction
/**
* Replace thunk function and insert opcodes
*/
void UUnLuaManager::ReplaceFunction(UFunction *TemplateFunction, UClass *OuterClass)
###1
FNativeFuncPtr *NativePtr = CachedNatives.Find(TemplateFunction);
if (!NativePtr)
#if ENABLE_CALL_OVERRIDDEN_FUNCTION
FName NewFuncName(*FString::Printf(TEXT("%s%s"), *TemplateFunction->GetName(), TEXT("Copy")));
if (TemplateFunction->HasAnyFunctionFlags(FUNC_Native))
// call this before duplicate UFunction that has FUNC_Native to eliminate "Failed to bind native function" warnings.
OuterClass->AddNativeFunction(*NewFuncName.ToString(), TemplateFunction->GetNativeFunc());
###1
###2
UFunction *NewFunc = DuplicateUFunction(TemplateFunction, OuterClass, NewFuncName);
GReflectionRegistry.AddOverriddenFunction(TemplateFunction, NewFunc);
#endif
CachedNatives.Add(TemplateFunction, TemplateFunction->GetNativeFunc());
if (!TemplateFunction->HasAnyFunctionFlags(FUNC_Native) && TemplateFunction->Script.Num() > 0)
CachedScripts.Add(TemplateFunction, TemplateFunction->Script);
TemplateFunction->Script.Empty(3);
OverrideUFunction(TemplateFunction, (FNativeFuncPtr)&FLuaInvoker::execCallLua, GReflectionRegistry.RegisterFunction(TemplateFunction));
###2
###1 将原UFucntion做了一个备份,将其命名为FunctionName+Copy
###2 Unlua全局数据结构GReflectionRegistry存起来原UFunction与新方法的映射,如果UFunction绑定的是蓝图脚本,会缓存字节码,然后调用全局方法OverrideUFunction真正开始覆盖方法
/**
* 1. Replace thunk function
* 2. Insert special opcodes if necessary
*/
void OverrideUFunction(UFunction *Function, FNativeFuncPtr NativeFunc, void *Userdata, bool bInsertOpcodes)
if (!Function->HasAnyFunctionFlags(FUNC_Net) || Function->HasAnyFunctionFlags(FUNC_Native))
Function->SetNativeFunc(NativeFunc);
if (Function->Script.Num() < 1)
#if UE_BUILD_SHIPPING || UE_BUILD_TEST
if (bInsertOpcodes)
Function->Script.Add(EX_CallLua);
int32 Index = Function->Script.AddZeroed(sizeof(Userdata));
FMemory::Memcpy(Function->Script.GetData() + Index, &Userdata, sizeof(Userdata));
Function->Script.Add(EX_Return);
Function->Script.Add(EX_Nothing);
else
int32 Index = Function->Script.AddZeroed(sizeof(Userdata));
FMemory::Memcpy(Function->Script.GetData() + Index, &Userdata, sizeof(Userdata));
#else
Function->Script.Add(EX_CallLua);
Function->Script.Add(EX_Return);
Function->Script.Add(EX_Nothing);
#endif
UFunction的Script存储的是蓝图字节码,如果不是SHIPPING,加入三个字节码EX_CallLua,EX_Return,EX_Nothing,如果是SHIPPING,多加了一个Userdata,也就是Function的FunctionDesc数据
可以看一下啊EX_CallLua的实现,是一个宏实现
/**
* Custom thunk function to call Lua function
*/
DEFINE_FUNCTION(FLuaInvoker::execCallLua)
bool bUnpackParams = false;
UFunction *Func = Stack.Node;
FFunctionDesc *FuncDesc = nullptr;
if (Stack.CurrentNativeFunction)
if (Func != Stack.CurrentNativeFunction)
Func = Stack.CurrentNativeFunction;
#if UE_BUILD_SHIPPING || UE_BUILD_TEST
FMemory::Memcpy(&FuncDesc, &Stack.CurrentNativeFunction->Script[1], sizeof(FuncDesc));
#endif
bUnpackParams = true;
else
if (Func->GetNativeFunc() == (FNativeFuncPtr)&FLuaInvoker::execCallLua)
check(*Stack.Code == EX_CallLua);
Stack.SkipCode(1); // skip EX_CallLua only when called from native func
//!!!Fix!!!
//find desc from classdesc
#if UE_BUILD_SHIPPING || UE_BUILD_TEST
if (!FuncDesc)
FMemory::Memcpy(&FuncDesc, Stack.Code, sizeof(FuncDesc));
Stack.SkipCode(sizeof(FuncDesc)); // skip 'FFunctionDesc' pointer
#else
FuncDesc = GReflectionRegistry.RegisterFunction(Func);
#endif
bool bRpcCall = false;
#if SUPPORTS_RPC_CALL
AActor *Actor = Cast<AActor>(Stack.Object);
if (!Actor)
UActorComponent *ActorComponent = Cast<UActorComponent>(Stack.Object);
if (ActorComponent)
Actor = ActorComponent->GetOwner();
if (Actor)
/*ENetMode NetMode = Actor->GetNetMode();
if ((Func->HasAnyFunctionFlags(FUNC_NetClient | FUNC_NetMulticast) && NetMode == NM_Client) || (Func->HasAnyFunctionFlags(FUNC_NetServer | FUNC_NetMulticast) && (NetMode == NM_DedicatedServer || NetMode == NM_ListenServer)))
bRpcCall = true;
*/
int32 Callspace = Actor->GetFunctionCallspace(Func, nullptr);
bRpcCall = Callspace & FunctionCallspace::Remote;
#endif
bool bSuccess = FuncDesc->CallLua(Context, Stack, (void*)RESULT_PARAM, bRpcCall, bUnpackParams);
if (!bSuccess && bUnpackParams)
FMemMark Mark(FMemStack::Get());
void *Params = New<uint8>(FMemStack::Get(), Func->ParmsSize, 16);
for (TFieldIterator<FProperty> It(Func); It && (It->PropertyFlags & CPF_Parm) == CPF_Parm; ++It)
Stack.Step(Stack.Object, It->ContainerPtrToValuePtr<uint8>(Params));
Stack.SkipCode(1); // skip EX_EndFunctionParms
这里主要是拿到FunctionDesc,然后通过FunctionDesc调用CallLua,CallLua才是核心代码
/**
* Call Lua function that overrides this UFunction
*/
bool FFunctionDesc::CallLua(UObject *Context, FFrame &Stack, void *RetValueAddress, bool bRpcCall, bool bUnpackParams)
// push Lua function to the stack
bool bSuccess = false;
lua_State *L = *GLuaCxt;
if (FunctionRef != INDEX_NONE)
bSuccess = PushFunction(L, Context, FunctionRef);
else
// support rpc in standlone mode
bRpcCall = Function->HasAnyFunctionFlags(FUNC_Net);
FunctionRef = PushFunction(L, Context, bRpcCall ? TCHAR_TO_UTF8(*FString::Printf(TEXT("%s_RPC"), *FuncName)) : TCHAR_TO_UTF8(*FuncName));
bSuccess = FunctionRef != INDEX_NONE;
if (bSuccess)
if (bUnpackParams)
void* Params = nullptr;
#if ENABLE_PERSISTENT_PARAM_BUFFER
if (!bHasDelegateParams)
Params = Buffer;
#endif
if (!Params)
Params = Function->ParmsSize > 0 ? FMemory::Malloc(Function->ParmsSize, 16) : nullptr;
for (TFieldIterator<FProperty> It(Function); It && (It->PropertyFlags & CPF_Parm) == CPF_Parm; ++It)
Stack.Step(Stack.Object, It->ContainerPtrToValuePtr<uint8>(Params));
check(Stack.PeekCode() == EX_EndFunctionParms);
Stack.SkipCode(1); // skip EX_EndFunctionParms
bSuccess = CallLuaInternal(L, Params, Stack.OutParms, RetValueAddress); // call Lua function...
if (Params)
#if ENABLE_PERSISTENT_PARAM_BUFFER
if (bHasDelegateParams)
#endif
FMemory::Free(Params);
else
bSuccess = CallLuaInternal(L, Stack.Locals, Stack.OutParms, RetValueAddress); // call Lua function...
return bSuccess;
- 找到绑定UObejct对象实例的Lua Module,在Lua Module中找到与FFucntionDesc的同名Lua函数,找到了函数压入到Lua栈中。
- 将Object入栈
- 将反射调用的上下文环境FFrame中的参数内存,按照参数顺序从内存中读取参数,参数转成Lua,然后将函数参数按顺序压入栈中。
- 通过lua_pcall调用Lua函数
- lua函数调用完成后会把返回值push到lua栈中,Unlua需要以一定顺序取出这些返回值,并设置到正确的C++属性上。lua函数支持多返回值,C++函数只能有一个返回值,但可以使用引用参数实现多返回值效果,因此UnLua支持lua函数直接返回多值给C++。
- 将调用后的栈中返回值,回写到反射调用的参数内存中。这里对于Lua中的基础类型不支持引用,以多返回值形式实现。
以上这个过程其实与上篇中所提到的Lua反射调用UFunction的操作,正好是一个逆操作。其原理是一样的。
以上是关于UE4 Unlua源码解析10 - Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的的主要内容,如果未能解决你的问题,请参考以下文章
UE4 Unlua源码解析10 - Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的
UE4 Unlua源码解析12 - Lua与UE4的混合GC
UE4 Unlua源码解析12 - Lua与UE4的混合GC