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 cc1636b687
commit ad41867dc5
55 changed files with 2122 additions and 123 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -301,10 +301,6 @@
"Name": "VisualStudioSourceCodeAccess",
"Enabled": true
},
{
"Name": "GitSourceControl",
"Enabled": true
},
{
"Name": "SlateInsights",
"Enabled": true

View File

@@ -1,3 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<bUsePCHFiles>false</bUsePCHFiles>
</Configuration>

View File

@@ -8,34 +8,31 @@
UGasaAttributeSet::UGasaAttributeSet()
{
InitHealth( 50.f );
InitHealth( 100.f );
InitMaxHealth( 100.f );
InitMana( 50.f );
InitMaxMana( 50.f );
}
#pragma region Rep Notifies
#pragma region Rep Notifies
void UGasaAttributeSet::Client_OnRep_Health( FGameplayAttributeData& PrevHealth )
{
// From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, Health ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), Health, PrevHealth );
}
void UGasaAttributeSet::Client_OnRep_MaxHealth( FGameplayAttributeData& PrevMaxHealth )
{
// From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, MaxHealth ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), MaxHealth, PrevMaxHealth );
}
void UGasaAttributeSet::Client_OnRep_Mana( FGameplayAttributeData& PrevMana )
{
// From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, Mana ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), Mana, PrevMana );
}
void UGasaAttributeSet::Client_OnRep_MaxMana( FGameplayAttributeData& PrevMaxMana )
{
// From GAMEPLAYATTRIBUTE_REPNOTIFY
@@ -43,6 +40,7 @@ void UGasaAttributeSet::Client_OnRep_MaxMana( FGameplayAttributeData& PrevMaxMan
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), MaxMana, PrevMaxMana );
}
#pragma endregion Rep Notifies
void UGasaAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const
{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );

View File

@@ -23,6 +23,7 @@ public:
UGasaAttributeSet();
UFUNCTION()
void Client_OnRep_Health( FGameplayAttributeData& PrevHealth );
UFUNCTION()

View File

@@ -4,6 +4,7 @@
#include "GasaAttributeSet.h"
#include "AbilitySystemComponent.h"
#pragma region Attribute Setters
FORCEINLINE
void UGasaAttributeSet::SetHealth( float NewVal )
{
@@ -40,6 +41,7 @@ void UGasaAttributeSet::SetMaxMana( float NewVal )
AbilityComp->SetNumericAttributeBase( GetMaxManaAttribute(), NewVal );
};
}
#pragma endregion Attribute Setters
namespace Gasa
{

View File

@@ -10,13 +10,13 @@ AGasaEffectActor::AGasaEffectActor()
RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
}
void AGasaEffectActor::ApplyEffectToTarget(AActor* Target, TSubclassOf<UGameplayEffect> EffectClass)
void AGasaEffectActor::ApplyEffectToActor(AActor* Actor, TSubclassOf<UGameplayEffect> EffectClass)
{
UGasaAbilitySystemComp* AS = GetAbilitySystem(Target, true);
UGasaAbilitySystemComp* AS = GetAbilitySystem(Actor, true);
FGameplayEffectContextHandle
Context = AS->MakeEffectContext();
Context.AddSourceObject(Target);
Context.AddSourceObject(Actor);
FGameplayEffectSpecHandle Spec = AS->MakeOutgoingSpec( EffectClass, 1.0f, Context );
AS->ApplyGameplayEffectSpecToSelf( * Spec.Data );

View File

@@ -1,19 +1,23 @@
#pragma once
#include "GasaCommon.h"
#include "Actors/GasaActor.h"
#include "GameFramework/Actor.h"
#include "GasaEffectActor.generated.h"
UCLASS()
class GASA_API AGasaEffectActor : public AActor
class GASA_API AGasaEffectActor : public AGasaActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Applied Effects")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gameplay Effects")
TSoftClassPtr<UGameplayEffect> InstantEffectClass;
AGasaEffectActor();
void ApplyEffectToTarget(AActor* Target, TSubclassOf<UGameplayEffect> EffectClass );
UFUNCTION(BlueprintCallable, Category = "Gameplay Effects")
void ApplyEffectToActor(AActor* Actor, TSubclassOf<UGameplayEffect> EffectClass );
};

View File

@@ -4,6 +4,7 @@
#include "GasaAttributeSet.h"
#include "GasaAttributeSet_Inlines.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
AGasaEffectActorDemo::AGasaEffectActorDemo()

View File

@@ -1,12 +1,14 @@
#pragma once
#include "GasaCommon.h"
#include "Actors/GasaActor.h"
#include "GameFramework/Actor.h"
#include "GasaEffectActorDemo.generated.h"
// Old demonstration code used before part 37.
UCLASS()
class GASA_API AGasaEffectActorDemo : public AActor
class GASA_API AGasaEffectActorDemo : public AGasaActor
{
GENERATED_BODY()
public:

View File

@@ -1,10 +1,12 @@
#pragma once
#include "GasaActor.h"
#include "GasaCommon.h"
#include "GameFramework/Actor.h"
#include "CameraMount.generated.h"
UCLASS(Blueprintable)
class GASA_API ACameraMount : public AActor
class GASA_API ACameraMount : public AGasaActor
{
GENERATED_BODY()
public:

View File

View File

@@ -0,0 +1,33 @@
#pragma once
#include "Networking/GasaNetLibrary.h"
#include "GameFramework/Actor.h"
#include "GasaActor.generated.h"
UCLASS()
class GASA_API AGasaActor : public AActor
{
GENERATED_BODY()
public:
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
};

View File

@@ -9,7 +9,10 @@
#include "AbilitySystem/GasaAbilitySystemComponent.h"
#include "AbilitySystem/GasaAttributeSet.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/PostProcessVolume.h"
#include "Game/GasaLevelScriptActor.h"
#include "Materials/MaterialInstanceDynamic.h"
void AGasaCharacter::SetHighlight(EHighlight Desired)
{

View File

@@ -5,6 +5,7 @@
#include "GasaCommon.h"
#include "Game/GasaPlayerState.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaCharacter.generated.h"
@@ -59,6 +60,28 @@ public:
AGasaCharacter();
FORCEINLINE AGasaPlayerState* GetGasaPlayerState() { return GetPlayerState<AGasaPlayerState>(); }
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region IAbilitySystem
FORCEINLINE UAttributeSet* GetAttributes() { return Attributes; }

View File

@@ -25,10 +25,13 @@ void APlayerCharacter::PossessedBy(AController* NewController)
AbilitySystem->InitAbilityActorInfo(PS, this);
}
AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitHostWidget(& Data);
if (IsLocallyControlled())
{
AGasaPlayerController* PC = GetController<AGasaPlayerController>();
AGasaHUD* HUD = PC->GetHUD<AGasaHUD>();
FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
HUD->InitHostWidget(& Data);
}
}
// TODO(Ed): We need to setup Net Slime...

View File

@@ -1,5 +1,84 @@
#include "GasaGameInstance.h"
#include "Engine/NetDriver.h"
#include "Engine/World.h"
using namespace Gasa;
#pragma region GameFramework
// TODO(Ed): Make a NetLog
void UGasaGameInstance::NotifyGameFrameworkClassReady(EGameFrameworkClassFlag ClassReady)
{
switch (ClassReady)
{
case EGameFrameworkClassFlag::GameMode:
GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::GameMode;
NetLog("Gameplay Framework class ready: Game State", ELogV::Log, LogGasaNet );
break;
case EGameFrameworkClassFlag::GameState:
GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::GameState;
NetLog("Gameplay Framework class ready: Game State", ELogV::Log, LogGasaNet );
break;
case EGameFrameworkClassFlag::PlayerController:
GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::PlayerController;
NetLog("Gameplay Framework class ready: Player Controller", ELogV::Log, LogGasaNet);
break;
case EGameFrameworkClassFlag::PlayerState:
GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::PlayerState;
NetLog("Gameplay Framework class ready: Player State", ELogV::Log, LogGasaNet);
break;
case EGameFrameworkClassFlag::Levels:
GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::Levels;
NetLog("Gameplay Framework class ready: Levels", ELogV::Log, LogGasaNet);
break;
}
ProcessGameFrameworkState();
}
void UGasaGameInstance::ProcessGameFrameworkState()
{
switch (GameFrameworkState)
{
case EGameFrameworkState::Uninitialized:
{
uint32 InitializedFlags =
(uint32)EGameFrameworkClassFlag::GameState |
(uint32)EGameFrameworkClassFlag::PlayerController |
(uint32)EGameFrameworkClassFlag::PlayerState |
(uint32)EGameFrameworkClassFlag::Levels
;
if (GetWorld()->NetDriver == nullptr || GetWorld()->NetDriver->IsServer())
{
InitializedFlags |= (uint32)EGameFrameworkClassFlag::GameMode;
}
FString MsgGM = "GameMode : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::GameMode )) );
FString MsgGS = "GameState : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::GameState )) );
FString MsgPC = "PlayerController: " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::PlayerController ) ));
FString MsgPS = "PlayerState : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::PlayerState ) ));
FString MsgL = "Levels : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::Levels ) ));
NetLog(MsgGM, ELogV::Log, LogGasaNet);
NetLog(MsgGS, ELogV::Log, LogGasaNet);
NetLog(MsgPC, ELogV::Log, LogGasaNet);
NetLog(MsgPS, ELogV::Log, LogGasaNet);
NetLog(MsgL, ELogV::Log, LogGasaNet);
if (GameFrameworkClassesState == InitializedFlags)
{
GameFrameworkState = EGameFrameworkState::Initialized;
NetLog("Gameplay Framework initialized");
Event_OnGameFrameworkInitialized.Broadcast();
}
break;
}
}
}
#pragma endregion GameFramework
#pragma region GameInstance
void UGasaGameInstance::Init()
{
Super::Init();
@@ -7,5 +86,6 @@ void UGasaGameInstance::Init()
DevOptionsCache.CachedDevOptions();
using namespace Gasa;
Log(FString::Printf(TEXT("UObject Size: %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize ));
NetLog(FString::Printf(TEXT("UObject Size: %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize ));
}
#pragma region GameInstance

View File

@@ -1,10 +1,32 @@
#pragma once
#include "GasaCommon.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaDevOptionsCache.h"
#include "Engine/Engine.h"
#include "Engine/GameInstance.h"
#include "GasaGameInstance.generated.h"
UENUM(BlueprintType)
enum class EGameFrameworkClassFlag : uint8
{
None = 0 UMETA(Hidden),
GameMode = 1 << 0,
GameState = 1 << 1,
PlayerController = 1 << 2,
PlayerState = 1 << 3,
Levels = 1 << 4
};
UENUM(BlueprintType)
enum class EGameFrameworkState : uint8
{
Initialized,
Uninitialized
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameFrameworkInitializedSig);
UCLASS(Blueprintable)
class GASA_API UGasaGameInstance : public UGameInstance
{
@@ -14,6 +36,55 @@ public:
UPROPERTY(VisibleAnywhere, Category="Dev Cache")
FGasaDevOptionsCache DevOptionsCache;
#pragma region GameFramework
UPROPERTY(BlueprintAssignable, Category = "GameFramework")
FOnGameFrameworkInitializedSig Event_OnGameFrameworkInitialized;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameFramework")
EGameFrameworkState GameFrameworkState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameFramework", meta=(Bitmask, BitmaskEnum = EGameFrameworkClassFlag))
int32 GameFrameworkClassesState;
UFUNCTION(BlueprintCallable, Category="GameFramework")
void ClearGameplayFrameworkState() {
Gasa::Log("Clearing game framework state", EGasaVerbosity::Log, LogGasaNet ); // TODO(Ed): Make a default NetLog
GameFrameworkClassesState = scast(int32, EGameFrameworkClassFlag::None);
GameFrameworkState = EGameFrameworkState::Uninitialized;
}
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameFramework")
FORCEINLINE bool IsGameFrameworkInitialized() { return GameFrameworkState == EGameFrameworkState::Initialized; }
UFUNCTION(BlueprintCallable, Category="GameFramework")
void NotifyGameFrameworkClassReady(EGameFrameworkClassFlag ClassReady);
UFUNCTION(BlueprintCallable, Category = "GameFramework", meta=(BlueprintProtected))
void ProcessGameFrameworkState();
#pragma endregion GameFramework
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region GameInstance
void Init() override;
#pragma endregion GameInstance

View File

@@ -1,2 +1,454 @@
#include "GasaGameMode.h"
#include "GasaGameInstance.h"
#include "GasaGameState.h"
#include "GasaPlayerController.h"
#include "GasaPlayerState.h"
#include "Engine/Player.h"
#include "GameFramework/GameSession.h"
#include "GameFramework/GameState.h"
#include "GameFramework/PlayerState.h"
#include "OnlineSubsystem.h"
using namespace Gasa;
#pragma region Game Framework
void AGasaGameMode::OnGameFrameworkInitialized()
{
NetLog("OnGameFrameworkInitialized");
BP_OnGameFrameworkInitialized();
if (MatchState == MatchState::WaitingToStart && ReadyToStartMatch())
StartMatch();
}
void AGasaGameMode::OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC)
{
NetLog("OwningClient_OnGameFrameworkInitialized");
HandleStartingNewPlayer(PC);
}
#pragma endregion Game Framework
#pragma region GameMode
void AGasaGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
{
Super::HandleStartingNewPlayer_Implementation(NewPlayer);
if (NewPlayer)
NewPlayer->bBlockInput = false;
}
bool AGasaGameMode::ReadyToStartMatch_Implementation()
{
if ( ! GetGameInstance<UGasaGameInstance>()->IsGameFrameworkInitialized())
{
return false;
}
// Super::ReadyToStartMatch();
{
// If bDelayed Start is set, wait for a manual match start
if (bDelayedStart)
{
return false;
}
// By default start when we have > 0 players
if (GetMatchState() == MatchState::WaitingToStart)
{
if (NumPlayers + NumBots > 0)
{
return true;
}
}
return false;
}
}
#pragma endregion GameMode
#pragma region GameModeBase
void AGasaGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
NetLog("EndPlay");
}
void AGasaGameMode::GenericPlayerInitialization(AController* C)
{
NetLog("GenericPlayerInitialization: " + C->GetName());
// AGameMode::GenericPlayerInitialization(C);
{
APlayerController* PC = Cast<APlayerController>(C);
if (PC != nullptr)
{
// Moved to: void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn)
// InitializeHUDForPlayer(Cast<APlayerController>(C));
// Notify the game that we can now be muted and mute others
UpdateGameplayMuteList(PC);
if (GameSession != nullptr)
{
// Tell the player to enable voice by default or use the push to talk method
PC->ClientEnableNetworkVoice(!GameSession->RequiresPushToTalk());
}
ReplicateStreamingStatus(PC);
bool HidePlayer = false, HideHUD = false, DisableMovement = false, DisableTurning = false;
// Check to see if we should start in cinematic mode (matinee movie capture)
if (ShouldStartInCinematicMode(PC, HidePlayer, HideHUD, DisableMovement, DisableTurning))
{
PC->SetCinematicMode(true, HidePlayer, HideHUD, DisableMovement, DisableTurning);
}
}
}
}
TSubclassOf<APlayerController> AGasaGameMode::GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController)
{
NetLog("GetPlayerControllerClassToSpawnForSeamlessTravel: " + PreviousPlayerController->GetName());
return Super::GetPlayerControllerClassToSpawnForSeamlessTravel(PreviousPlayerController);
}
void AGasaGameMode::HandleSeamlessTravelPlayer(AController*& Controller)
{
NetLog("HandleSeamlessTravelPlayer: " + Controller->GetName());
// Super::HandleSeamlessTravelPlayer( C );
UE_LOG(LogGameMode, Log, TEXT(">> GameMode::HandleSeamlessTravelPlayer: %s "), * Controller->GetName());
APlayerController* PC = Cast<APlayerController>(Controller);
UClass* PCClassToSpawn = GetPlayerControllerClassToSpawnForSeamlessTravel(PC);
// FUniqueNetIdRepl StoredNativePlatformUniqueNetId = C->GetPlayerState<AGasaPlayerState>()->NativePlatformUniqueNetId;
if (PC && PC->GetClass() != PCClassToSpawn)
{
if (PC->Player != nullptr)
{
// We need to spawn a new PlayerController to replace the old one
APlayerController* const NewPC = SpawnPlayerControllerCommon(PC->IsLocalPlayerController()
? ROLE_SimulatedProxy
: ROLE_AutonomousProxy, PC->GetFocalLocation(), PC->GetControlRotation(), PCClassToSpawn);
if (NewPC == nullptr)
{
NetLog(FString::Printf(
TEXT("Failed to spawn new PlayerController for %s (old class %s)"), *PC->GetHumanReadableName(), *PC->GetClass()->GetName())
, ELogV::Warning
);
PC->Destroy();
return;
}
else
{
PC->SeamlessTravelTo(NewPC);
NewPC->SeamlessTravelFrom(PC);
SwapPlayerControllers(PC, NewPC);
PC = NewPC;
Controller = NewPC;
}
}
else
{
PC->Destroy();
}
}
else
{
// clear out data that was only for the previous game
Controller->PlayerState->Reset();
// create a new PlayerState and copy over info; this is necessary because the old GameMode may have used a different PlayerState class
APlayerState* OldPlayerState = Controller->PlayerState;
Controller->InitPlayerState();
OldPlayerState->SeamlessTravelTo( Controller->PlayerState);
// we don"t need the old PlayerState anymore
//@fixme: need a way to replace PlayerStates that doesn't cause incorrect "player left the game"/"player entered the game" messages
OldPlayerState->Destroy();
}
InitSeamlessTravelPlayer(Controller);
// Initialize hud and other player details, shared with PostLogin
GenericPlayerInitialization(Controller);
if (AGasaGameState* GS = GetGameState<AGasaGameState>())
{
#if 0
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
int32 NumConnections = GI->SessionSettings.bPublicGame
? GI->SessionSettings.PublicConnections
: GI->SessionSettings.PrivateConnections;
if (GS->OnlinePlayers.Num() < NumConnections)
GS->OnlinePlayers.Init( nullptr, NumConnections );
for (AGasaPlayerState* & PS : GS->OnlinePlayers)
{
if (PS == nullptr)
{
PS = C->GetPlayerState<ASlipgatePlayerState>();
PS->SgID = GS->OnlinePlayers.Find( PS );
break;
}
}
#endif
}
// Controller->GetPlayerState<AGasaPlayerState>()->NativePlatformUniqueNetId = StoredNativePlatformUniqueNetId;
NetLog(FString::Printf(TEXT("HandleSeamlessTravelPlayer: %s"), * Controller->GetName()) );
#if 0
if (PC)
PC->Event_NetOwner_OnGameplayFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized);
#endif
}
void AGasaGameMode::InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer)
{
// Super::InitializeHUDForPlayer_Implementation(NewPlayer);
NewPlayer->ClientSetHUD(HUDClass);
}
void AGasaGameMode::InitSeamlessTravelPlayer(AController* NewController)
{
if (NewController)
NewController->bBlockInput = true;
// GameMode::InitSeamlessTravelPlayer(NewController);
{
// AGameModeBase::InitSeamlessTravelPlayer(NewController);
{
APlayerController* NewPC = Cast<APlayerController>(NewController);
FString ErrorMessage;
if (!UpdatePlayerStartSpot(NewController, TEXT(""), ErrorMessage))
NetLog(FString::Printf( TEXT("InitSeamlessTravelPlayer: %s"), *ErrorMessage), ELogV::Warning);
if (NewPC != nullptr)
{
NewPC->PostSeamlessTravel();
if (MustSpectate(NewPC))
{
NewPC->StartSpectatingOnly();
}
else
{
NewPC->bPlayerIsWaiting = true;
NewPC->ChangeState(NAME_Spectating);
NewPC->ClientGotoState(NAME_Spectating);
}
}
}
APlayerController* NewPC = Cast<APlayerController>(NewController);
if (NewPC != nullptr)
{
SetSeamlessTravelViewTarget(NewPC);
if (!MustSpectate(NewPC))
{
NumPlayers++;
NumTravellingPlayers--;
}
}
else
{
NumBots++;
}
}
NetLog("InitSeamlessTravelPlayer: " + NewController->GetName());
}
void AGasaGameMode::HandleMatchAborted()
{
Super::HandleMatchAborted();
NetLog("HandleMatchAborted");
}
void AGasaGameMode::Logout(AController* Exiting)
{
Super::Logout(Exiting);
Event_OnLogout.Broadcast(Cast<AGasaPlayerController>(Exiting));
NetLog("User Logged out: " + Exiting->GetName());
if (AGasaGameState* GS = Cast<AGasaGameState>(GetWorld()->GetGameState()))
{
#if 0
int32 Index = GS->OnlinePlayers.Find(Exiting->GetPlayerState<AGasaGameState>());
if (Index == INDEX_NONE)
{
NetLog("Could not find exiting player state in online players!", ELogV::Error);
return;
}
GS->OnlinePlayers[Index] = nullptr;
#endif
#if 0
IOnlineSessionPtr const SessionInterface = Online::GetSessionInterface(GetWorld());
if ( ! SessionInterface.IsValid())
{
NetLog("DestoryCurrentSession: Could not get the session interface.", ELogV::Warning);
return;
}
#endif
}
}
void AGasaGameMode::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
{
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
// TODO(Ed): Refuse players if the server is full.
}
void AGasaGameMode::PostLogin(APlayerController* NewPlayer)
{
// AGameMode::PostLogin(NewPlayer);
{
UWorld* World = GetWorld();
// Update player count
if (MustSpectate(NewPlayer))
NumSpectators++;
else if (World->IsInSeamlessTravel() || NewPlayer->HasClientLoadedCurrentWorld())
NumPlayers++;
else
NumTravellingPlayers++;
// Save network address for re-associating with reconnecting player, after stripping out port number
FString Address = NewPlayer->GetPlayerNetworkAddress();
int32 Pos = Address.Find(TEXT(":"), ESearchCase::CaseSensitive);
NewPlayer->PlayerState->SavedNetworkAddress = (Pos > 0) ? Address.Left(Pos) : Address;
// Check if this player is reconnecting and already has PlayerState
FindInactivePlayer(NewPlayer);
}
// AGameModeBase::PostLogin(NewPlayer)
{
// Runs shared initialization that can happen during seamless travel as well
GenericPlayerInitialization(NewPlayer);
// Perform initialization that only happens on initially joining a server
NewPlayer->ClientCapBandwidth(NewPlayer->Player->CurrentNetSpeed);
if (MustSpectate(NewPlayer))
{
NewPlayer->ClientGotoState(NAME_Spectating);
}
else
{
// If NewPlayer is not only a spectator and has a valid ID, add him as a user to the replay.
FUniqueNetIdRepl const&
NewPlayerStateUniqueId = NewPlayer->PlayerState->GetUniqueId();
if (NewPlayerStateUniqueId.IsValid())
{
GetGameInstance()->AddUserToReplay(NewPlayerStateUniqueId.ToString());
}
}
if (GameSession)
{
GameSession->PostLogin(NewPlayer);
}
}
if (AGasaGameState* GS = GetGameState<AGasaGameState>())
{
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
// int32 numconnections = gi->sessionsettings.bpublicgame
// ? GI->SessionSettings.PublicConnections : GI->SessionSettings.PrivateConnections;
#if 0
if (GS->OnlinePlayers.Num() < NumConnections)
{
GS->OnlinePlayers.Init( nullptr, NumConnections );
}
for (AGasaPlayerState* & PS : GS->OnlinePlayers)
{
if (PS == nullptr)
{
PS = NewPlayer->GetPlayerState<AGasaPlayerState>();
PS->GasaID = GS->OnlinePlayers.Find( PS );
break;
}
}
#endif
}
// cont. AGameModeBase::PostLogin(NewPlayer)
{
// Notify Blueprints that a new player has logged in. Calling it here, because this is the first time that the PlayerController can take RPCs
DispatchPostLogin(NewPlayer);
}
AGasaPlayerController* PC = Cast<AGasaPlayerController>(NewPlayer);
// if (PC)
// PC->Event_OnNetOwner_GameplayFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized);
}
void AGasaGameMode::PostSeamlessTravel()
{
Super::PostSeamlessTravel();
NetLog("PostSeamlessTravel");
}
void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn)
{
InitializeHUDForPlayer(Cast<APlayerController>(PlayerPawn->GetController()));
// Super::SetPlayerDefaults(PlayerPawn);
{
PlayerPawn->SetPlayerDefaults();
}
}
void AGasaGameMode::SetSeamlessTravelViewTarget(APlayerController* PC)
{
Super::SetSeamlessTravelViewTarget(PC);
NetLog("SetSeamlessTravelViewTarget");
}
void AGasaGameMode::StartPlay()
{
if (MatchState == MatchState::EnteringMap)
SetMatchState(MatchState::WaitingToStart);
// Start match is deferred until the framework is considered initialized.
NetLog("StartPlay");
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
GI->Event_OnGameFrameworkInitialized.AddDynamic(this, &ThisClass::OnGameFrameworkInitialized);
GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::GameMode);
// Not called yet, will wait for initialization of the framework.
//Super::StartPlay();
}
void AGasaGameMode::StartToLeaveMap()
{
NetLog("StartToLeaveMap");
Super::StartToLeaveMap();
}
void AGasaGameMode::SwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC)
{
NetLog("SwapPlayerControllers");
NetLog("Old: " + OldPC->GetName());
NetLog("New: " + NewPC->GetName());
Super::SwapPlayerControllers(OldPC, NewPC);
}
APlayerController* AGasaGameMode::SpawnPlayerControllerCommon(ENetRole InRemoteRole, FVector const& SpawnLocation, FRotator const& SpawnRotation,
TSubclassOf<APlayerController> InPlayerControllerClass)
{
NetLog("SpawnPlayerControllerCommon");
return Super::SpawnPlayerControllerCommon(InRemoteRole, SpawnLocation, SpawnRotation, InPlayerControllerClass);
}
#pragma endregion GameModeBase

View File

@@ -1,28 +1,102 @@
#pragma once
#pragma once
#include "GameFramework/GameMode.h"
#include "GasaCommon.h"
#include "Networking/GasaNetLibrary_Inlines.h"
#include "Engine/Engine.h"
#include "GasaGameMode.generated.h"
UCLASS(Blueprintable)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLogoutSig, AGasaPlayerController*, PC);
UCLASS( Blueprintable )
class GASA_API AGasaGameMode : public AGameMode
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintAssignable)
FOnLogoutSig Event_OnLogout;
#pragma region GameFramework
UFUNCTION()
void OnGameFrameworkInitialized();
UFUNCTION()
void OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC);
UFUNCTION(BlueprintCallable, meta=(DisplayName = "On Game Framework Initialized"))
void BP_OnGameFrameworkInitialized();
#pragma endregion GameFramework
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region GameMode
void HandleMatchAborted() override;
void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) override;
bool ReadyToStartMatch_Implementation() override;
#pragma endregion GameMode
#pragma region GameModeBase
void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
void GenericPlayerInitialization(AController* C) override;
TSubclassOf<APlayerController> GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController) override;
void HandleSeamlessTravelPlayer(AController*& C) override;
void InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer) override;
void InitSeamlessTravelPlayer(AController* NewController) override;
void Logout(AController* Exiting) override;
void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
void PostLogin(APlayerController* NewPlayer) override;
void PostSeamlessTravel() override;
void SetPlayerDefaults(APawn* PlayerPawn) override;
void SetSeamlessTravelViewTarget(APlayerController* PC) override;
void StartPlay() override;
void StartToLeaveMap() override;
void SwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC) override;
APlayerController* SpawnPlayerControllerCommon(ENetRole InRemoteRole, FVector const& SpawnLocation, FRotator const& SpawnRotation, TSubclassOf<APlayerController> InPlayerControllerClass) override;
#pragma endregion GameModeBase
};
namespace Gasa
{
inline
AGasaGameMode* GetGameMode(UObject* Context)
inline AGasaGameMode* GetGameMode( UObject* Context )
{
UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
UWorld* World = GEngine->GetWorldFromContextObject( Context, EGetWorldErrorMode::LogAndReturnNull );
if ( World == nullptr )
{
Log("World is null... are you running in a proper context?", ELogV::Error);
Log( "World is null... are you running in a proper context?", ELogV::Error );
return nullptr;
}
return Cast<AGasaGameMode>(World->GetAuthGameMode());
return Cast<AGasaGameMode>( World->GetAuthGameMode() );
}
}

View File

@@ -2,6 +2,10 @@
#include "CogAll.h"
#include "CogWindowManager.h"
#include "GasaPlayerState.h"
#include "GasaGameInstance.h"
#include "Net/UnrealNetwork.h"
using namespace Gasa;
AGasaGameState::AGasaGameState()
{
@@ -9,11 +13,92 @@ AGasaGameState::AGasaGameState()
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.SetTickFunctionEnable(true);
PrimaryActorTick.bStartWithTickEnabled = true;
// Replication
bReplicates = true;
bNetLoadOnClient = false;
NetDormancy = DORM_Awake;
NetCullDistanceSquared = NetCullDist_Default;
NetUpdateFrequency = 10.0f;
MinNetUpdateFrequency = 1.0f;
NetPriority = 5.0f;
}
#pragma region GameState
#pragma region GameFramework
void AGasaGameState::OnGameFrameworkInitialized()
{
NetLog("Received gameplay framework initialization.");
if (IsServer())
{
if (PlayerArray.Num() > 0)
{
ListenServerHost = Cast<AGasaPlayerState>(PlayerArray[0]);
}
else
{
NetLog("Was not able to assign HostingPlayer!", ELogV::Error);
}
}
BP_OnGameFrameworkInitialized();
}
#pragma endregion GameFramework
#pragma region Networking
void AGasaGameState::Client_OnRep_OnlinePlayers()
{
}
#pragma endregion Networking
#pragma region Seamless Travel
void AGasaGameState::Multicast_R_NotifySeamlessTravelEnd_Implementation()
{
NetLog("Multicast_R_NotifySeamlessTravelEnd_Implementation");
BP_Event_OnSeamlessTravelEnd.Broadcast();
Event_OnSeamlessTravelEnd.Broadcast();
}
#pragma endregion Seamless Travel
#pragma region GameStateBase
void AGasaGameState::HandleBeginPlay()
{
Super::HandleBeginPlay();
NetLog("HandleBeginPlay: Directly called from GM");
}
void AGasaGameState::SeamlessTravelTransitionCheckpoint(bool bToTransitionMap)
{
Super::SeamlessTravelTransitionCheckpoint(bToTransitionMap);
NetLog("SeamlessTravelTransitionCheckpoint");
NetLog(FString("ToTransitionMap: ") + FString(bToTransitionMap ? "true" : "false"));
if (bToTransitionMap)
{
Event_OnSeamlessTravelStart.Broadcast();
}
else
{
Multicast_R_NotifySeamlessTravelEnd();
}
}
#pragma endregion GameStateBase
#pragma region Actor
void AGasaGameState::BeginPlay()
{
Super::BeginPlay();
NetLog("BeginPlay");
// Notified as initialized here as any possible components should also be initialized by this point.
UGasaGameInstance*
GI = GetGameInstance<UGasaGameInstance>();
GI->Event_OnGameFrameworkInitialized.AddDynamic(this, & ThisClass::OnGameFrameworkInitialized);
GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::GameState);
#if ENABLE_COG
CogWindowManager = NewObject<UCogWindowManager>(this);
CogWindowManagerRef = CogWindowManager;
@@ -23,6 +108,28 @@ void AGasaGameState::BeginPlay()
#endif //ENABLE_COG
}
void AGasaGameState::PostInitializeComponents()
{
NetLog("PostInitializeComponents");
Super::PostInitializeComponents();
if ( ! GetWorld()->IsEditorWorld() && IsServer())
{
OnlinePlayers.Empty();
#if 0
const auto GI = Cast<UGasaGameInstance>(GetGameInstance());
if (GI != nullptr)
{
int32 NumConnections = GI->SessionSettings.bPublicGame
? GI->SessionSettings.PublicConnections
: GI->SessionSettings.PrivateConnections;
OnlinePlayers.Init(nullptr, NumConnections);
}
#endif
}
}
void AGasaGameState::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
@@ -31,4 +138,14 @@ void AGasaGameState::Tick(float DeltaSeconds)
CogWindowManager->Tick(DeltaSeconds);
#endif //ENABLE_COG
}
#pragma endregion GameState
#pragma endregion Actor
#pragma region UObject
void AGasaGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AGasaGameState, ListenServerHost);
DOREPLIFETIME(AGasaGameState, OnlinePlayers);
}
#pragma endregion UObject

View File

@@ -1,46 +1,116 @@
#pragma once
#include "GameFramework/GameState.h"
#include "GasaCommon.h"
#include "Engine/Engine.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaGameState.generated.h"
UCLASS(Blueprintable)
DECLARE_MULTICAST_DELEGATE( FOnTravelDelegate );
DECLARE_DYNAMIC_MULTICAST_DELEGATE( FOnTravelSig );
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerCharacterReadySig, APlayerCharacter*, Character);
UCLASS( Blueprintable )
class GASA_API AGasaGameState : public AGameState
{
GENERATED_BODY()
public:
#pragma region Cog
// To make sure it doesn't get garbage collected.
UPROPERTY()
TObjectPtr<UObject> CogWindowManagerRef;
// To make sure it doesn't get garbage collected.
UPROPERTY()
TObjectPtr<UObject> CogWindowManagerRef;
#if ENABLE_COG
TObjectPtr<UCogWindowManager> CogWindowManager;
#endif // ENABLE_COG
#endif
// ENABLE_COG
#pragma endregion Cog
AGasaGameState();
#pragma region GameFramework
UFUNCTION()
void OnGameFrameworkInitialized();
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "Game Framework Initialized"))
void BP_OnGameFrameworkInitialized();
#pragma endregion GameFramework
#pragma region Networking
UPROPERTY(Replicated, BlueprintReadOnly)
AGasaPlayerState* ListenServerHost;
UPROPERTY(ReplicatedUsing = "Client_OnRep_OnlinePlayers", BlueprintReadOnly)
TArray<AGasaPlayerState> OnlinePlayers;
UFUNCTION()
void Client_OnRep_OnlinePlayers();
#pragma endregion Networking
#pragma region Seamless Travel
UPROPERTY(BlueprintAssignable)
FOnTravelSig Event_OnSeamlessTravelStart;
UPROPERTY(BlueprintAssignable, meta=(DisplayName="Event: On Seamless Travel End"))
FOnTravelSig BP_Event_OnSeamlessTravelEnd;
FOnTravelDelegate Event_OnSeamlessTravelEnd;
UFUNCTION(NetMulticast, Reliable)
void Multicast_R_NotifySeamlessTravelEnd();
#pragma endregion Seamless Travel
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region GameState
void BeginPlay() override;
void HandleBeginPlay() override;
#pragma endregion GameState
void Tick(float DeltaSeconds) override;
#pragma endregion GameState
#pragma region GameStateBase
void SeamlessTravelTransitionCheckpoint(bool bToTransitionMap) override;
#pragma endregion GameStateBase
#pragma region Actor
void BeginPlay() override;
void PostInitializeComponents() override;
void Tick( float DeltaSeconds ) override;
#pragma endregion Actor
#pragma region UObject
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
#pragma endregion UObject
};
namespace Gasa
{
inline
AGasaGameState* GetGameState(UObject* Context)
inline AGasaGameState* GetGameState( UObject* Context )
{
UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
UWorld* World = GEngine->GetWorldFromContextObject( Context, EGetWorldErrorMode::LogAndReturnNull );
if ( World == nullptr )
{
Log("World is null... are you running in a proper context?", ELogV::Error);
Log( "World is null... are you running in a proper context?", ELogV::Error );
return nullptr;
}
return Cast<AGasaGameState>(World->GetGameState());
return Cast<AGasaGameState>( World->GetGameState() );
}
}

View File

@@ -1,11 +1,39 @@
#include "GasaLevelScriptActor.h"
#include "GasaDevOptions.h"
#include "GasaGameInstance.h"
#include "Engine/PostProcessVolume.h"
#include "Kismet/GameplayStatics.h"
#include "Materials/MaterialInstance.h"
#include "Materials/MaterialInstanceDynamic.h"
using namespace Gasa;
#pragma region Game Framework
AGasaLevelScriptActor::AGasaLevelScriptActor()
{
// Replication
bReplicates = true;
bNetLoadOnClient = true;
NetDormancy = DORM_Awake;
NetCullDistanceSquared = NetCullDist_Default;
NetUpdateFrequency = 10.0f;
MinNetUpdateFrequency = 1.0f;
NetPriority = 1.0f;
}
void AGasaLevelScriptActor::OnGameFrameworkInitialized()
{
NetLog("Received game framework initialization.");
BP_OnGameFrameworkInitialized();
}
#pragma endregion Game Framework
#pragma region Actor
void AGasaLevelScriptActor::BeginPlay()
{
Super::BeginPlay();
NetLog("BeginPlay");
using namespace Gasa;
@@ -21,4 +49,12 @@ void AGasaLevelScriptActor::BeginPlay()
PPV->Settings.WeightedBlendables.Array[0].Object = MID;
break;
}
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
if(GI)
GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & ThisClass::OnGameFrameworkInitialized);
if (!bOverrideGameplayFrameworkReady)
GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::Levels);
}
#pragma endregion Actor

View File

@@ -1,8 +1,9 @@
#pragma once
#include "GasaCommon.h"
#include "Engine/Engine.h"
#include "Engine/LevelScriptActor.h"
#include "GasaCommon.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaLevelScriptActor.generated.h"
@@ -14,6 +15,41 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Post Process")
TObjectPtr<APostProcessVolume> GlobalPPV;
AGasaLevelScriptActor();
#pragma region GameFramework
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
bool bOverrideGameplayFrameworkReady = false;
UFUNCTION()
void OnGameFrameworkInitialized();
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName="On Game Framework Initialized"))
void BP_OnGameFrameworkInitialized();
#pragma endregion GameFramework
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region Actor
void BeginPlay() override;
#pragma region endActor

View File

@@ -155,19 +155,22 @@ void AGasaPlayerController::BeginPlay()
{
Super::BeginPlay();
check(IMC);
UEnhancedInputLocalPlayerSubsystem*
EILP_Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
check(EILP_Subsystem);
EILP_Subsystem->AddMappingContext(IMC, 0);
if (IsLocalController())
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
FInputModeGameAndUI MouseMode;
MouseMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
MouseMode.SetHideCursorDuringCapture(false);
SetInputMode(MouseMode);
check(IMC);
UEnhancedInputLocalPlayerSubsystem*
EILP_Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
check(EILP_Subsystem);
EILP_Subsystem->AddMappingContext(IMC, 0);
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
FInputModeGameAndUI MouseMode;
MouseMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
MouseMode.SetHideCursorDuringCapture(false);
SetInputMode(MouseMode);
}
}
}

View File

@@ -1,7 +1,9 @@
#pragma once
#include "GasaCommon.h"
#include "Engine/Engine.h"
#include "GameFramework/PlayerController.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaPlayerController.generated.h"
@@ -51,12 +53,33 @@ public:
AGasaPlayerController();
inline AGasaPlayerState* GetPlayerState();
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region PlayerController
void SpawnDefaultHUD() override;
void OnPossess(APawn* InPawn) override;
void OnUnPossess() override;
void PlayerTick(float DeltaTime) override;

View File

@@ -5,6 +5,8 @@
AGasaPlayerState::AGasaPlayerState()
{
bAutoAbilitySystem = true;
AbilitySystem = CreateDefaultSubobject<UGasaAbilitySystemComp>("Ability System");
AbilitySystem->SetIsReplicated(true);
AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);

View File

@@ -5,6 +5,7 @@
#include "GameFramework/PlayerState.h"
#include "GasaCommon.h"
#include "Networking/GasaNetLibrary.h"
#include "GasaPlayerState.generated.h"
@@ -16,7 +17,7 @@ class GASA_API AGasaPlayerState : public APlayerState
public:
#pragma region Ability System
UPROPERTY(EditAnywhere, Category="Ability System")
bool bAutoAbilitySystem = true;
bool bAutoAbilitySystem;
UPROPERTY(EditAnywhere, Category="Ability System")
TObjectPtr<UAbilitySystemComponent> AbilitySystem;
@@ -27,6 +28,28 @@ public:
AGasaPlayerState();
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region IAbilitySystem
FORCEINLINE UAttributeSet* GetAttributes() { return Attributes; }
FORCEINLINE UAbilitySystemComponent* GetAbilitySystemComponent() const override { return AbilitySystem; }

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using UnrealBuildTool;
using ModuleRules = UnrealBuildTool.ModuleRules;
using ReadOnlyTargetRules = UnrealBuildTool.ReadOnlyTargetRules;
using TargetRules = UnrealBuildTool.TargetRules;
@@ -10,7 +10,33 @@ public class Gasa : ModuleRules
{
public Gasa(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = false;
bUseUnity = false;
bMergeUnityFiles = false;
IWYUSupport = IWYUSupport.None;
PCHUsage = PCHUsageMode.NoPCHs;
OptimizeCode = CodeOptimization.Never;
MinCpuArchX64 = MinimumCpuArchitectureX64.AVX512;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
bCodeCoverage = false;
bDisableStaticAnalysis = true;
bValidateCircularDependencies = true;
bValidateFormatStrings = false;
bValidateInternalApi = false;
bEnableExceptions = false;
bEnableBufferSecurityChecks = false;
bEnableNonInlinedGenCppWarnings = false;
bEnableUndefinedIdentifierWarnings = false;
bIgnoreUnresolvedSymbols = false;
bEnableObjCAutomaticReferenceCounting = false;
bEnableObjCExceptions = false;
var Kilobyte = 1024;
NumIncludedBytesPerUnityCPPOverride = Kilobyte * 32;
MinFilesUsingPrecompiledHeaderOverride = 1;
PrivatePCHHeaderFile = "GasaColdHeadersPCH.h";
#region Engine
PrivateIncludePathModuleNames.AddRange(new string[] {
@@ -32,6 +58,7 @@ public class Gasa : ModuleRules
"InputCore",
"NetCore",
"Niagara",
"OnlineSubsystem",
"SlateCore",
"UMG",
});

View File

@@ -0,0 +1,53 @@
#pragma once
#include "GasaEngineMinimal.h"
#include "GasaCommon.h"
#include "GasaGameplayTags.h"
#include "GasaLibrary.h"
#include "GasaModule.h"
#include "GasaViewportSubsystem.h"
#include "GasaDevOptions.h"
#include "GasaDevOptionsCache.h"
// Ability System
// #include "AbilitySystem/"
#include "AbilitySystem/GasaAbilitySystem.h"
// #include "AbilitySystem/GasaAbilitySystemComponent.h"
// #include "AbilitySystem/GasaAttributeSet.h"
// #include "GasaEffectActor.h"
// Actors
#include "Actors/CameraMount.h"
// Characters
// #include "Characters/GasaCharacter.h"
// #include "Characters/EnemyCharacter.h"
// #include "Characters/PlayerCharacter.h"
// Game
// #include "Game/GasaGameInstance.h"
// #include "Game/GasaGameMode.h"
// #include "Game/GasaGameState.h"
// #include "Game/GasaLevelScriptActor.h"
// #include "Game/GasaPlayerController.h"
// #include "Game/GasaPlayerController_Inlines.h"
// #include "Game/GasaPlayerState.h"
#include "Game/GasaViewport.h"
// Networking
// #include "Networking/GasaNetLibrary.h"
// #include "Networking/GasaNetLibrary_Inlines.h"
// UI
// #include "UI/GasaCanvas.h"
// #include "UI/GasaCanvasPanel.h"
// #include "UI/GasaHUD.h"
// #include "UI/GasaHUD_Inlines.h"
// #include "UI/GasaImage.h"
// #include "UI/GasaOverlay.h"
// #include "UI/GasaProgressBar.h"
// #include "UI/GasaSizeBox.h"
// #include "UI/GasaUserWidget.h"
// #include "UI/HostWidgetController.h"
// #include "UI/HUDHostWidget.h"
// #include "UI/WidgetController.h"

View File

@@ -1,13 +1,16 @@

#pragma once
#pragma once
#include "CoreMinimal.h"
// #define private protected
#include "GasaEngineMinimal.h"
#define global
#define internal static
#define local_persist static
#define ccast( Type, Value ) ( *const_cast<(Type)*>( &( Value ) ) )
#define pcast( Type, Value ) ( *reinterpret_cast<(Type)*>( &( Value ) ) )
#define rcast( Type, Value ) reinterpret_cast<Type>( Value )
#define scast( Type, Value ) static_cast<Type>( Value )
#pragma region Math
#define m_pow2( value ) (value * value)
#pragma endregion Math
@@ -16,6 +19,9 @@
struct FInputActionValue;
struct FOnAttributeChangeData;
class AActor;
class APostProcessVolume;
class IAbilitySystemInterface;
class UAbilitySystemComponent;
@@ -45,11 +51,13 @@ class AGasaGameState;
class AGasaLevelScriptActor;
class AGasaPlayerController;
class AGasaPlayerState;
class APlayerCharacter;
class UGasaAbilitySystemComp;
class UGasaAttributeSet;
class UGasaDevOptions;
class UGasaImage;
class UGasaObject;
class UGasaOverlay;
class UGasaProgressBar;
class UGasaSizeBox;
@@ -58,6 +66,21 @@ class UHUDHostWidget;
class UWidgetController;
#pragma endregion Forwards
#pragma region Bitfields
namespace Gasa
{
inline
bool Bitfield_IsSet(int32 Bitfield, int32 Bitmask) {
int32 Result = Bitmask == (Bitfield & Bitmask);
return scast(bool, Result);
}
inline void Bitfield_Set ( int32& Bitfield, int32 BitsToAdd ) { Bitfield |= BitsToAdd; }
inline void Bitfield_Remove( int32& Bitfield, int32 BitsToRemove ) { Bitfield &= (! BitsToRemove); }
inline void Bitfield_Toggle( int32& Bitfield, int32 Bitmask ) { Bitfield ^= Bitmask; }
}
#pragma endregion Bitfields
#pragma region Logging
// Straight from the Engine
UENUM(BlueprintType)
@@ -107,7 +130,6 @@ namespace Gasa
{
using ELogV = EGasaVerbosity;
//◞ ‸ ◟//
// Works for Unreal 5.4, Win64 MSVC (untested in other scenarios, for now)
inline
void Log( FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log

View File

@@ -0,0 +1,160 @@
#pragma once
/*----------------------------------------------------------------------------
Low level includes.
----------------------------------------------------------------------------*/
#include "CoreTypes.h"
/*----------------------------------------------------------------------------
Forward declarations
----------------------------------------------------------------------------*/
#include "CoreFwd.h"
#include "UObject/UObjectHierarchyFwd.h"
#include "Containers/ContainersFwd.h"
/*----------------------------------------------------------------------------
Commonly used headers
----------------------------------------------------------------------------*/
#include "Misc/VarArgs.h"
#include "Logging/LogVerbosity.h"
#include "UObject/ObjectMacros.h"
// #include "Misc/OutputDevice.h"
// #include "HAL/PlatformCrt.h"
// #include "HAL/PlatformMisc.h"
// #include "Misc/AssertionMacros.h"
// #include "Templates/IsPointer.h"
// #include "HAL/PlatformMemory.h"
// #include "HAL/PlatformAtomics.h"
// #include "Misc/Exec.h"
// #include "HAL/MemoryBase.h"
// #include "HAL/UnrealMemory.h"
// #include "Templates/IsArithmetic.h"
// #include "Templates/AndOrNot.h"
// #include "Templates/IsPODType.h"
// #include "Templates/IsUECoreType.h"
// #include "Templates/IsTriviallyCopyConstructible.h"
// #include "Templates/UnrealTypeTraits.h"
// #include "Templates/EnableIf.h"
// #include "Templates/RemoveReference.h"
// #include "Templates/IntegralConstant.h"
// #include "Templates/IsClass.h"
// #include "Templates/TypeCompatibleBytes.h"
// #include "Traits/IsContiguousContainer.h"
// #include "Templates/UnrealTemplate.h"
// #include "Math/NumericLimits.h"
// #include "HAL/PlatformMath.h"
// #include "Templates/IsTriviallyCopyAssignable.h"
// #include "Templates/IsTriviallyDestructible.h"
// #include "Templates/MemoryOps.h"
// #include "Containers/ContainerAllocationPolicies.h"
// #include "Templates/IsEnumClass.h"
// #include "HAL/PlatformProperties.h"
// #include "Misc/EngineVersionBase.h"
// #include "Internationalization/TextNamespaceFwd.h"
// #include "Serialization/Archive.h"
// #include "Templates/Less.h"
// #include "Templates/Sorting.h"
// #include "Misc/Char.h"
// #include "GenericPlatform/GenericPlatformStricmp.h"
// #include "GenericPlatform/GenericPlatformString.h"
// #include "HAL/PlatformString.h"
// #include "Misc/CString.h"
// #include "Misc/Crc.h"
// #include "Math/UnrealMathUtility.h"
// #include "Containers/UnrealString.h"
// #include "Containers/Array.h"
// #include "Misc/FrameNumber.h"
// #include "Misc/Timespan.h"
// #include "Containers/StringConv.h"
// #include "UObject/UnrealNames.h"
// #include "UObject/NameTypes.h"
// #include "Misc/Parse.h"
// #include "Templates/AlignmentTemplates.h"
// #include "Misc/StructBuilder.h"
// #include "Templates/Decay.h"
// #include "Templates/PointerIsConvertibleFromTo.h"
// #include "Templates/Invoke.h"
// #include "Templates/Function.h"
// #include "Templates/TypeHash.h"
// #include "Containers/ScriptArray.h"
// #include "Containers/BitArray.h"
// #include "Containers/SparseArray.h"
// #include "Containers/Set.h"
// #include "Algo/Reverse.h"
// #include "Containers/Map.h"
// #include "Math/IntPoint.h"
// #include "Math/IntVector.h"
// #include "Logging/LogCategory.h"
// #include "Logging/LogMacros.h"
// #include "Math/Vector2D.h"
// #include "Math/IntRect.h"
// #include "Misc/ByteSwap.h"
// #include "Containers/EnumAsByte.h"
// #include "HAL/PlatformTLS.h"
// #include "CoreGlobals.h"
// #include "Templates/SharedPointer.h"
// #include "Internationalization/CulturePointer.h"
// #include "UObject/WeakObjectPtrTemplates.h"
// #include "Delegates/DelegateSettings.h"
// #include "Delegates/IDelegateInstance.h"
// #include "Delegates/DelegateBase.h"
// #include "Delegates/MulticastDelegateBase.h"
// #include "Delegates/IntegerSequence.h"
// #include "Templates/Tuple.h"
// #include "UObject/ScriptDelegates.h"
// #include "Delegates/Delegate.h"
// #include "Internationalization/TextLocalizationManager.h"
// #include "Misc/Optional.h"
// #include "Templates/IsArray.h"
// #include "Templates/RemoveExtent.h"
// #include "Templates/UniquePtr.h"
// #include "Internationalization/Text.h"
// #include "Templates/UniqueObj.h"
// #include "Internationalization/Internationalization.h"
// #include "Math/Vector.h"
// #include "Math/Vector4.h"
// #include "Math/VectorRegister.h"
// #include "Math/TwoVectors.h"
// #include "Math/Edge.h"
// #include "UObject/ObjectVersion.h"
// #include "Math/CapsuleShape.h"
// #include "Math/Rotator.h"
// #include "Misc/DateTime.h"
// #include "Math/RangeBound.h"
// #include "Misc/AutomationEvent.h"
// #include "Math/Range.h"
// #include "Math/RangeSet.h"
// #include "Math/Interval.h"
// #include "Math/Box.h"
// #include "Math/Box2D.h"
// #include "Math/BoxSphereBounds.h"
// #include "Math/OrientedBox.h"
// #include "Math/Axis.h"
// #include "Math/Matrix.h"
// #include "Math/RotationTranslationMatrix.h"
// #include "Math/RotationAboutPointMatrix.h"
// #include "Math/ScaleRotationTranslationMatrix.h"
// #include "Math/RotationMatrix.h"
// #include "Math/Quat.h"
// #include "Math/PerspectiveMatrix.h"
// #include "Math/OrthoMatrix.h"
// #include "Math/TranslationMatrix.h"
// #include "Math/QuatRotationTranslationMatrix.h"
// #include "Math/InverseRotationMatrix.h"
// #include "Math/ScaleMatrix.h"
// #include "Math/MirrorMatrix.h"
// #include "Math/ClipProjectionMatrix.h"
// #include "Math/Float32.h"
// #include "Math/Float16.h"
// #include "Math/Transform.h"
// #include "Math/ConvexHull2d.h"
// #include "Math/UnrealMath.h"

View File

@@ -8,6 +8,7 @@
#include "Game/GasaGameState.h"
#include "Game/GasaPlayerController.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/ConfigCacheIni.h"
#pragma region Game
UGasaDevOptions* UGasaLib::GetGasaDevOptions(UObject* Context) {

View File

@@ -1,6 +1,7 @@
#pragma once
#include "GasaCommon.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "GasaLibrary.Generated.h"

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
class GASA_API FGasaModule : public IModuleInterface
{

View File

@@ -0,0 +1,77 @@
#include "GasaObject.h"
#include "Engine/ActorChannel.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "Engine/NetDriver.h"
#include "GameFramework/Actor.h"
using namespace Gasa;
UGasaObject::UGasaObject()
{
bReplicates = false;
bDisconnectOnBadReplication = false;
}
void UGasaObject::Destroy()
{
if ( ! IsValid(this))
{
checkf(GetOwningActor()->HasAuthority() == true, TEXT("Destroy:: Object does not have authority to destroy itself!"));
OnDestroyed();
Event_OnDestroyed.Broadcast();
ConditionalBeginDestroy();
}
}
void UGasaObject::OnDestroyed()
{
}
bool UGasaObject::ReplicateAsSubobject(AActor* ActorResponsible, UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
if (!ActorResponsible)
{
NetLog("Actor reponsible is null", ELogV::Error);
return false;
}
if (!bDisconnectOnBadReplication && ActorResponsible != GetOuter())
{
NetLog("Attempted to replicate whose outer was not set to the actor whose responsible for replicating it as a subobject", ELogV::Error);
return false;
}
return Channel->ReplicateSubobject(this, *Bunch, *RepFlags);
}
bool UGasaObject::CallRemoteFunction(UFunction* Function, void* Parms, FOutParmRec* OutParms, FFrame* Stack)
{
check(! HasAnyFlags(RF_ClassDefaultObject));
AActor* Owner = GetOwningActor();
UNetDriver* NetDriver = Owner->GetNetDriver();
if (NetDriver)
{
NetDriver->ProcessRemoteFunction(Owner, Function, Parms, OutParms, Stack, this);
return true;
}
return false;
}
int32 UGasaObject::GetFunctionCallspace(UFunction* Function, FFrame* Stack)
{
check(GetOuter() != nullptr);
return GetOuter()->GetFunctionCallspace(Function, Stack);
}
void UGasaObject::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
// Add any Blueprint properties
// This is not required if you do not want the class to be "Blueprintable"
if (const UBlueprintGeneratedClass* BP = Cast<UBlueprintGeneratedClass>(GetClass()))
{
BP->GetLifetimeBlueprintReplicationList(OutLifetimeProps);
}
}
bool UGasaObject::IsSupportedForNetworking() const
{
return bReplicates;
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include "GasaCommon.h"
#include "Networking/GasaNetLibrary.h"
#include "UObject/Object.h"
#include "GasaObject.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGasaObjectEventSig);
// A UObject which supports replication and other features.
UCLASS( Blueprintable )
class GASA_API UGasaObject : public UObject
{
GENERATED_BODY()
public:
UGasaObject();
UFUNCTION(BlueprintPure)
FORCEINLINE AActor* GetOwningActor() const { return GetTypedOuter<AActor>(); };
UPROPERTY(BlueprintAssignable, Category="Lifetime")
FGasaObjectEventSig Event_OnDestroyed;
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Lifetime")
virtual void Destroy();
virtual void OnDestroyed();
UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName = "On Destroyed"))
void BP_OnDestroyed();
#pragma region Replication
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Replication")
bool bReplicates;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Replication")
bool bDisconnectOnBadReplication ;
virtual bool ReplicateAsSubobject(AActor* ActorResponsible, UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags);
UFUNCTION(BlueprintCallable)
void SetIsReplicated(bool DesiredValue)
{
bReplicates = DesiredValue;
}
#pragma endregion Replication
#pragma region NetSlime
// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp
FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); }
FORCEINLINE bool IsClient() const { return Gasa::IsClient( this ); }
FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer( this ); }
FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner( this ); }
FORCEINLINE bool IsServer() const { return Gasa::IsServer( this ); }
FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy( this ); }
FORCEINLINE void NetLog(
FString Message,
EGasaVerbosity Verbosity = EGasaVerbosity::Log,
FLogCategoryBase& Category = LogGasaNet,
bool DumpStack = false,
int32 Line = __builtin_LINE(),
ANSICHAR const* File = __builtin_FILE(),
ANSICHAR const* Func = __builtin_FUNCTION()
)
{
Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
#pragma endregion NetSlime
#pragma region UObject
bool CallRemoteFunction(UFunction* Function, void* Parms, FOutParmRec* OutParms, FFrame* Stack) override;
int32 GetFunctionCallspace(UFunction* Function, FFrame* Stack) override;
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
bool IsSupportedForNetworking() const override;
#pragma endregion UObject
};

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;
}
}

View File

@@ -8,10 +8,10 @@ using namespace Gasa;
void AGasaHUD::InitHostWidget(FWidgetControllerData const* WidgetControllerData)
{
HostWidget = CreateWidget<UHUDHostWidget>( GetWorld()
HostWidget = CreateWidget<UHUDHostWidget>( GetWorld()
, GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() );
HostWidgetController = NewObject<UHostWidgetController>(this, GetDevOptions()->Template_HostWidgetController.Get());
HostWidgetController = NewObject<UHostWidgetController>(this, GetDevOptions()->Template_HostWidgetController.Get());
HostWidgetController->Data = (* WidgetControllerData);
HostWidget->SetWidgetController(HostWidgetController);
HostWidgetController->BindCallbacksToDependencies();

View File

@@ -3,6 +3,9 @@
#include "AbilitySystem/GasaAttributeSet.h"
#include "GameplayEffectTypes.h"
#pragma region Attribute Changed Callbacks
// Attribute Changed Callbacks are generated by GasaGen/GasaGen_HostWidgetController.cpp

View File

@@ -2,6 +2,7 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Runtime;
using UnrealBuildTool;
using BuildSettingsVersion = UnrealBuildTool.BuildSettingsVersion;
using TargetInfo = UnrealBuildTool.TargetInfo;
using TargetRules = UnrealBuildTool.TargetRules;
@@ -17,6 +18,7 @@ public class GasaEditorTarget : TargetRules
bUseUnityBuild = true;
// bUseXGEController = false;
LinkType = TargetLinkType.Modular;
ExtraModuleNames.Add("Gasa");
ExtraModuleNames.Add("GasaEditor");

View File

@@ -18,6 +18,7 @@ using namespace gen;
#include "GasaGen_ChangeBPActionMenu.cpp"
#include "GasaGen_DevOptionsCache.cpp"
#include "GasaGen_HostWidgetController.cpp"
#include "GasaGen_NetSlime.cpp"
int gen_main()
{
@@ -80,6 +81,7 @@ int gen_main()
gen_UGasaAttributeSet();
gen_FGasaDevOptionsCache();
gen_UHostWidgetController();
// gen_netslime_interfaces();
// One offs
if (0)

View File

@@ -13,6 +13,9 @@ using namespace gen;
#define path_config path_source "Config/"
#define path_module_gasa path_source "Gasa/"
#define path_gasa_ability_system path_module_gasa "AbilitySystem/"
#define path_gasa_actors path_module_gasa "Actors/"
#define path_gasa_characters path_module_gasa "Characters/"
#define path_gasa_game path_module_gasa "Game/"
#define path_gasa_ui path_module_gasa "UI/"
constexpr StrC str_DECLARE_CLASS = txt("DECLARE_CLASS(");

View File

@@ -0,0 +1,118 @@
// Used in the GasaGen.cpp translation unit
#if GASA_INTELLISENSE_DIRECTIVES
#pragma once
#define GEN_EXPOSE_BACKEND
#include "gen.hpp"
#include "gen.builder.hpp"
#include "GasaGenCommon.cpp"
#endif
void gen_netslime_interface(CodeClass aclass)
{
CodeBody net_slime_class_interface = def_body(ECode::Class_Body);
{
#pragma push_macro("FORCEINLINE")
#undef FORCEINLINE
CodeFn DrawNetCullingSphere = parse_function( code(
FORCEINLINE void DrawNetCullingSphere(float Duration, float Thickness) const final { Gasa::DrawNetCullingSphere(this, Duration, Thickness); }
));
CodeFn GetNetworkMode = parse_function( code( FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode(this); } ));
CodeFn IsClient = parse_function( code( FORCEINLINE bool IsClient() const { return Gasa::IsClient(this); } ));
CodeFn IsListenServer = parse_function( code( FORCEINLINE bool IsListenServer() const { return Gasa::IsListenServer(this); } ));
CodeFn IsNetOwner = parse_function( code( FORCEINLINE bool IsNetOwner() const { return Gasa::IsNetOwner(this); } ));
CodeFn IsServer = parse_function( code( FORCEINLINE bool IsServer() const { return Gasa::IsServer(this); } ));
CodeFn IsSimulatedProxy = parse_function( code( FORCEINLINE bool IsSimulatedProxy() const { return Gasa::IsSimulatedProxy(this); } ));
CodeFn NetLog = parse_function( code(
FORCEINLINE void NetLog( 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() )
{
Gasa::NetLog(this, Message, Verbosity, Category, DumpStack, Line, File, Func );
}
));
CodeFn ServerAuthorized = parse_function( code( FORCEINLINE bool ServerAuthorized() const { return Gasa::ServerAuthorized(this); } ));
#pragma pop_macro("FORCEINLINE")
net_slime_class_interface.append(GetNetworkMode);
net_slime_class_interface.append(IsClient);
net_slime_class_interface.append(IsListenServer);
net_slime_class_interface.append(IsNetOwner);
net_slime_class_interface.append(IsServer);
net_slime_class_interface.append(IsSimulatedProxy);
net_slime_class_interface.append(NetLog);
}
CodeBody new_body = def_body(ECode::Class_Body);
for(Code code = aclass->Body.begin(); code != aclass->Body.end(); ++ code )
{
switch (code->Type)
{
default:
new_body.append(code);
break;
// TODO(Ed): Could this be turned into a singly? void find_and_swap_region_pragma(CodeClass, StrC region)
// IT could return void if its assumed that the Code passed will have destructive edits to the body.
case ECode::Preprocess_Pragma:
{
local_persist bool found = false;
if (found || ! code->Content.starts_with( txt("region NetSlime")))
{
new_body.append(code);
continue;
}
// Add pragma
new_body.append(code);
++ code;
new_body.append( def_comment( txt("NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp")));
new_body.append(net_slime_class_interface);
while (code->Type != ECode::Preprocess_Pragma
|| ! code->Content.starts_with(txt("endregion NetSlime")))
++ code;
new_body.append(code);
}
break;
}
}
aclass->Body = new_body;
}
void gen_netslime_interfaces()
{
Array<StringCached> header_paths = Array<StringCached>::init_reserve(GlobalAllocator, 32);
// header_paths.append(get_cached_string(txt( path_module_gasa "GasaObject.h")));
// header_paths.append(get_cached_string(txt( path_gasa_actors "GasaActor.h")));
// header_paths.append(get_cached_string(txt( path_gasa_characters "GasaCharacter.h")));
// header_paths.append(get_cached_string(txt( path_gasa_game "GasaGameMode.h")));
// header_paths.append(get_cached_string(txt( path_gasa_game "GasaGameState.h")));
for (StringCached path : header_paths)
{
CodeBody original_header = parse_file(path);
CodeBody header_body = def_body(ECode::Global_Body);
for (Code code : original_header)
{
switch (code->Type) {
case ECode::Class:
{
CodeClass aclass = code.cast<CodeClass>();
gen_netslime_interface(aclass);
header_body.append(aclass);
}
break;
default:
header_body.append(code);
}
}
Builder header = Builder::open(path);
header.print(header_body);
header.write();
format_file(path);
}
}

View File

@@ -272,6 +272,7 @@ void def_attribute_field_value_setters( CodeBody body, Array<StringCached> prope
void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name, Array<StringCached> properties )
{
body.append(def_pragma( txt("region Attribute Setters")));
for ( String property : properties )
{
CodeFn generated_get_attribute = parse_function(
@@ -288,6 +289,7 @@ void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name
)));
body.append( generated_get_attribute );
}
body.append(def_pragma( txt("endregion Attribute Setters")));
}
void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties )
@@ -307,11 +309,10 @@ void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties
void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties )
{
body.append(fmt_newline);
body.append(def_pragma( txt("region Rep Notifies")));
for ( String property : properties )
{
body.append(fmt_newline);
CodeFn field_impl = parse_function( token_fmt(
"class_name", class_name, "property", (StrC)property, "from_notice", txt("\n// From GAMEPLAYATTRIBUTE_REPNOTIFY\n"),
stringize(
@@ -326,6 +327,7 @@ void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached>
body.append( field_impl );
}
body.append( def_pragma( txt("endregion Rep Notifies")));
body.append(fmt_newline);
}
inline

View File

@@ -1610,7 +1610,7 @@ void CodeConstructor::to_string_fwd( String& result )
if ( ast->InlineCmt )
result.append_fmt( "; // %S\n", ast->InlineCmt->Content );
else
result.append( ";" );
result.append( ";\n" );
}
String CodeClass::to_string()
@@ -6278,6 +6278,16 @@ namespace parser
move_forward();
preprocess_content.Length++;
if ( current == '\r' && scanner[1] == '\n' )
{
move_forward();
move_forward();
}
else if ( current == '\n' )
{
move_forward();
}
Tokens.append( preprocess_content );
return Lex_Continue; // Skip found token, its all handled here.
}
@@ -7262,7 +7272,7 @@ namespace parser
Tokens = Array<Token>::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / sizeof( Token ) );
defines_map_arena = Arena_256KB::init();
defines = HashTable<StrC>::init( defines_map_arena );
defines = HashTable<StrC>::init_reserve( defines_map_arena, 256 );
}
internal void deinit()

View File

@@ -1663,7 +1663,7 @@ struct Array
{
Header& header = *get_header();
if ( begin < 0 || end >= header.Num )
if ( begin < 0 || end > header.Num )
return false;
for ( sw idx = begin; idx < end; idx++ )
@@ -1820,13 +1820,11 @@ struct HashTable
Type Value;
};
static constexpr f32 CriticalLoadScale = 0.7f;
static HashTable init( AllocatorInfo allocator )
{
HashTable<Type> result = { { nullptr }, { nullptr } };
result.Hashes = Array<sw>::init( allocator );
result.Entries = Array<Entry>::init( allocator );
HashTable<Type> result = init_reserve(allocator, 8);
return result;
}
@@ -1834,21 +1832,19 @@ struct HashTable
{
HashTable<Type> result = { { nullptr }, { nullptr } };
result.Hashes = Array<sw>::init_reserve( allocator, num );
result.Hashes = Array<sw>::init_reserve( allocator, num );
result.Hashes.get_header()->Num = num;
result.Hashes.resize( num );
result.Hashes.fill( 0, num, -1);
result.Entries = Array<Entry>::init_reserve( allocator, num );
result.Entries = Array<Entry>::init_reserve( allocator, num );
return result;
}
void clear( void )
{
for ( sw idx = 0; idx < Hashes.num(); idx++ )
Hashes[idx] = -1;
Hashes.clear();
Entries.clear();
Hashes.fill( 0, Hashes.num(), -1);
}
void destroy( void )
@@ -1901,32 +1897,19 @@ struct HashTable
void rehash( sw new_num )
{
sw idx;
sw last_added_index;
HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num );
Array<sw>::Header* hash_header = new_ht.Hashes.get_header();
for ( idx = 0; idx < new_ht.Hashes.num(); ++idx )
new_ht.Hashes[idx] = -1;
for ( idx = 0; idx < Entries.num(); ++idx )
HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num );
for ( sw idx = 0; idx < Entries.num(); ++idx )
{
Entry& entry = Entries[idx];
FindResult find_result;
if ( new_ht.Hashes.num() == 0 )
new_ht.grow();
entry = Entries[idx];
Entry& entry = Entries[idx];
find_result = new_ht.find( entry.Key );
last_added_index = new_ht.add_entry( entry.Key );
if ( find_result.PrevIndex < 0 )
new_ht.Hashes[find_result.HashIndex] = last_added_index;
else
new_ht.Entries[find_result.PrevIndex].Next = last_added_index;
@@ -1984,11 +1967,10 @@ struct HashTable
sw idx;
FindResult find_result;
if ( Hashes.num() == 0 )
if ( full() )
grow();
find_result = find( key );
if ( find_result.EntryIndex >= 0 )
{
idx = find_result.EntryIndex;
@@ -2060,7 +2042,9 @@ protected:
b32 full()
{
return 0.75f * Hashes.num() < Entries.num();
uw critical_load = uw( CriticalLoadScale * f32(Hashes.num()) );
b32 result = Entries.num() > critical_load;
return result;
}
};
@@ -2096,7 +2080,7 @@ struct StrC
#define txt( text ) \
StrC \
{ \
sizeof( text ) - 1, text \
sizeof( (text) ) - 1, (text) \
}
StrC to_str( char const* str )