前置阅读 | Pre-reading
Puer的版本 | Puer Version
master
UE的版本 | UE Version
5.4.4
发生在哪个平台 | Platform
Editor(win)
错误信息 | Error Message
BP_TestParent 父类蓝图,BP_TestChild 子类蓝图,ts mixin类分别mixin蓝图类,然后TestParent和TestChild蓝图中均无实现ReceiveBeginPlay,会直接出现crash
测试发现只要子类BP_TestChild在蓝图中定义了ReceiveBeginPlay方法即不会crash了
0x0000000000000000
UFunction::Invoke(UObject *, FFrame &, void *const) Class.cpp:6847
[Blueprint] BP_TestChild: ReceiveBeginPlay Function /Game/Blueprints/BP_TestChild.BP_TestChild_C:ReceiveBeginPlay
UObject::ProcessEvent(UFunction *, void *) ScriptCore.cpp:2142
AActor::ProcessEvent(UFunction *, void *) Actor.cpp:1092
AActor::BeginPlay() Actor.cpp:4251
AActor::DispatchBeginPlay(bool) Actor.cpp:4191
AWorldSettings::NotifyBeginPlay() WorldSettings.cpp:305
AGameStateBase::HandleBeginPlay() GameStateBase.cpp:227
AGameModeBase::StartPlay() GameModeBase.cpp:205
UWorld::BeginPlay() World.cpp:5329
UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer *, const FGameInstancePIEParameters &) GameInstance.cpp:566
UEditorEngine::CreateInnerProcessPIEGameInstance(FRequestPlaySessionParams &, const FGameInstancePIEParameters &, int) PlayLevel.cpp:3141
UEditorEngine::OnLoginPIEComplete_Deferred(int, bool, FString, FPieLoginStruct) PlayLevel.cpp:1589
UEditorEngine::CreateNewPlayInEditorInstance(FRequestPlaySessionParams &, const bool, EPlayNetMode) PlayLevel.cpp:1853
UEditorEngine::StartPlayInEditorSession(FRequestPlaySessionParams &) PlayLevel.cpp:2868
UEditorEngine::StartQueuedPlaySessionRequestImpl() PlayLevel.cpp:1167
UEditorEngine::StartQueuedPlaySessionRequest() PlayLevel.cpp:1064
UEditorEngine::Tick(float, bool) EditorEngine.cpp:1901
UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:547
FEngineLoop::Tick() LaunchEngineLoop.cpp:5915
[Inlined] EngineTick() Launch.cpp:61
GuardedMain(const wchar_t *) Launch.cpp:182
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:247
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:298
看了相关代码后感觉原因应该是子类在UJSGeneratedClass::Mixin时,由于蓝图没有ReceiveBeginPlay函数,Duplicate了父类已经被Mixin了的ReceiveBeginPlay函数,然后误认为子类已经Mixin过了,直接用了父类这个Mixin函数,状态出现了些问题,麻烦帮忙看看这里这样设计是否合理
UFunction* UJSGeneratedClass::Mixin(v8::Isolate* Isolate, UClass* Class, UFunction* Super,
TSharedPtr<PUERTS_NAMESPACE::IDynamicInvoker, ESPMode::ThreadSafe> DynamicInvoker, bool TakeJsObjectRef, bool Warning)
{
bool Existed = Super->GetOuter() == Class;
if (!Existed)
{
UFunction* Tmp =
Cast<UFunction>(StaticDuplicateObject(Super, Class, Super->GetFName(), RF_AllFlags, UFunction::StaticClass()));
Tmp->SetSuperStruct(Super);
Tmp->Next = Class->Children;
Class->Children = Tmp;
Class->AddFunctionToFunctionMap(Tmp, Tmp->GetFName());
Tmp->SetFlags(Tmp->GetFlags() | RF_Transient);
Super = Tmp;
Super->ClearInternalFlags(EInternalObjectFlags::Native);
Super->StaticLink(true);
}
auto MaybeJSFunction = Cast<UJSGeneratedFunction>(Super);
if (!MaybeJSFunction)
{
MaybeJSFunction = UJSGeneratedFunction::GetJSGeneratedFunctionFromScript(Super);
}
if (MaybeJSFunction)
{
if (Warning)
{
UE_LOG(Puerts, Warning, TEXT("Try to mixin a function[%s:%s] already mixin by anthor vm"), *Class->GetName(),
*Super->GetName());
}
return MaybeJSFunction;
}
...
}
问题重现 | Bug reproduce
复现用demo项目:https://github.com/Edenlia/puerts_unreal_demo_edenlia
BP_TestParent_C.ts
import * as UE from 'ue'
import {argv, blueprint} from 'puerts';
let BP_TestParent_UClass = UE.Class.Load('/Game/Blueprints/BP_TestParent.BP_TestParent_C');
const BP_TestParent_JsProxyClass = blueprint.tojs<typeof UE.Game.Blueprints.BP_TestParent.BP_TestParent_C>(BP_TestParent_UClass);
interface BP_TestParent_TS extends UE.Game.Blueprints.BP_TestParent.BP_TestParent_C {}
class BP_TestParent_TS {
ReceiveBeginPlay():void {
console.log(`BP_TestParent Ts ReceiveBeginPlay`);
}
ReceiveTick(DeltaSeconds: number) {
// console.log(`BP_TestParent Ts ReceiveTick`);
}
}
const BP_TestParent = blueprint.mixin(BP_TestParent_JsProxyClass, BP_TestParent_TS);
export {BP_TestParent_TS, BP_TestParent};
export default BP_TestParent;
BP_TestChild_C.ts
import * as UE from 'ue'
import {argv, blueprint} from 'puerts';
import {BP_TestParent_TS, BP_TestParent} from './BP_TestParent_C';
let BP_TestChild_UClass = UE.Class.Load('/Game/Blueprints/BP_TestChild.BP_TestChild_C');
const BP_TestChild_JsProxyClass = blueprint.tojs<typeof UE.Game.Blueprints.BP_TestChild.BP_TestChild_C>(BP_TestChild_UClass);
interface BP_TestChild_TS extends UE.Game.Blueprints.BP_TestChild.BP_TestChild_C {}
class BP_TestChild_TS extends BP_TestParent_TS {
ReceiveBeginPlay():void {
// super.ReceiveBeginPlay();
console.log(`BP_TestChild Ts ReceiveBeginPlay`);
}
// ReceiveTick(DeltaSeconds: number) {
// // console.log(`BP_TestChild Ts ReceiveTick`);
// }
}
const BP_TestChild = blueprint.mixin(BP_TestChild_JsProxyClass, BP_TestChild_TS);
export {BP_TestChild_TS, BP_TestChild};
export default BP_TestChild;
QuickStart.ts
import "./BP_TestParent_C"
import "./BP_TestChild_C"
BP_TestChild蓝图中删除ReceiveBeginPlay函数
将BP_TestChild_C拖入场景,点击PIE Crash
前置阅读 | Pre-reading
Puer的版本 | Puer Version
master
UE的版本 | UE Version
5.4.4
发生在哪个平台 | Platform
Editor(win)
错误信息 | Error Message
BP_TestParent 父类蓝图,BP_TestChild 子类蓝图,ts mixin类分别mixin蓝图类,然后TestParent和TestChild蓝图中均无实现ReceiveBeginPlay,会直接出现crash
测试发现只要子类BP_TestChild在蓝图中定义了ReceiveBeginPlay方法即不会crash了
0x0000000000000000
UFunction::Invoke(UObject *, FFrame &, void *const) Class.cpp:6847
[Blueprint] BP_TestChild: ReceiveBeginPlay Function /Game/Blueprints/BP_TestChild.BP_TestChild_C:ReceiveBeginPlay
UObject::ProcessEvent(UFunction *, void *) ScriptCore.cpp:2142
AActor::ProcessEvent(UFunction *, void *) Actor.cpp:1092
AActor::BeginPlay() Actor.cpp:4251
AActor::DispatchBeginPlay(bool) Actor.cpp:4191
AWorldSettings::NotifyBeginPlay() WorldSettings.cpp:305
AGameStateBase::HandleBeginPlay() GameStateBase.cpp:227
AGameModeBase::StartPlay() GameModeBase.cpp:205
UWorld::BeginPlay() World.cpp:5329
UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer *, const FGameInstancePIEParameters &) GameInstance.cpp:566
UEditorEngine::CreateInnerProcessPIEGameInstance(FRequestPlaySessionParams &, const FGameInstancePIEParameters &, int) PlayLevel.cpp:3141
UEditorEngine::OnLoginPIEComplete_Deferred(int, bool, FString, FPieLoginStruct) PlayLevel.cpp:1589
UEditorEngine::CreateNewPlayInEditorInstance(FRequestPlaySessionParams &, const bool, EPlayNetMode) PlayLevel.cpp:1853
UEditorEngine::StartPlayInEditorSession(FRequestPlaySessionParams &) PlayLevel.cpp:2868
UEditorEngine::StartQueuedPlaySessionRequestImpl() PlayLevel.cpp:1167
UEditorEngine::StartQueuedPlaySessionRequest() PlayLevel.cpp:1064
UEditorEngine::Tick(float, bool) EditorEngine.cpp:1901
UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:547
FEngineLoop::Tick() LaunchEngineLoop.cpp:5915
[Inlined] EngineTick() Launch.cpp:61
GuardedMain(const wchar_t *) Launch.cpp:182
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:247
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:298
看了相关代码后感觉原因应该是子类在UJSGeneratedClass::Mixin时,由于蓝图没有ReceiveBeginPlay函数,Duplicate了父类已经被Mixin了的ReceiveBeginPlay函数,然后误认为子类已经Mixin过了,直接用了父类这个Mixin函数,状态出现了些问题,麻烦帮忙看看这里这样设计是否合理
问题重现 | Bug reproduce
复现用demo项目:https://github.com/Edenlia/puerts_unreal_demo_edenlia
BP_TestParent_C.ts
BP_TestChild_C.ts
QuickStart.ts
BP_TestChild蓝图中删除ReceiveBeginPlay函数
将BP_TestChild_C拖入场景,点击PIE Crash