270 lines
8.1 KiB
C++
270 lines
8.1 KiB
C++
#include "UDPInstanceSubsystem.h"
|
|
#include "Serialization/JsonReader.h"
|
|
#include "Serialization/JsonWriter.h"
|
|
#include "Sockets.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Common/UdpSocketBuilder.h"
|
|
#include "Common/UdpSocketReceiver.h"
|
|
#include "GameFramework/Pawn.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
|
|
TSharedPtr<FJsonObject> FDeviceData::ToJsonObject() const
|
|
{
|
|
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
|
|
JsonObject->SetStringField("id", ID);
|
|
JsonObject->SetNumberField("x", X);
|
|
JsonObject->SetNumberField("y", Y);
|
|
JsonObject->SetNumberField("z", Z);
|
|
JsonObject->SetNumberField("yaw", Yaw);
|
|
JsonObject->SetNumberField("roll", Roll);
|
|
JsonObject->SetNumberField("pitch", Pitch);
|
|
return JsonObject;
|
|
}
|
|
|
|
bool FDeviceData::FromJsonObject(const TSharedPtr<FJsonObject>& JsonObject, FDeviceData& OutDeviceData)
|
|
{
|
|
if (!JsonObject.IsValid())
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetStringField("id", OutDeviceData.ID))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("x", OutDeviceData.X))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("y", OutDeviceData.Y))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("z", OutDeviceData.Z))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("yaw", OutDeviceData.Yaw))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("roll", OutDeviceData.Roll))
|
|
return false;
|
|
|
|
if (!JsonObject->TryGetNumberField("pitch", OutDeviceData.Pitch))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
InitNetwork();
|
|
UUID = FGuid::NewGuid().ToString();
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::Deinitialize()
|
|
{
|
|
CleanupNetwork();
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::InitNetwork()
|
|
{
|
|
// 初始化发送socket
|
|
SenderSocket = FUdpSocketBuilder(TEXT("UDPSenderSocket"))
|
|
.AsReusable()
|
|
.WithBroadcast();
|
|
|
|
// 设置远程端点 (192.168.1.28:1234)
|
|
FIPv4Address::Parse(TEXT("192.168.30.254"), RemoteEndpoint.Address);
|
|
RemoteEndpoint.Port = 1234;
|
|
|
|
// 初始化接收socket
|
|
ReceiverSocket = FUdpSocketBuilder(TEXT("UDPReceiverSocket"))
|
|
.AsReusable()
|
|
.WithBroadcast()
|
|
.BoundToEndpoint(FIPv4Endpoint(FIPv4Address::Any, 9999)) // 监听相同端口
|
|
.WithReceiveBufferSize(65535);
|
|
|
|
// 启动接收线程
|
|
SocketReceiver = new FUdpSocketReceiver(ReceiverSocket, FTimespan::FromMilliseconds(100), TEXT("UDPReceiver"));
|
|
SocketReceiver->OnDataReceived().BindUObject(this, &UUDPInstanceSubsystem::OnDataReceived);
|
|
SocketReceiver->Start();
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::CleanupNetwork()
|
|
{
|
|
// 停止接收线程
|
|
if (SocketReceiver)
|
|
{
|
|
SocketReceiver->Stop();
|
|
delete SocketReceiver;
|
|
SocketReceiver = nullptr;
|
|
}
|
|
|
|
// 关闭接收socket
|
|
if (ReceiverSocket)
|
|
{
|
|
ReceiverSocket->Close();
|
|
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ReceiverSocket);
|
|
ReceiverSocket = nullptr;
|
|
}
|
|
|
|
// 关闭发送socket
|
|
if (SenderSocket)
|
|
{
|
|
SenderSocket->Close();
|
|
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(SenderSocket);
|
|
SenderSocket = nullptr;
|
|
}
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::SetTrackedPawn(APawn* NewPawn)
|
|
{
|
|
TrackedPawn = NewPawn;
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::SendDeviceData(const FDeviceData& DeviceData)
|
|
{
|
|
if (!SenderSocket)
|
|
return;
|
|
|
|
// 将结构体转换为JSON
|
|
TSharedPtr<FJsonObject> JsonObject = DeviceData.ToJsonObject();
|
|
FString JsonString;
|
|
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&JsonString);
|
|
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
|
|
|
|
// 发送数据
|
|
TArray<uint8> Data;
|
|
FTCHARToUTF8 Converter(*JsonString);
|
|
Data.Append((uint8*)Converter.Get(), Converter.Length());
|
|
|
|
int32 BytesSent = 0;
|
|
SenderSocket->SendTo(Data.GetData(), Data.Num(), BytesSent, RemoteEndpoint.ToInternetAddr().Get());
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::SendCurrentPawnData()
|
|
{
|
|
if (!TrackedPawn)
|
|
return;
|
|
|
|
FDeviceData DeviceData;
|
|
/*DeviceData.ID = "PlayerPawn";*/
|
|
DeviceData.ID = UUID;
|
|
UCameraComponent* TargetCamera = TrackedPawn->FindComponentByClass<UCameraComponent>();
|
|
|
|
FVector Location = TargetCamera->GetComponentLocation();
|
|
DeviceData.X = Location.X;
|
|
DeviceData.Y = Location.Y;
|
|
DeviceData.Z = 0;
|
|
|
|
FRotator Rotation = TargetCamera->GetComponentRotation();
|
|
DeviceData.Yaw = Rotation.Yaw;
|
|
DeviceData.Roll = Rotation.Roll;
|
|
DeviceData.Pitch = Rotation.Pitch;
|
|
|
|
/*FVector Location = TrackedPawn->GetActorLocation();
|
|
DeviceData.X = Location.X;
|
|
DeviceData.Y = Location.Y;
|
|
DeviceData.Z = Location.Z;
|
|
|
|
FRotator Rotation = TrackedPawn->GetActorRotation();
|
|
DeviceData.Yaw = Rotation.Yaw;
|
|
DeviceData.Roll = Rotation.Roll;
|
|
DeviceData.Pitch = Rotation.Pitch;*/
|
|
|
|
SendDeviceData(DeviceData);
|
|
}
|
|
|
|
|
|
void UUDPInstanceSubsystem::SpawnActorInGameThread(TSubclassOf<AActor> Actor, const FVector& Location, const FRotator& Rotation, const FString& ID, FOnActorSpawned OnActorSpawned)
|
|
{
|
|
// 强制要求在 GameThread 调用
|
|
if (IsInGameThread())
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("In main thread"));
|
|
return;
|
|
}
|
|
// 记录开始创建
|
|
UE_LOG(LogTemp, Log, TEXT("Actor1111 "));
|
|
|
|
// 使用智能指针或委托机制才能安全地处理异步结果
|
|
AsyncTask(ENamedThreads::GameThread, [this, Location, Rotation, ID, Actor, OnActorSpawned]()
|
|
{
|
|
if (!GetWorld())
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Cannot spawn actor: invalid world"));
|
|
OnActorSpawned.ExecuteIfBound(nullptr, ID);
|
|
return;
|
|
}
|
|
|
|
if (!Actor)
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Cannot spawn actor: invalid actor class"));
|
|
OnActorSpawned.ExecuteIfBound(nullptr, ID);
|
|
return;
|
|
}
|
|
|
|
AActor* NewActor = GetWorld()->SpawnActor<AActor>(Actor, Location, Rotation);
|
|
if (NewActor)
|
|
{
|
|
UE_LOG(LogTemp, Log, TEXT("Actor %s spawned at %s"), ID.Len() ? *ID : TEXT("Unknown"), *Location.ToString());
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("Failed to spawn actor %s"), *ID);
|
|
}
|
|
|
|
OnActorSpawned.ExecuteIfBound(NewActor, ID);
|
|
});
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::OnDataReceived(const FArrayReaderPtr& ArrayReaderPtr, const FIPv4Endpoint& EndPt)
|
|
{
|
|
// 将接收到的二进制数据转换为字符串
|
|
uint8* Data = (uint8*)ArrayReaderPtr->GetData();
|
|
int32 DataSize = ArrayReaderPtr->Num();
|
|
|
|
// 添加字符串结束符
|
|
TArray<uint8> DataWithNull;
|
|
DataWithNull.Append(Data, DataSize);
|
|
DataWithNull.Add(0); // 添加null终止符
|
|
|
|
// 转换为FString
|
|
FString DataString = UTF8_TO_TCHAR(DataWithNull.GetData());
|
|
|
|
ParseReceivedData(DataString);
|
|
|
|
// 在游戏线程中处理数据
|
|
// AsyncTask(ENamedThreads::GameThread, [this, DataString]() {
|
|
//
|
|
// });
|
|
}
|
|
|
|
void UUDPInstanceSubsystem::ParseReceivedData(const FString& DataString)
|
|
{
|
|
//UE_LOG(LogTemp, Warning, TEXT("Received Data: %s"), *DataString);
|
|
|
|
TMap<FString, FDeviceData> DeviceDataMap;
|
|
|
|
TSharedPtr<FJsonObject> JsonObject;
|
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(DataString);
|
|
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
|
|
{
|
|
for (const auto& Pair : JsonObject->Values)
|
|
{
|
|
if (Pair.Value.IsValid() && Pair.Value->Type == EJson::Object)
|
|
{
|
|
FDeviceData DeviceData;
|
|
if (FDeviceData::FromJsonObject(Pair.Value->AsObject(), DeviceData))
|
|
{
|
|
DeviceData.ID = Pair.Key; // Set ID from map key
|
|
DeviceDataMap.Add(Pair.Key, DeviceData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FDeviceDataMap DeviceDataMapStruct;
|
|
DeviceDataMapStruct.ReceivedDataMap = DeviceDataMap;
|
|
AsyncTask(ENamedThreads::GameThread, [&,this, DeviceDataMapStruct]()
|
|
{
|
|
OnDeviceDataReceived.Broadcast(DeviceDataMapStruct);
|
|
});
|
|
} |