Compare commits

..

6 Commits

Author SHA1 Message Date
martinluckyrobots
aac54426b4 Merge branch 'main' of https://luckyrobots.com/luckyrobots/LuckyRobotsUnreal into martin2 2025-04-10 13:30:35 +08:00
martinluckyrobots
d188cb846e Optimizing WB_GameWidget 2025-04-10 13:30:13 +08:00
martinluckyrobots
55fabb31e8 Optimizing WB_MainScreen 2025-04-10 10:50:58 +08:00
martinluckyrobots
28c87e6f85 Optimizing WB_MainScreen 2025-04-10 00:12:27 +08:00
martinluckyrobots
17dc39a612 Optimizing gameinstance 2025-04-09 15:18:16 +08:00
c2ddce0ce8 Merge pull request 'martin' (#17) from martin into main
Reviewed-on: luckyrobots/LuckyRobotsUnreal#17
2025-04-09 05:05:41 +00:00
23 changed files with 328 additions and 20 deletions

View File

@ -73,11 +73,11 @@ public:
/** Generate a delegates for callback events */ /** Generate a delegates for callback events */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestComplete, class USIOJRequestJSON*, Request); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOJOnRequestComplete, class USIOJRequestJSON*, Request);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestFail, class USIOJRequestJSON*, Request); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSIOJOnRequestFail, class USIOJRequestJSON*, Request);
DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestComplete, class USIOJRequestJSON*); DECLARE_MULTICAST_DELEGATE_OneParam(FSIOJOnStaticRequestComplete, class USIOJRequestJSON*);
DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestFail, class USIOJRequestJSON*); DECLARE_MULTICAST_DELEGATE_OneParam(FSIOJOnStaticRequestFail, class USIOJRequestJSON*);
/** /**
@ -216,17 +216,17 @@ private:
public: public:
/** Event occured when the request has been completed */ /** Event occured when the request has been completed */
UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event") UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event")
FOnRequestComplete OnRequestComplete; FSIOJOnRequestComplete OnRequestComplete;
/** Event occured when the request wasn't successfull */ /** Event occured when the request wasn't successfull */
UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event") UPROPERTY(BlueprintAssignable, Category = "SIOJ|Event")
FOnRequestFail OnRequestFail; FSIOJOnRequestFail OnRequestFail;
/** Event occured when the request has been completed */ /** Event occured when the request has been completed */
FOnStaticRequestComplete OnStaticRequestComplete; FSIOJOnStaticRequestComplete OnStaticRequestComplete;
/** Event occured when the request wasn't successfull */ /** Event occured when the request wasn't successfull */
FOnStaticRequestFail OnStaticRequestFail; FSIOJOnStaticRequestFail OnStaticRequestFail;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -9,6 +9,8 @@
#include "GameFramework/GameUserSettings.h" #include "GameFramework/GameUserSettings.h"
#include "Kismet/KismetMathLibrary.h" #include "Kismet/KismetMathLibrary.h"
#include "FunctionLibraries/LuckyRobotsFunctionLibrary.h" #include "FunctionLibraries/LuckyRobotsFunctionLibrary.h"
#include "VaRestSubsystem.h"
#include "Subsystems/SubsystemBlueprintLibrary.h"
void ULuckyRobotsGameInstance::DoSendMessage(const FString& SendValue) void ULuckyRobotsGameInstance::DoSendMessage(const FString& SendValue)
{ {
@ -284,6 +286,99 @@ TArray<FSelectableItemData> ULuckyRobotsGameInstance::GetSelectableItemList(EIte
return SelectableItemList; return SelectableItemList;
} }
void ULuckyRobotsGameInstance::GetMessageParse(FString Json)
{
auto VaRestSubsystem = CastChecked<UVaRestSubsystem>(USubsystemBlueprintLibrary::GetEngineSubsystem(UVaRestSubsystem::StaticClass()), ECastCheckedType::NullChecked);
if (VaRestSubsystem)
{
UVaRestJsonObject* VaRestJsonObject = VaRestSubsystem->ConstructVaRestJsonObject();
if (VaRestJsonObject)
{
if (VaRestJsonObject->DecodeJson(Json, true))
{
TArray<UVaRestJsonValue*> VaRestJsonValueList = VaRestJsonObject->GetArrayField("LuckyCode");
if (VaRestJsonValueList.Num() > 0)
{
LuckyCodeList.Empty();
for (auto VaRestJsonValue : VaRestJsonValueList)
{
if (VaRestJsonValue)
{
UVaRestJsonObject* TempObject = VaRestJsonValue->AsObject();
if (TempObject)
{
FLuckyCode TempLuckyCode;
TempLuckyCode.ID = FCString::Atoi(*(TempObject->GetStringField("ID")));
TempLuckyCode.Code = TempObject->GetStringField("code");
TempLuckyCode.Time = FCString::Atof(*(TempObject->GetStringField("time")));
TempLuckyCode.bCallback = (TempObject->GetStringField("callback") == "on");
LuckyCodeList.Add(TempLuckyCode);
}
}
}
}
else
{
UVaRestJsonValue* VaRestJsonValue = VaRestJsonObject->GetField("LuckyCapture");
if (VaRestJsonValue)
{
UVaRestJsonObject* TempObject = VaRestJsonValue->AsObject();
if (TempObject)
{
bIsCapture = (TempObject->GetStringField("capture") == "on");
bIsCaptureHand = (TempObject->GetStringField("is_capture_hand") == "on");
bIsCaptureHead = (TempObject->GetStringField("is_capture_head") == "on");
bScenarioCapture = (TempObject->GetStringField("scenario_capture") == "on");
TargetPosition = FTransform();
SetCurrentFileName(TempObject->GetStringField("file_name"));
SetCurrentFolderName(TempObject->GetStringField("folder_name"));
SetCurrentCaptureNumber(FCString::Atoi(*TempObject->GetStringField("capture_name")));
SetCurrentIsInfiniteCapture(TempObject->GetStringField("infinite_capture") == "on");
SetCurrentWritesPerSec(FCString::Atoi(*TempObject->GetStringField("per_second")));
SetCurrentIsRandomPeople(TempObject->GetStringField("random_people") == "on");
}
}
}
}
}
}
}
FParsedData ULuckyRobotsGameInstance::DoJsonParse(const FString& JsonString)
{
FParsedData ParsedData;
auto VaRestSubsystem = CastChecked<UVaRestSubsystem>(USubsystemBlueprintLibrary::GetEngineSubsystem(UVaRestSubsystem::StaticClass()), ECastCheckedType::NullChecked);
if (!VaRestSubsystem)
{
return ParsedData;
}
UVaRestJsonObject* VaRestJsonObject = VaRestSubsystem->ConstructVaRestJsonObject();
if (!VaRestJsonObject)
{
return ParsedData;
}
if (VaRestJsonObject->DecodeJson(JsonString, true))
{
UVaRestJsonObject* TempJsonObject = VaRestJsonObject->GetObjectField("startup_instructions");
if (TempJsonObject)
{
ParsedData.LevelName = TempJsonObject->GetStringField("level");
ParsedData.CharacterName = TempJsonObject->GetStringField("character");
ParsedData.Quality = TempJsonObject->GetStringField("quality");
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("Parse Problem"));
}
return ParsedData;
}
void ULuckyRobotsGameInstance::SetCurrentFolderName(const FString& FolderName) void ULuckyRobotsGameInstance::SetCurrentFolderName(const FString& FolderName)
{ {
CurrentCaptureSettingsData.FolderName = FText::FromString(FolderName); CurrentCaptureSettingsData.FolderName = FText::FromString(FolderName);

View File

@ -16,7 +16,8 @@ UClass* ALuckyRobotsGameMode::GetDefaultPawnClassForController_Implementation(AC
UClass* RobotClass = Super::GetDefaultPawnClassForController_Implementation(InController); UClass* RobotClass = Super::GetDefaultPawnClassForController_Implementation(InController);
ERobotsName CurrentRobot = ERobotsName::None; ERobotsName CurrentRobot = ERobotsName::None;
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance())) ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
CurrentRobot = GameInstance->CurrentSelectRobot; CurrentRobot = GameInstance->CurrentSelectRobot;
} }

View File

@ -5,6 +5,7 @@
#include "SocketIOClientComponent.h" #include "SocketIOClientComponent.h"
#include "FunctionLibraries/LuckyRobotsFunctionLibrary.h" #include "FunctionLibraries/LuckyRobotsFunctionLibrary.h"
#include "Core/LuckyRobotsGameInstance.h" #include "Core/LuckyRobotsGameInstance.h"
#include "SIOJLibrary.h"
ALuckyRobotsGameState::ALuckyRobotsGameState() ALuckyRobotsGameState::ALuckyRobotsGameState()
{ {
@ -41,6 +42,7 @@ void ALuckyRobotsGameState::DoSocketOnGenericEvent(FString EventName, USIOJsonVa
{ {
if (ULuckyRobotsGameInstance* GameInstance = ULuckyRobotsFunctionLibrary::GetLuckyRobotsGameInstance(this)) if (ULuckyRobotsGameInstance* GameInstance = ULuckyRobotsFunctionLibrary::GetLuckyRobotsGameInstance(this))
{ {
GameInstance->DoGetDispatch(EventName, EventData); GameInstance->OnMessageDispatched.Broadcast(EventName);
GameInstance->GetMessageParse(USIOJLibrary::Conv_SIOJsonValueToString(EventData));
} }
} }

View File

@ -5,11 +5,25 @@
#include "Engine/DataTable.h" #include "Engine/DataTable.h"
#include "Core/LuckyRobotsGameInstance.h" #include "Core/LuckyRobotsGameInstance.h"
#include "FunctionLibraries/LuckyRobotsFunctionLibrary.h" #include "FunctionLibraries/LuckyRobotsFunctionLibrary.h"
#include "Subsystems/SubsystemBlueprintLibrary.h"
#include "VaRestSubsystem.h"
#include <Kismet/GameplayStatics.h>
void UMainScreenUserWidget::NativeConstruct() void UMainScreenUserWidget::NativeConstruct()
{ {
Super::NativeConstruct(); Super::NativeConstruct();
InitData(); InitData();
DoSendReadyJson();
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
GameInstance->bIsFirstOpenGame = false;
GameInstance->DoResolutionChange(bIsFullScreen);
GameInstance->OnMessageDispatched.AddDynamic(this, &UMainScreenUserWidget::OnMessageDispatchedHandler);
}
} }
void UMainScreenUserWidget::InitData() void UMainScreenUserWidget::InitData()
@ -17,7 +31,8 @@ void UMainScreenUserWidget::InitData()
InitRobotData(); InitRobotData();
InitLevelData(); InitLevelData();
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance())) ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
CurrentQualityIndex = static_cast<int32>(GameInstance->CurrentSelectQuality); CurrentQualityIndex = static_cast<int32>(GameInstance->CurrentSelectQuality);
} }
@ -108,9 +123,33 @@ void UMainScreenUserWidget::SelectPreviousQuality()
UpdateSelectQuality(); UpdateSelectQuality();
} }
void UMainScreenUserWidget::DoResolutionChange()
{
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
bIsFullScreen = !bIsFullScreen;
GameInstance->DoResolutionChange(bIsFullScreen);
}
}
void UMainScreenUserWidget::GameStart()
{
if (UKismetSystemLibrary::IsValidSoftObjectReference(CurrentSelectLevelObject))
{
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
GameInstance->bIsFirstOpenGame = true;
UGameplayStatics::OpenLevelBySoftObjectPtr(this, CurrentSelectLevelObject);
}
}
}
void UMainScreenUserWidget::UpdateSelectRobot() void UMainScreenUserWidget::UpdateSelectRobot()
{ {
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance())) ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
GameInstance->CurrentSelectRobot = GetCurrentRobotData().Name; GameInstance->CurrentSelectRobot = GetCurrentRobotData().Name;
} }
@ -120,7 +159,8 @@ void UMainScreenUserWidget::UpdateSelectRobot()
void UMainScreenUserWidget::UpdateSelectLevel() void UMainScreenUserWidget::UpdateSelectLevel()
{ {
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance())) ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
GameInstance->CurrentSelectLevel = GetCurrentLevelData().LevelEnum; GameInstance->CurrentSelectLevel = GetCurrentLevelData().LevelEnum;
} }
@ -129,9 +169,75 @@ void UMainScreenUserWidget::UpdateSelectLevel()
void UMainScreenUserWidget::UpdateSelectQuality() void UMainScreenUserWidget::UpdateSelectQuality()
{ {
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance())) ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
GameInstance->CurrentSelectQuality = static_cast<EQualityEnum>(CurrentQualityIndex); GameInstance->CurrentSelectQuality = static_cast<EQualityEnum>(CurrentQualityIndex);
} }
BPUpdateSelectQuality(); BPUpdateSelectQuality();
} }
void UMainScreenUserWidget::OnMessageDispatchedHandler(const FString& Message)
{
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
FParsedData ParsedData = GameInstance->DoJsonParse(Message);
if (ParsedData.CharacterName == "DRONE")
{
GameInstance->CurrentSelectRobot = ERobotsName::LuckyDrone;
}else if(ParsedData.CharacterName == "Stretch Robot V1")
{
GameInstance->CurrentSelectRobot = ERobotsName::StretchRobotV1;
}
GameInstance->bIsFirstOpenGame = true;
ELevelEnum TempLevelEnum = ELevelEnum::None;
if (ParsedData.LevelName == "LOFT")
{
TempLevelEnum = ELevelEnum::Loft;
}else if(ParsedData.LevelName == "ISTANBUL")
{
TempLevelEnum = ELevelEnum::Istanbul;
}
if (TempLevelEnum != ELevelEnum::None)
{
TArray<FLevelData> TempLevelDataList = ULuckyRobotsFunctionLibrary::GetActiveLevelDataList(this);
for (const FLevelData& LevelData : TempLevelDataList)
{
if (LevelData.LevelEnum == TempLevelEnum && LevelData.LevelObject)
{
UGameplayStatics::OpenLevelBySoftObjectPtr(this, LevelData.LevelObject);
break;
}
}
}
}
}
void UMainScreenUserWidget::DoSendReadyJson()
{
auto VaRestSubsystem = CastChecked<UVaRestSubsystem>(USubsystemBlueprintLibrary::GetEngineSubsystem(UVaRestSubsystem::StaticClass()), ECastCheckedType::NullChecked);
if (!VaRestSubsystem)
{
return;
}
UVaRestJsonObject* VaRestJsonObject = VaRestSubsystem->ConstructVaRestJsonObject();
if (!VaRestJsonObject)
{
return;
}
VaRestJsonObject->SetStringField("name", "game_is_loaded");
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
FString SendedString = VaRestJsonObject->EncodeJsonToSingleString();
GameInstance->DoSendMessage(SendedString);
UE_LOG(LogTemp, Log, TEXT("Sended: %s"), *SendedString);
}
}

View File

@ -7,8 +7,45 @@
void UGameUserWidget::NativeConstruct() void UGameUserWidget::NativeConstruct()
{ {
Super::NativeConstruct(); Super::NativeConstruct();
if (ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance()))
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{ {
GameInstance->GameUserWidget = this; GameInstance->GameUserWidget = this;
} }
} }
void UGameUserWidget::DoWaitSecond()
{
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
if (GameInstance->bIsFirstOpenGame)
{
DownCount = 3;
GetWorld()->GetTimerManager().ClearTimer(UpdateDownCountTimerHandle);
GetWorld()->GetTimerManager().SetTimer(UpdateDownCountTimerHandle, this, &UGameUserWidget::UpdateDownCount, 1.0f, true);
UpdateDownCount();
}
}
}
void UGameUserWidget::UpdateDownCount()
{
if (DownCount > 0)
{
DownCountStr = FString::FromInt(DownCount);
DownCount--;
}
else
{
DownCountStr = "";
ULuckyRobotsGameInstance* GameInstance = Cast<ULuckyRobotsGameInstance>(GetGameInstance());
if (GameInstance)
{
GameInstance->DoQualitySettings(0, true);
}
GetWorld()->GetTimerManager().ClearTimer(UpdateDownCountTimerHandle);
}
}

View File

@ -7,6 +7,9 @@
#include "SharedDef.h" #include "SharedDef.h"
#include "LuckyRobotsGameInstance.generated.h" #include "LuckyRobotsGameInstance.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMessageDispatched, const FString&, Message);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRandomMeshesUpdated);
class USIOJsonValue; class USIOJsonValue;
class UGameUserWidget; class UGameUserWidget;
/** /**
@ -128,6 +131,13 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FLuckyCode> LuckyCodeList; TArray<FLuckyCode> LuckyCodeList;
public:
UPROPERTY(BlueprintCallable, BlueprintAssignable, Category = "Event")
FOnMessageDispatched OnMessageDispatched;
UPROPERTY(BlueprintCallable, BlueprintAssignable, Category = "Event")
FOnRandomMeshesUpdated OnRandomMeshesUpdated;
public: public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void DoSendMessage(const FString& SendValue); void DoSendMessage(const FString& SendValue);
@ -190,6 +200,12 @@ public:
UFUNCTION(BlueprintPure, Category = "Selectable Items") UFUNCTION(BlueprintPure, Category = "Selectable Items")
TArray<FSelectableItemData> GetSelectableItemList(EItemCategory ItemCategory); TArray<FSelectableItemData> GetSelectableItemList(EItemCategory ItemCategory);
UFUNCTION(BlueprintCallable)
void GetMessageParse(FString Json);
UFUNCTION(BlueprintCallable)
FParsedData DoJsonParse(const FString& JsonString);
public: public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void SetCurrentFolderName(const FString& FolderName); void SetCurrentFolderName(const FString& FolderName);
@ -295,7 +311,4 @@ public:
UFUNCTION(BlueprintPure) UFUNCTION(BlueprintPure)
int32 GetWidgetTotalHit() const; int32 GetWidgetTotalHit() const;
public:
UFUNCTION(BlueprintImplementableEvent)
void DoGetDispatch(const FString& EventName, USIOJsonValue* EventData);
}; };

View File

@ -28,6 +28,12 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 CurrentQualityIndex; int32 CurrentQualityIndex;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsFullScreen = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSoftObjectPtr<UWorld> CurrentSelectLevelObject;
public: public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void InitData(); void InitData();
@ -62,10 +68,20 @@ public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void SelectPreviousQuality(); void SelectPreviousQuality();
UFUNCTION(BlueprintCallable)
void DoResolutionChange();
UFUNCTION(BlueprintCallable)
void GameStart();
void UpdateSelectRobot(); void UpdateSelectRobot();
void UpdateSelectLevel(); void UpdateSelectLevel();
void UpdateSelectQuality(); void UpdateSelectQuality();
public:
void OnMessageDispatchedHandler(const FString& Message);
void DoSendReadyJson();
public: public:
UFUNCTION(BlueprintImplementableEvent) UFUNCTION(BlueprintImplementableEvent)
void BPUpdateSelectRobot(); void BPUpdateSelectRobot();

View File

@ -671,3 +671,18 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
FTransform Transform; FTransform Transform;
}; };
USTRUCT(BlueprintType)
struct FParsedData
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category = "Parsed Data")
FString LevelName;
UPROPERTY(BlueprintReadWrite, Category = "Parsed Data")
FString CharacterName;
UPROPERTY(BlueprintReadWrite, Category = "Parsed Data")
FString Quality;
};

View File

@ -19,9 +19,32 @@ protected:
virtual void NativeConstruct() override; virtual void NativeConstruct() override;
public: public:
UFUNCTION(BlueprintImplementableEvent) FTimerHandle UpdateDownCountTimerHandle;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsCrash;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsGoalMenuVis;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsTracing;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 DownCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DownCountStr;
public:
UFUNCTION(BlueprintCallable)
void DoWaitSecond();
void UpdateDownCount();
public:
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
void DoLogItemAdd(const FString& Topic, const FString& MsgText, ELogItemType LogItemType); void DoLogItemAdd(const FString& Topic, const FString& MsgText, ELogItemType LogItemType);
UFUNCTION(BlueprintImplementableEvent) UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
void DoRefreshListView(); void DoRefreshListView();
}; };