diff --git a/Source/RSHWNetwork/Private/Logging.cpp b/Source/RSHWNetwork/Private/Logging.cpp new file mode 100644 index 0000000..ef9cbd2 --- /dev/null +++ b/Source/RSHWNetwork/Private/Logging.cpp @@ -0,0 +1,3 @@ +#include "Logging.h" + +DEFINE_LOG_CATEGORY(LogRSHWNetwork); diff --git a/Source/RSHWNetwork/Private/Logging.h b/Source/RSHWNetwork/Private/Logging.h new file mode 100644 index 0000000..19c7c25 --- /dev/null +++ b/Source/RSHWNetwork/Private/Logging.h @@ -0,0 +1,6 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogRSHWNetwork, Log, All); diff --git a/Source/RSHWNetwork/Private/RSHWNetworkClient.cpp b/Source/RSHWNetwork/Private/RSHWNetworkClient.cpp new file mode 100644 index 0000000..1c38dbe --- /dev/null +++ b/Source/RSHWNetwork/Private/RSHWNetworkClient.cpp @@ -0,0 +1,256 @@ +#include "RSHWNetworkClient.h" + +#include "Logging.h" +#include "Sockets.h" +#include "IPAddress.h" +#include "SocketSubsystem.h" + +URSHWNetworkClient::URSHWNetworkClient(const FObjectInitializer & ObjectInitializer) + : ServerAddr(ISocketSubsystem::Get()->CreateInternetAddr()) +{ + ServerAddr->SetLoopbackAddress(); + ServerAddr->SetPort(25565); +} + +bool URSHWNetworkClient::SetHandler(TScriptInterface InHandlerObject) +{ + if (bIsRunning) return false; + HandlerObject = InHandlerObject; + return true; +} + +bool URSHWNetworkClient::SetServerAddr(TSharedRef InServerAddr) +{ + if (bIsRunning) return false; + if (!InServerAddr->IsValid()) return false; + ServerAddr = InServerAddr; + return true; +} + +bool URSHWNetworkClient::SetServerAddrByString(const FString & InServerAddr) +{ + if (bIsRunning) return false; + + TSharedRef NewServerAddr = ISocketSubsystem::Get()->CreateInternetAddr(); + + bool bIsValid; + NewServerAddr->SetPort(ServerAddr->GetPort()); + NewServerAddr->SetIp(*InServerAddr, bIsValid); + + if (bIsValid) + { + ServerAddr = NewServerAddr; + } + else + { + return false; + } + + return true; +} + +bool URSHWNetworkClient::Send(const TArray& Data) +{ + if (!bIsRunning || !(ClientPass.ID | ClientPass.Key)) return false; + + SendBuffer.SetNumUninitialized(8, false); + + SendBuffer[0] = ClientPass.ID >> 0; + SendBuffer[1] = ClientPass.ID >> 8; + SendBuffer[2] = ClientPass.ID >> 16; + SendBuffer[3] = ClientPass.ID >> 24; + + SendBuffer[4] = ClientPass.Key >> 0; + SendBuffer[5] = ClientPass.Key >> 8; + SendBuffer[6] = ClientPass.Key >> 16; + SendBuffer[7] = ClientPass.Key >> 24; + + SendBuffer.Append(Data); + + int32 BytesSend; + return SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *ServerAddr) && BytesSend == SendBuffer.Num(); +} + +void URSHWNetworkClient::Login() +{ + if (bIsRunning) return; + + if (HandlerObject.GetInterface() == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("HandlerObject is nullptr in '%s'."), *GetName()); + return; + } + + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + + if (SocketSubsystem == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket subsystem is nullptr in '%s'."), *GetName()); + HandlerObject->OnLoginFailure(); + return; + } + + SocketPtr = SocketSubsystem->CreateSocket(NAME_DGram, FString::Printf(TEXT("RSHW Client Socket in '%s'."), *GetName())); + + if (SocketPtr == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket creation failed in '%s'."), *GetName()); + HandlerObject->OnLoginFailure(); + return; + } + + if (!SocketPtr->SetNonBlocking()) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket set non-blocking failed in '%s'."), *GetName()); + SocketSubsystem->DestroySocket(SocketPtr); + HandlerObject->OnLoginFailure(); + return; + } + + ClientPass.ID = 0; + ClientPass.Key = 0; + bIsRunning = true; + StartupTime = FDateTime::Now(); + LastRecvTime = StartupTime; + LastHeartbeat = FDateTime::MinValue(); + UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network client '%s' try login."), *GetName()); + +} + +void URSHWNetworkClient::Unlogin() +{ + if (!bIsRunning) return; + + HandlerObject->OnUnlogin(); + + ResetRunningData(); + + UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network client '%s' unlogin."), *GetName()); +} + +void URSHWNetworkClient::ResetRunningData() +{ + bIsRunning = false; + + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + check(SocketSubsystem); + SocketSubsystem->DestroySocket(SocketPtr); + + SendBuffer.SetNum(0); + RecvBuffer.SetNum(0); + DataBuffer.SetNum(0); +} + +void URSHWNetworkClient::BeginDestroy() +{ + Unlogin(); + + Super::BeginDestroy(); +} + +void URSHWNetworkClient::Tick(float DeltaTime) +{ + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + check(SocketSubsystem); + + const FDateTime NowTime = FDateTime::Now(); + + // send heartbeat + { + if (NowTime - LastHeartbeat > FTimespan::FromSeconds(1.0)) + { + SendBuffer.SetNumUninitialized(8, false); + + SendBuffer[0] = ClientPass.ID >> 0; + SendBuffer[1] = ClientPass.ID >> 8; + SendBuffer[2] = ClientPass.ID >> 16; + SendBuffer[3] = ClientPass.ID >> 24; + + SendBuffer[4] = ClientPass.Key >> 0; + SendBuffer[5] = ClientPass.Key >> 8; + SendBuffer[6] = ClientPass.Key >> 16; + SendBuffer[7] = ClientPass.Key >> 24; + + int32 BytesSend; + if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *ServerAddr) && BytesSend == SendBuffer.Num()) + { + LastHeartbeat = NowTime; + } + } + } + + // handle socket recv + { + int32 BytesRead; + TSharedRef SourceAddr = SocketSubsystem->CreateInternetAddr(); + + while (true) { + + RecvBuffer.SetNumUninitialized(65535, false); + + if (!SocketPtr->RecvFrom(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead, *SourceAddr)) break; + + if (BytesRead < 8) continue; + RecvBuffer.SetNumUninitialized(BytesRead, false); + + FRSHWNetworkPass SourcePass; + SourcePass.ID = 0; + SourcePass.Key = 0; + + SourcePass.ID |= (int32)RecvBuffer[0] << 0; + SourcePass.ID |= (int32)RecvBuffer[1] << 8; + SourcePass.ID |= (int32)RecvBuffer[2] << 16; + SourcePass.ID |= (int32)RecvBuffer[3] << 24; + + SourcePass.Key |= (int32)RecvBuffer[4] << 0; + SourcePass.Key |= (int32)RecvBuffer[5] << 8; + SourcePass.Key |= (int32)RecvBuffer[6] << 16; + SourcePass.Key |= (int32)RecvBuffer[7] << 24; + + // is registration request + if (!(ClientPass.ID | ClientPass.Key)) + { + ClientPass = SourcePass; + HandlerObject->OnLogin(); + } + + if (SourcePass.ID == ClientPass.ID && SourcePass.Key == ClientPass.Key) + { + LastRecvTime = NowTime; + } + + // is heartbeat request + if ((SourcePass.ID | SourcePass.Key) && RecvBuffer.Num() == 8) continue; + + // is server request + if (SourcePass.ID == ClientPass.ID && SourcePass.Key == ClientPass.Key) + { + DataBuffer.SetNumUninitialized(RecvBuffer.Num() - 8, false); + FMemory::Memcpy(DataBuffer.GetData(), RecvBuffer.GetData() + 8, RecvBuffer.Num() - 8); + HandlerObject->OnRecv(DataBuffer); + } + } + } + + // handle login timeout + { + if (!(ClientPass.ID | ClientPass.Key) && NowTime - StartupTime > TimeoutLimit) + { + ResetRunningData(); + + UE_LOG(LogRSHWNetwork, Warning, TEXT("RSHW network client '%s' login timeout."), *GetName()); + HandlerObject->OnLoginFailure(); + } + } + + // handle timeout + { + if ((ClientPass.ID | ClientPass.Key) && NowTime - LastRecvTime > TimeoutLimit) + { + ResetRunningData(); + + UE_LOG(LogRSHWNetwork, Warning, TEXT("RSHW network client '%s' timeout."), *GetName()); + HandlerObject->OnUnlogin(); + } + } +} diff --git a/Source/RSHWNetwork/Private/RSHWNetworkServer.cpp b/Source/RSHWNetwork/Private/RSHWNetworkServer.cpp new file mode 100644 index 0000000..e81d8ad --- /dev/null +++ b/Source/RSHWNetwork/Private/RSHWNetworkServer.cpp @@ -0,0 +1,340 @@ +#include "RSHWNetworkServer.h" + +#include "Logging.h" +#include "Sockets.h" +#include "IPAddress.h" +#include "SocketSubsystem.h" +#include "HAL/UnrealMemory.h" + +bool URSHWNetworkServer::SetHandler(TScriptInterface InHandlerObject) +{ + if (bIsRunning) return false; + HandlerObject = InHandlerObject; + return true; +} + +bool URSHWNetworkServer::SetBindPort(int32 InPort) +{ + if (bIsRunning) return false; + Port = InPort; + return true; +} + +bool URSHWNetworkServer::Send(int32 ClientID, const TArray& Data) +{ + if (!bIsRunning || !Registration.Contains(ClientID)) return false; + + const FRegistrationInfo& Info = Registration[ClientID]; + + SendBuffer.SetNumUninitialized(8, false); + + SendBuffer[0] = Info.Pass.ID >> 0; + SendBuffer[1] = Info.Pass.ID >> 8; + SendBuffer[2] = Info.Pass.ID >> 16; + SendBuffer[3] = Info.Pass.ID >> 24; + + SendBuffer[4] = Info.Pass.Key >> 0; + SendBuffer[5] = Info.Pass.Key >> 8; + SendBuffer[6] = Info.Pass.Key >> 16; + SendBuffer[7] = Info.Pass.Key >> 24; + + SendBuffer.Append(Data); + + int32 BytesSend; + return SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Info.Addr) && BytesSend == SendBuffer.Num(); +} + +bool URSHWNetworkServer::RunServer() +{ + if (bIsRunning) return true; + + if (HandlerObject.GetInterface() == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("HandlerObject is nullptr in '%s'."), *GetName()); + return false; + } + + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + + if (SocketSubsystem == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket subsystem is nullptr in '%s'."), *GetName()); + return false; + } + + SocketPtr = SocketSubsystem->CreateSocket(NAME_DGram, FString::Printf(TEXT("RSHW Server Socket in '%s'."), *GetName())); + + if (SocketPtr == nullptr) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket creation failed in '%s'."), *GetName()); + return false; + } + + TSharedRef ServerAddr = SocketSubsystem->CreateInternetAddr(); + + ServerAddr->SetAnyAddress(); + ServerAddr->SetPort(Port); + + if (!SocketPtr->Bind(*ServerAddr)) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket bind failed in '%s'."), *GetName()); + SocketSubsystem->DestroySocket(SocketPtr); + return false; + } + + if (!SocketPtr->SetNonBlocking()) + { + UE_LOG(LogRSHWNetwork, Error, TEXT("Socket set non-blocking failed in '%s'."), *GetName()); + SocketSubsystem->DestroySocket(SocketPtr); + return false; + } + + NextRegistrationID = 1; + + bIsRunning = true; + UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network server '%s' start."), *GetName()); + + return true; +} + +void URSHWNetworkServer::StopServer() +{ + if (!bIsRunning) return; + + TArray RegistrationAddr; + Registration.GetKeys(RegistrationAddr); + + for (int32 ID : RegistrationAddr) + { + HandlerObject->OnUnlogin(ID); + } + + ResetRunningData(); + + UE_LOG(LogRSHWNetwork, Log, TEXT("RSHW network server '%s' stop."), *GetName()); +} + +void URSHWNetworkServer::ResetRunningData() +{ + bIsRunning = false; + + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + check(SocketSubsystem); + SocketSubsystem->DestroySocket(SocketPtr); + + SendBuffer.SetNum(0); + RecvBuffer.SetNum(0); + DataBuffer.SetNum(0); + + PreRegistration.Reset(); + Registration.Reset(); +} + +void URSHWNetworkServer::BeginDestroy() +{ + StopServer(); + + Super::BeginDestroy(); +} + +void URSHWNetworkServer::Tick(float DeltaTime) +{ + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(); + check(SocketSubsystem); + + const FDateTime NowTime = FDateTime::Now(); + + // send heartbeat + { + TArray RegistrationAddr; + Registration.GetKeys(RegistrationAddr); + + for (int32 ID : RegistrationAddr) + { + if (NowTime - Registration[ID].Heartbeat > FTimespan::FromSeconds(1.0)) + { + SendBuffer.SetNum(8, false); + + SendBuffer[0] = Registration[ID].Pass.ID >> 0; + SendBuffer[1] = Registration[ID].Pass.ID >> 8; + SendBuffer[2] = Registration[ID].Pass.ID >> 16; + SendBuffer[3] = Registration[ID].Pass.ID >> 24; + + SendBuffer[4] = Registration[ID].Pass.Key >> 0; + SendBuffer[5] = Registration[ID].Pass.Key >> 8; + SendBuffer[6] = Registration[ID].Pass.Key >> 16; + SendBuffer[7] = Registration[ID].Pass.Key >> 24; + + int32 BytesSend; + if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *Registration[ID].Addr) && BytesSend == SendBuffer.Num()) + { + Registration[ID].Heartbeat = NowTime; + } + } + } + } + + // handle socket recv + { + int32 BytesRead; + TSharedRef SourceAddr = SocketSubsystem->CreateInternetAddr(); + + while (true) { + + RecvBuffer.SetNumUninitialized(65535, false); + + if (!SocketPtr->RecvFrom(RecvBuffer.GetData(), RecvBuffer.Num(), BytesRead, *SourceAddr)) break; + + if (BytesRead < 8) continue; + RecvBuffer.SetNumUninitialized(BytesRead, false); + + FRSHWNetworkPass SourcePass; + SourcePass.ID = 0; + SourcePass.Key = 0; + + SourcePass.ID |= (int32)RecvBuffer[0] << 0; + SourcePass.ID |= (int32)RecvBuffer[1] << 8; + SourcePass.ID |= (int32)RecvBuffer[2] << 16; + SourcePass.ID |= (int32)RecvBuffer[3] << 24; + + SourcePass.Key |= (int32)RecvBuffer[4] << 0; + SourcePass.Key |= (int32)RecvBuffer[5] << 8; + SourcePass.Key |= (int32)RecvBuffer[6] << 16; + SourcePass.Key |= (int32)RecvBuffer[7] << 24; + + FString SourceAddrStr = SourceAddr->ToString(true); + + // is pre-register pass request + if (!(SourcePass.ID | SourcePass.Key)) + { + if (!PreRegistration.Contains(SourceAddrStr)) + { + FPreRegistrationInfo NewRegistration; + NewRegistration.Time = NowTime; + NewRegistration.Pass.ID = NextRegistrationID++; + NewRegistration.Pass.Key ^= FMath::Rand() << 0; + NewRegistration.Pass.Key ^= FMath::Rand() << 8; + NewRegistration.Pass.Key ^= FMath::Rand() << 16; + NewRegistration.Pass.Key ^= FMath::Rand() << 24; + + PreRegistration.Add(SourceAddrStr, NewRegistration); + + UE_LOG(LogRSHWNetwork, Log, TEXT("Pre register pass [ %s - %i:%i ] in '%s'."), *SourceAddrStr, NewRegistration.Pass.ID, NewRegistration.Pass.Key, *GetName()); + } + + const FRSHWNetworkPass& Pass = PreRegistration[SourceAddrStr].Pass; + + SendBuffer.SetNum(8, false); + + SendBuffer[0] = Pass.ID >> 0; + SendBuffer[1] = Pass.ID >> 8; + SendBuffer[2] = Pass.ID >> 16; + SendBuffer[3] = Pass.ID >> 24; + + SendBuffer[4] = Pass.Key >> 0; + SendBuffer[5] = Pass.Key >> 8; + SendBuffer[6] = Pass.Key >> 16; + SendBuffer[7] = Pass.Key >> 24; + + int32 BytesSend; + if (SocketPtr->SendTo(SendBuffer.GetData(), SendBuffer.Num(), BytesSend, *SourceAddr) && BytesSend == SendBuffer.Num()) + { + UE_LOG(LogRSHWNetwork, Log, TEXT("Send pre registration pass [ %s - %i:%i ] in '%s'."), *SourceAddrStr, Pass.ID, Pass.Key, *GetName()); + } + } + else + { + // redirect connection + if (Registration.Contains(SourcePass.ID)) + { + if (!(*Registration[SourcePass.ID].Addr == *SourceAddr)) + { + Registration[SourcePass.ID].Addr = SourceAddr; + + UE_LOG(LogRSHWNetwork, Log, TEXT("Redirect connection [ %i:%i ] in '%s'."), SourcePass.ID, SourcePass.Key, *GetName()); + } + + } + + // register connection + { + bool bIsValidRegistration = false; + if (PreRegistration.Contains(SourceAddrStr)) + { + if (PreRegistration[SourceAddrStr].Pass.ID == SourcePass.ID && PreRegistration[SourceAddrStr].Pass.Key == SourcePass.Key) + { + bIsValidRegistration = true; + } + } + + if (bIsValidRegistration) + { + FRegistrationInfo NewRegistration; + NewRegistration.Pass = SourcePass; + NewRegistration.RecvTime = NowTime; + NewRegistration.Heartbeat = FDateTime::MinValue(); + NewRegistration.Addr = SourceAddr; + + Registration.Add(SourcePass.ID, NewRegistration); + + PreRegistration.Remove(SourceAddrStr); + + HandlerObject->OnLogin(SourcePass.ID); + + UE_LOG(LogRSHWNetwork, Log, TEXT("Register connection [ %i:%i ] in '%s'."), SourcePass.ID, SourcePass.Key, *GetName()); + } + } + } + + if (Registration.Contains(SourcePass.ID)) + { + Registration[SourcePass.ID].RecvTime = NowTime; + } + + // is heartbeat request + if ((SourcePass.ID | SourcePass.Key) && RecvBuffer.Num() == 8) continue; + + // is client request + if (Registration.Contains(SourcePass.ID)) + { + DataBuffer.SetNumUninitialized(RecvBuffer.Num() - 8, false); + FMemory::Memcpy(DataBuffer.GetData(), RecvBuffer.GetData() + 8, RecvBuffer.Num() - 8); + HandlerObject->OnRecv(SourcePass.ID, DataBuffer); + } + } + } + + // handle pre-registration timeout + { + TArray PreRegistrationAddr; + PreRegistration.GetKeys(PreRegistrationAddr); + + for (const FString& Addr : PreRegistrationAddr) + { + if (NowTime - PreRegistration[Addr].Time > TimeoutLimit) + { + UE_LOG(LogRSHWNetwork, Log, TEXT("Pre-registration pass [ %i:%i ] timeout in '%s'."), PreRegistration[Addr].Pass.ID, PreRegistration[Addr].Pass.Key, *GetName()); + + PreRegistration.Remove(Addr); + } + } + } + + // handle running timeout + { + TArray RegistrationAddr; + Registration.GetKeys(RegistrationAddr); + + for (int32 ID : RegistrationAddr) + { + if (NowTime - Registration[ID].RecvTime > TimeoutLimit) + { + UE_LOG(LogRSHWNetwork, Log, TEXT("Registration pass [ %i:%i ] timeout in '%s'."), Registration[ID].Pass.ID, Registration[ID].Pass.Key, *GetName()); + + Registration.Remove(ID); + + HandlerObject->OnUnlogin(ID); + } + } + } +} diff --git a/Source/RSHWNetwork/Private/RSHWNetworkType.cpp b/Source/RSHWNetwork/Private/RSHWNetworkType.cpp new file mode 100644 index 0000000..7bb4703 --- /dev/null +++ b/Source/RSHWNetwork/Private/RSHWNetworkType.cpp @@ -0,0 +1 @@ +#include "RSHWNetworkType.h" diff --git a/Source/RSHWNetwork/Public/RSHWNetworkClient.h b/Source/RSHWNetwork/Public/RSHWNetworkClient.h new file mode 100644 index 0000000..0d4e013 --- /dev/null +++ b/Source/RSHWNetwork/Public/RSHWNetworkClient.h @@ -0,0 +1,105 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Misc/DateTime.h" +#include "UObject/Object.h" +#include "RSHWNetworkType.h" +#include "UObject/Interface.h" +#include "RSHWNetworkClient.generated.h" + +class FInternetAddr; + +UINTERFACE() +class RSHWNETWORK_API URSHWNetworkClientHandler : public UInterface +{ + GENERATED_BODY() +}; + +class RSHWNETWORK_API IRSHWNetworkClientHandler +{ + GENERATED_BODY() + +public: + + virtual void OnLogin() { } + + virtual void OnLoginFailure() { } + + virtual void OnRecv(const TArray& Data) { } + + virtual void OnUnlogin() { } + +}; + +UCLASS() +class RSHWNETWORK_API URSHWNetworkClient : public UObject, public FTickableGameObject +{ + GENERATED_BODY() + +public: + + URSHWNetworkClient(const FObjectInitializer& ObjectInitializer); + + bool IsRunning() const { return bIsRunning; } + + bool SetHandler(TScriptInterface InHandlerObject); + + TScriptInterface GetHandler() const { return HandlerObject; } + + bool SetServerAddr(TSharedRef InServerAddr); + + TSharedRef GetServerAddr() const { return ServerAddr.ToSharedRef(); } + + bool SetServerAddrByString(const FString& InServerAddr = TEXT("127.0.0.1:25565")); + + FString GetServerAddrByString() const { return ServerAddr->ToString(true); } + + bool Send(const TArray& Data); + + void Login(); + + void Unlogin(); + +public: + + FTimespan TimeoutLimit = FTimespan::FromSeconds(8.0); + +private: + + bool bIsRunning = false; + + TSharedPtr ServerAddr; + + UPROPERTY() + TScriptInterface HandlerObject; + +private: + + FSocket* SocketPtr; + + TArray SendBuffer; + TArray RecvBuffer; + TArray DataBuffer; + + FDateTime StartupTime; + + FRSHWNetworkPass ClientPass; + + FDateTime LastRecvTime; + FDateTime LastHeartbeat; + + void ResetRunningData(); + +private: + + //~ Begin UObject Interface + virtual void BeginDestroy() override; + //~ End UObject Interface + + //~ Begin FTickableGameObject Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickable() const override { return !IsTemplate() && bIsRunning; } + virtual TStatId GetStatId() const override { return GetStatID(); } + //~ End FTickableGameObject Interface + +}; diff --git a/Source/RSHWNetwork/Public/RSHWNetworkServer.h b/Source/RSHWNetwork/Public/RSHWNetworkServer.h new file mode 100644 index 0000000..dc27bf6 --- /dev/null +++ b/Source/RSHWNetwork/Public/RSHWNetworkServer.h @@ -0,0 +1,109 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "RSHWNetworkType.h" +#include "UObject/Interface.h" +#include "RSHWNetworkServer.generated.h" + +class FSocket; + +UINTERFACE() +class RSHWNETWORK_API URSHWNetworkServerHandler : public UInterface +{ + GENERATED_BODY() +}; + +class RSHWNETWORK_API IRSHWNetworkServerHandler +{ + GENERATED_BODY() + +public: + + virtual void OnLogin(int32 ClientID) { } + + virtual void OnRecv(int32 ClientID, const TArray& Data) { } + + virtual void OnUnlogin(int32 ClientID) { } + +}; + +UCLASS() +class RSHWNETWORK_API URSHWNetworkServer : public UObject, public FTickableGameObject +{ + GENERATED_BODY() + +public: + + bool IsRunning() const { return bIsRunning; } + + bool SetHandler(TScriptInterface InHandlerObject); + + TScriptInterface GetHandler() const { return HandlerObject; } + + bool SetBindPort(int32 InPort = 25565); + + int32 GetBindPort() const { return Port; } + + bool Send(int32 ClientID, const TArray& Data); + + bool RunServer(); + + void StopServer(); + +public: + + FTimespan TimeoutLimit = FTimespan::FromSeconds(8.0); + +private: + + bool bIsRunning = false; + + int32 Port = 25565; + + UPROPERTY() + TScriptInterface HandlerObject; + +private: + + FSocket* SocketPtr; + + TArray SendBuffer; + TArray RecvBuffer; + TArray DataBuffer; + + int32 NextRegistrationID; + + struct FPreRegistrationInfo + { + FDateTime Time; + FRSHWNetworkPass Pass; + }; + + TMap PreRegistration; + + struct FRegistrationInfo + { + FRSHWNetworkPass Pass; + FDateTime RecvTime; + FDateTime Heartbeat; + TSharedPtr Addr; + }; + + TMap Registration; + + void ResetRunningData(); + +private: + + //~ Begin UObject Interface + virtual void BeginDestroy() override; + //~ End UObject Interface + + //~ Begin FTickableGameObject Interface + virtual void Tick(float DeltaTime) override; + virtual bool IsTickable() const override { return !IsTemplate() && bIsRunning; } + virtual TStatId GetStatId() const override { return GetStatID(); } + //~ End FTickableGameObject Interface + +}; diff --git a/Source/RSHWNetwork/Public/RSHWNetworkType.h b/Source/RSHWNetwork/Public/RSHWNetworkType.h new file mode 100644 index 0000000..cec5989 --- /dev/null +++ b/Source/RSHWNetwork/Public/RSHWNetworkType.h @@ -0,0 +1,9 @@ +#pragma once + +#include "CoreMinimal.h" + +struct FRSHWNetworkPass +{ + int32 ID; + int32 Key; +}; diff --git a/Source/RSHWNetwork/RSHWNetwork.Build.cs b/Source/RSHWNetwork/RSHWNetwork.Build.cs index 9e7ac51..b505f08 100644 --- a/Source/RSHWNetwork/RSHWNetwork.Build.cs +++ b/Source/RSHWNetwork/RSHWNetwork.Build.cs @@ -38,6 +38,8 @@ public class RSHWNetwork : ModuleRules "Engine", "Slate", "SlateCore", + "Networking", + "Sockets", // ... add private dependencies that you statically link with here ... } );