WIP: Boostrapping NetSlime

- Just a old name for a set of changes to make the game framework hardened for multiplayer as well as some ease of use functionality.
This commit is contained in:
2024-04-23 01:10:02 -04:00
parent 28b1ad19dc
commit 2574960fff
50 changed files with 2109 additions and 119 deletions

View File

@ -1,2 +1,67 @@
#include "GasaNetLibrary.h"
#include "GasaNetLibrary_Inlines.h"
DEFINE_LOG_CATEGORY(LogGasaNet);
void NetLog( UObject const* Context, FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log
, FLogCategoryBase& Category = LogGasaNet
, bool DumpStack = false
, int32 Line = __builtin_LINE()
, const ANSICHAR* File = __builtin_FILE()
, const ANSICHAR* Func = __builtin_FUNCTION() )
{
#if !UE_BUILD_SHIPPING && !NO_LOGGING
ELogVerbosity::Type EngineVerbosity = (ELogVerbosity::Type) Verbosity;
if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY)
return;
if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > Category.GetVerbosity())
return;
if ( Category.IsSuppressed(EngineVerbosity))
return;
AActor const* Actor = nullptr;
FString ActorLevel;
{
if (Context != nullptr)
{
if (Context->GetClass()->IsChildOf(AActor::StaticClass()))
Actor = Cast<AActor>(Context);
else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
Actor = Cast<UActorComponent>(Context)->GetOwner();
// Its assumed that all GasaObjects have an outer actor
else if (Context->IsA(UGasaObject::StaticClass()))
Actor = Cast<AActor>(Context->GetOuter());
}
if (Actor)
{
if (Actor->HasLocalNetOwner())
ActorLevel = TEXT("Net Owner");
else if (Actor->HasAuthority())
ActorLevel = TEXT("Server Authorized");
else
ActorLevel = TEXT("No Authority");
}
else
ActorLevel = TEXT("Local");
}
FString NetMode = FString::Printf(TEXT("%-16s"), * GetNetworkModeStr(Context));
ActorLevel = FString::Printf(TEXT("%-18s"), * ActorLevel);
FString Name = FString::Printf(TEXT("%-40s"), * Context->GetName());
FString FullMsg = NetMode + " " + ActorLevel + " " + Name + " : " + Message;
static UE::Logging::Private::FStaticBasicLogDynamicData LOG_Dynamic;
static UE::Logging::Private::FStaticBasicLogRecord
LOG_Static(TEXT("%s -- %hs %hs(%d)"), File, Line, EngineVerbosity, LOG_Dynamic);
SET_WARN_COLOR(COLOR_PURPLE)
if (DumpStack)
FDebug::DumpStackTraceToLog(EngineVerbosity);
BasicLog(Category, &LOG_Static, * FullMsg, File, Func, Line);
CLEAR_WARN_COLOR()
#endif
}

View File

@ -1,4 +1,22 @@
#pragma once
// NetSlime: Ol'Reliable
#pragma once
#include "GasaCommon.h"
#define DOREPLIFETIME_DEFAULT_GAS(Class, ReplicatedVar) \
DOREPLIFETIME_CONDITION_NOTIFY(Class, ReplicatedVar, COND_None, REPNOTIFY_Always)
DECLARE_LOG_CATEGORY_EXTERN(LogGasaNet, Log, All);
UENUM(BlueprintType)
enum class ENetworkMode : uint8
{
Standalone,
DedicatedServer,
ListenServer,
Client,
MAX,
};
namespace Gasa
{
@ -12,6 +30,32 @@ namespace Gasa
constexpr float NetCullDist_VeryFar = 10000.0f * 10000.0f;
constexpr float NetCullDist_VisualMax = 15000.0f * 15000.0f;
#define DOREPLIFETIME_DEFAULT_GAS(Class, ReplicatedVar) \
DOREPLIFETIME_CONDITION_NOTIFY(Class, ReplicatedVar, COND_None, REPNOTIFY_Always)
void DrawNetCullingSphere(UObject const* Context, float Duration, float Thickness);
ENetworkMode GetNetworkMode(UObject const* Context);
FString GetNetworkModeStr(UObject const* Context);
bool IsClient(UObject const* Context);
bool IsListenServer(UObject const* Context);
bool IsNetOwner(UObject const* Context);
bool IsNetOwner(UGasaObject const* Context);
bool IsNetOwner(AActor const* Context);
bool IsServer(UObject const* Context);
bool IsSimulatedProxy(UObject const* Context);
bool IsSimulatedProxy(UGasaObject const* Context);
bool IsSimulatedProxy(AActor const* Context);
void NetLog( UObject const* Context, FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log
, FLogCategoryBase& Category = LogGasaNet
, bool DumpStack = false
, int32 Line = __builtin_LINE()
, const ANSICHAR* File = __builtin_FILE()
, const ANSICHAR* Func = __builtin_FUNCTION() );
bool ServerAuthorized(UObject const* Context);
bool ServerAuthorized(UGasaObject const* Context);
bool ServerAuthorized(AActor const* Context);
}

View File

@ -0,0 +1,278 @@
#include "GasaNetLibrary.h"
#include "GasaObject.h"
#include "Engine/NetDriver.h"
#include "Game/GasaGameMode.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/KismetSystemLibrary.h"
namespace Gasa
{
// TODO(Ed): Profile these...
inline
void DrawNetCullingSphere(const UObject* Context, float Duration, float Thickness)
{
const AActor* actor = nullptr;
if (Context->IsA(UGasaObject::StaticClass()))
actor = Cast<AActor>(Context->GetOuter());
else if (Context->IsA(AActor::StaticClass()))
actor = Cast<AActor>(Context);
if (actor)
UKismetSystemLibrary::DrawDebugSphere(actor
, actor->GetActorLocation()
, UKismetMathLibrary::Sqrt(actor->NetCullDistanceSquared) * 2
, 12
, FLinearColor(FColor::Emerald)
, Duration
, Thickness);
}
inline
ENetworkMode GetNetworkMode(UObject const* Context)
{
if (Context == nullptr)
{
Log("Context is null...", ELogV::Error);
return scast(ENetworkMode, ENetMode::NM_MAX);
}
UWorld* World = Context->GetWorld();
if (World == nullptr) {
Log("World is null... are you running in a proper context?", ELogV::Error);
return scast(ENetworkMode, ENetMode::NM_MAX);
}
if (IsValid(World) == false)
return ENetworkMode::Standalone;
ENetworkMode NetMode = scast(ENetworkMode, World->GetNetMode());
return NetMode;
}
inline
FString GetNetworkModeStr(UObject const* Context)
{
FString Str;
if (Context == nullptr)
return Str;
switch (GetNetworkMode(Context))
{
case ENetworkMode::Standalone:
Str = TEXT("Standalone");
break;
case ENetworkMode::ListenServer:
Str = TEXT("ListenServer");
break;
case ENetworkMode::DedicatedServer:
Str = TEXT("DedicatedServer");
break;
case ENetworkMode::Client:
Str = TEXT("Client");
break;
}
return Str;
}
inline
bool IsClient(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
UNetDriver* NetDriver = Context->GetWorld()->NetDriver;
bool Result = NetDriver && ! NetDriver->IsServer();
return Result;
}
inline
bool IsListenServer(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
UNetDriver* NetDriver = Context->GetWorld()->NetDriver;
bool Result = NetDriver && NetDriver->GetNetMode() == ENetMode::NM_ListenServer;
return Result;
}
inline
bool IsNetOwner(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = nullptr;
if (Context->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Context);
else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
Actor = Cast<UActorComponent>(Context)->GetOwner();
// Its assumed that all GasaObjects have an outer actor
else if (Context->IsA(UGasaObject::StaticClass()))
Actor = Cast<AActor>(Context->GetOuter());
else
{
UObject const* Outermost = Context->GetOutermostObject();
if (Outermost->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Outermost);
}
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->HasLocalNetOwner();
return Result;
}
inline
bool IsNetOwner(UGasaObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = Cast<AActor>(Context->GetOuter());
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->HasLocalNetOwner();
return Result;
}
inline
bool IsNetOwner(AActor const* Actor)
{
if (Actor == nullptr || Actor->GetWorld() == nullptr)
return false;
bool Result = Actor->HasLocalNetOwner();
return Result;
}
inline
bool IsServer(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
UNetDriver* NetDriver = Context->GetWorld()->NetDriver;
bool Result = NetDriver && NetDriver->IsServer();
return Result;
}
inline
bool IsSimulatedProxy(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = nullptr;
if (Context->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Context);
else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
Actor = Cast<UActorComponent>(Context)->GetOwner();
// Its assumed that all GasaObjects have an outer actor
else if (Context->IsA(UGasaObject::StaticClass()))
Actor = Cast<AActor>(Context->GetOuter());
else
{
UObject const* Outermost = Context->GetOutermostObject();
if (Outermost->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Outermost);
}
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->GetLocalRole() == ENetRole::ROLE_SimulatedProxy;
return Result;
}
inline
bool IsSimulatedProxy(UGasaObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = Cast<AActor>(Context->GetOuter());
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->GetLocalRole() == ENetRole::ROLE_SimulatedProxy;
return Result;
}
inline
bool IsSimulatedProxy(AActor const* Actor)
{
if (Actor == nullptr || Actor->GetWorld() == nullptr)
return false;
bool Result = Actor->GetLocalRole() == ENetRole::ROLE_SimulatedProxy;
return Result;
}
inline
bool ServerAuthorized(UObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = nullptr;
if (Context->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Context);
else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
Actor = Cast<UActorComponent>(Context)->GetOwner();
// Its assumed that all GasaObjects have an outer actor
else if (Context->IsA(UGasaObject::StaticClass()))
Actor = Cast<AActor>(Context->GetOuter());
else
{
UObject const* Outermost = Context->GetOutermostObject();
if (Outermost->IsA(AActor::StaticClass()))
Actor = Cast<AActor>(Outermost);
}
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->HasAuthority();
return Result;
}
inline
bool ServerAuthorized(UGasaObject const* Context)
{
if (Context == nullptr || Context->GetWorld() == nullptr)
return false;
AActor const* Actor = Cast<AActor>(Context->GetOuter());
if (Actor == nullptr)
{
Log("Could not get actor reference", ELogV::Warning, LogGasaNet);
return false;
}
bool Result = Actor->HasAuthority();
return Result;
}
inline
bool ServerAuthorized(AActor const* Actor)
{
if (Actor == nullptr || Actor->GetWorld() == nullptr)
return false;
bool Result = Actor->HasAuthority();
return Result;
}
}