diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.dll b/Project/Binaries/Win64/UnrealEditor-Gasa.dll
index f3801ba..8bbd303 100644
--- a/Project/Binaries/Win64/UnrealEditor-Gasa.dll
+++ b/Project/Binaries/Win64/UnrealEditor-Gasa.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2230c6cb14601b714954bb70c831b116d14801e27d78748213f2f730644ca305
-size 605696
+oid sha256:457c5440bf798c3fe0ac5681a4d6e59166b427c08fddba63e479c5249ee92114
+size 1010176
diff --git a/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll b/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll
index 3d5438c..28e4fff 100644
--- a/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll
+++ b/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d558410faddece4149f4930726a1b11728cf235a111e8763bac8a43ba10c228d
+oid sha256:36682d8d415863139146fca4b596b67bb232b60f306c82f2b2692780b044f200
size 79360
diff --git a/Project/Gasa.uproject b/Project/Gasa.uproject
index 2439597..b33ad17 100644
--- a/Project/Gasa.uproject
+++ b/Project/Gasa.uproject
@@ -301,10 +301,6 @@
"Name": "VisualStudioSourceCodeAccess",
"Enabled": true
},
- {
- "Name": "GitSourceControl",
- "Enabled": true
- },
{
"Name": "SlateInsights",
"Enabled": true
diff --git a/Project/Saved/UnrealBuildTool/BuildConfiguration.xml b/Project/Saved/UnrealBuildTool/BuildConfiguration.xml
index 261cb7a..ec273d3 100644
--- a/Project/Saved/UnrealBuildTool/BuildConfiguration.xml
+++ b/Project/Saved/UnrealBuildTool/BuildConfiguration.xml
@@ -1,3 +1,5 @@
+ false
+
diff --git a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp
index fae9819..119eca9 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp
+++ b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp
@@ -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( 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( 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( 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& OutLifetimeProps ) const
{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );
diff --git a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h
index 898ea2c..6d4cd2a 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h
+++ b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h
@@ -23,6 +23,7 @@ public:
UGasaAttributeSet();
+
UFUNCTION()
void Client_OnRep_Health( FGameplayAttributeData& PrevHealth );
UFUNCTION()
diff --git a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet_Inlines.h b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet_Inlines.h
index bad1e43..3f49d74 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet_Inlines.h
+++ b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet_Inlines.h
@@ -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
{
diff --git a/Project/Source/Gasa/AbilitySystem/GasaEffectActor.cpp b/Project/Source/Gasa/AbilitySystem/GasaEffectActor.cpp
index 42367ae..e8e8559 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaEffectActor.cpp
+++ b/Project/Source/Gasa/AbilitySystem/GasaEffectActor.cpp
@@ -10,13 +10,13 @@ AGasaEffectActor::AGasaEffectActor()
RootComponent = CreateDefaultSubobject("Root");
}
-void AGasaEffectActor::ApplyEffectToTarget(AActor* Target, TSubclassOf EffectClass)
+void AGasaEffectActor::ApplyEffectToActor(AActor* Actor, TSubclassOf 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 );
diff --git a/Project/Source/Gasa/AbilitySystem/GasaEffectActor.h b/Project/Source/Gasa/AbilitySystem/GasaEffectActor.h
index f000ac3..49d3be8 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaEffectActor.h
+++ b/Project/Source/Gasa/AbilitySystem/GasaEffectActor.h
@@ -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 InstantEffectClass;
AGasaEffectActor();
- void ApplyEffectToTarget(AActor* Target, TSubclassOf EffectClass );
+ UFUNCTION(BlueprintCallable, Category = "Gameplay Effects")
+ void ApplyEffectToActor(AActor* Actor, TSubclassOf EffectClass );
};
+
diff --git a/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.cpp b/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.cpp
index 024e3a2..9b8de52 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.cpp
+++ b/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.cpp
@@ -4,6 +4,7 @@
#include "GasaAttributeSet.h"
#include "GasaAttributeSet_Inlines.h"
#include "Components/SphereComponent.h"
+#include "Components/StaticMeshComponent.h"
AGasaEffectActorDemo::AGasaEffectActorDemo()
diff --git a/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.h b/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.h
index 852468b..bab5276 100644
--- a/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.h
+++ b/Project/Source/Gasa/AbilitySystem/GasaEffectActorDemo.h
@@ -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:
diff --git a/Project/Source/Gasa/Actors/CameraMount.h b/Project/Source/Gasa/Actors/CameraMount.h
index 60a7b37..e08e6f3 100644
--- a/Project/Source/Gasa/Actors/CameraMount.h
+++ b/Project/Source/Gasa/Actors/CameraMount.h
@@ -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:
diff --git a/Project/Source/Gasa/Actors/GasaActor.cpp b/Project/Source/Gasa/Actors/GasaActor.cpp
new file mode 100644
index 0000000..e69de29
diff --git a/Project/Source/Gasa/Actors/GasaActor.h b/Project/Source/Gasa/Actors/GasaActor.h
new file mode 100644
index 0000000..a6a4ecc
--- /dev/null
+++ b/Project/Source/Gasa/Actors/GasaActor.h
@@ -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
+};
diff --git a/Project/Source/Gasa/Characters/GasaCharacter.cpp b/Project/Source/Gasa/Characters/GasaCharacter.cpp
index d224f81..d562697 100644
--- a/Project/Source/Gasa/Characters/GasaCharacter.cpp
+++ b/Project/Source/Gasa/Characters/GasaCharacter.cpp
@@ -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)
{
diff --git a/Project/Source/Gasa/Characters/GasaCharacter.h b/Project/Source/Gasa/Characters/GasaCharacter.h
index 6a71200..4e2bd73 100644
--- a/Project/Source/Gasa/Characters/GasaCharacter.h
+++ b/Project/Source/Gasa/Characters/GasaCharacter.h
@@ -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(); }
+
+#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; }
diff --git a/Project/Source/Gasa/Characters/PlayerCharacter.cpp b/Project/Source/Gasa/Characters/PlayerCharacter.cpp
index cfe19cc..1ec3939 100644
--- a/Project/Source/Gasa/Characters/PlayerCharacter.cpp
+++ b/Project/Source/Gasa/Characters/PlayerCharacter.cpp
@@ -25,10 +25,13 @@ void APlayerCharacter::PossessedBy(AController* NewController)
AbilitySystem->InitAbilityActorInfo(PS, this);
}
- AGasaPlayerController* PC = GetController();
- AGasaHUD* HUD = PC->GetHUD();
- FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
- HUD->InitHostWidget(& Data);
+ if (IsLocallyControlled())
+ {
+ AGasaPlayerController* PC = GetController();
+ AGasaHUD* HUD = PC->GetHUD();
+ FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes };
+ HUD->InitHostWidget(& Data);
+ }
}
// TODO(Ed): We need to setup Net Slime...
diff --git a/Project/Source/Gasa/Game/GasaGameInstance.cpp b/Project/Source/Gasa/Game/GasaGameInstance.cpp
index 1b1b707..3424c08 100644
--- a/Project/Source/Gasa/Game/GasaGameInstance.cpp
+++ b/Project/Source/Gasa/Game/GasaGameInstance.cpp
@@ -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
diff --git a/Project/Source/Gasa/Game/GasaGameInstance.h b/Project/Source/Gasa/Game/GasaGameInstance.h
index 4c50a68..fb23c69 100644
--- a/Project/Source/Gasa/Game/GasaGameInstance.h
+++ b/Project/Source/Gasa/Game/GasaGameInstance.h
@@ -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
diff --git a/Project/Source/Gasa/Game/GasaGameMode.cpp b/Project/Source/Gasa/Game/GasaGameMode.cpp
index 67304d3..2861ca5 100644
--- a/Project/Source/Gasa/Game/GasaGameMode.cpp
+++ b/Project/Source/Gasa/Game/GasaGameMode.cpp
@@ -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()->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(C);
+ if (PC != nullptr)
+ {
+ // Moved to: void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn)
+ // InitializeHUDForPlayer(Cast(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 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(Controller);
+ UClass* PCClassToSpawn = GetPlayerControllerClassToSpawnForSeamlessTravel(PC);
+ // FUniqueNetIdRepl StoredNativePlatformUniqueNetId = C->GetPlayerState()->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())
+ {
+#if 0
+ UGasaGameInstance* GI = GetGameInstance();
+ 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();
+ PS->SgID = GS->OnlinePlayers.Find( PS );
+ break;
+ }
+ }
+#endif
+ }
+
+ // Controller->GetPlayerState()->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(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(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(Exiting));
+ NetLog("User Logged out: " + Exiting->GetName());
+
+ if (AGasaGameState* GS = Cast(GetWorld()->GetGameState()))
+ {
+#if 0
+ int32 Index = GS->OnlinePlayers.Find(Exiting->GetPlayerState());
+ 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())
+ {
+ UGasaGameInstance* GI = GetGameInstance();
+
+ // 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();
+ 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(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(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();
+ 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 InPlayerControllerClass)
+{
+ NetLog("SpawnPlayerControllerCommon");
+ return Super::SpawnPlayerControllerCommon(InRemoteRole, SpawnLocation, SpawnRotation, InPlayerControllerClass);
+}
+#pragma endregion GameModeBase
diff --git a/Project/Source/Gasa/Game/GasaGameMode.h b/Project/Source/Gasa/Game/GasaGameMode.h
index cd0ba2e..0be8d82 100644
--- a/Project/Source/Gasa/Game/GasaGameMode.h
+++ b/Project/Source/Gasa/Game/GasaGameMode.h
@@ -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 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 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(World->GetAuthGameMode());
+ return Cast( World->GetAuthGameMode() );
}
}
diff --git a/Project/Source/Gasa/Game/GasaGameState.cpp b/Project/Source/Gasa/Game/GasaGameState.cpp
index f8b1a60..0a35308 100644
--- a/Project/Source/Gasa/Game/GasaGameState.cpp
+++ b/Project/Source/Gasa/Game/GasaGameState.cpp
@@ -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(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();
+ GI->Event_OnGameFrameworkInitialized.AddDynamic(this, & ThisClass::OnGameFrameworkInitialized);
+ GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::GameState);
+
#if ENABLE_COG
CogWindowManager = NewObject(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(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& OutLifetimeProps) const
+{
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+
+ DOREPLIFETIME(AGasaGameState, ListenServerHost);
+ DOREPLIFETIME(AGasaGameState, OnlinePlayers);
+}
+#pragma endregion UObject
diff --git a/Project/Source/Gasa/Game/GasaGameState.h b/Project/Source/Gasa/Game/GasaGameState.h
index b8a9e37..cf9fa6b 100644
--- a/Project/Source/Gasa/Game/GasaGameState.h
+++ b/Project/Source/Gasa/Game/GasaGameState.h
@@ -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 CogWindowManagerRef;
-
+ // To make sure it doesn't get garbage collected.
+ UPROPERTY()
+ TObjectPtr CogWindowManagerRef;
+
#if ENABLE_COG
TObjectPtr 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 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& 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(World->GetGameState());
+ return Cast( World->GetGameState() );
}
}
diff --git a/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp
index ec21511..904a899 100644
--- a/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp
+++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp
@@ -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();
+ if(GI)
+ GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & ThisClass::OnGameFrameworkInitialized);
+
+ if (!bOverrideGameplayFrameworkReady)
+ GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::Levels);
}
+#pragma endregion Actor
diff --git a/Project/Source/Gasa/Game/GasaLevelScriptActor.h b/Project/Source/Gasa/Game/GasaLevelScriptActor.h
index f5447d4..0463081 100644
--- a/Project/Source/Gasa/Game/GasaLevelScriptActor.h
+++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.h
@@ -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 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
diff --git a/Project/Source/Gasa/Game/GasaPlayerController.cpp b/Project/Source/Gasa/Game/GasaPlayerController.cpp
index 994de32..bb713e6 100644
--- a/Project/Source/Gasa/Game/GasaPlayerController.cpp
+++ b/Project/Source/Gasa/Game/GasaPlayerController.cpp
@@ -155,19 +155,22 @@ void AGasaPlayerController::BeginPlay()
{
Super::BeginPlay();
- check(IMC);
-
- UEnhancedInputLocalPlayerSubsystem*
- EILP_Subsystem = ULocalPlayer::GetSubsystem(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(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);
+ }
}
}
diff --git a/Project/Source/Gasa/Game/GasaPlayerController.h b/Project/Source/Gasa/Game/GasaPlayerController.h
index 9674b86..7fcf542 100644
--- a/Project/Source/Gasa/Game/GasaPlayerController.h
+++ b/Project/Source/Gasa/Game/GasaPlayerController.h
@@ -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;
diff --git a/Project/Source/Gasa/Game/GasaPlayerState.cpp b/Project/Source/Gasa/Game/GasaPlayerState.cpp
index 69d6747..c5ba382 100644
--- a/Project/Source/Gasa/Game/GasaPlayerState.cpp
+++ b/Project/Source/Gasa/Game/GasaPlayerState.cpp
@@ -5,6 +5,8 @@
AGasaPlayerState::AGasaPlayerState()
{
+ bAutoAbilitySystem = true;
+
AbilitySystem = CreateDefaultSubobject("Ability System");
AbilitySystem->SetIsReplicated(true);
AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
diff --git a/Project/Source/Gasa/Game/GasaPlayerState.h b/Project/Source/Gasa/Game/GasaPlayerState.h
index 47ba22c..2c498e8 100644
--- a/Project/Source/Gasa/Game/GasaPlayerState.h
+++ b/Project/Source/Gasa/Game/GasaPlayerState.h
@@ -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 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; }
diff --git a/Project/Source/Gasa/Gasa.Build.cs b/Project/Source/Gasa/Gasa.Build.cs
index b2abffc..2de60ae 100644
--- a/Project/Source/Gasa/Gasa.Build.cs
+++ b/Project/Source/Gasa/Gasa.Build.cs
@@ -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",
});
diff --git a/Project/Source/Gasa/GasaColdHeadersPCH.h b/Project/Source/Gasa/GasaColdHeadersPCH.h
new file mode 100644
index 0000000..3e37bb5
--- /dev/null
+++ b/Project/Source/Gasa/GasaColdHeadersPCH.h
@@ -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"
diff --git a/Project/Source/Gasa/GasaCommon.h b/Project/Source/Gasa/GasaCommon.h
index 5870db4..3662cc1 100644
--- a/Project/Source/Gasa/GasaCommon.h
+++ b/Project/Source/Gasa/GasaCommon.h
@@ -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( Value )
+#define scast( Type, Value ) static_cast( 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
diff --git a/Project/Source/Gasa/GasaEngineMinimal.h b/Project/Source/Gasa/GasaEngineMinimal.h
new file mode 100644
index 0000000..85194d7
--- /dev/null
+++ b/Project/Source/Gasa/GasaEngineMinimal.h
@@ -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"
\ No newline at end of file
diff --git a/Project/Source/Gasa/GasaLibrary.cpp b/Project/Source/Gasa/GasaLibrary.cpp
index 351a251..37b1667 100644
--- a/Project/Source/Gasa/GasaLibrary.cpp
+++ b/Project/Source/Gasa/GasaLibrary.cpp
@@ -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) {
diff --git a/Project/Source/Gasa/GasaLibrary.h b/Project/Source/Gasa/GasaLibrary.h
index 9dee172..84c75d4 100644
--- a/Project/Source/Gasa/GasaLibrary.h
+++ b/Project/Source/Gasa/GasaLibrary.h
@@ -1,6 +1,7 @@
#pragma once
#include "GasaCommon.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
#include "GasaLibrary.Generated.h"
diff --git a/Project/Source/Gasa/GasaModule.h b/Project/Source/Gasa/GasaModule.h
index 7d4e9ba..0d9b987 100644
--- a/Project/Source/Gasa/GasaModule.h
+++ b/Project/Source/Gasa/GasaModule.h
@@ -1,6 +1,7 @@
#pragma once
#include "Modules/ModuleInterface.h"
+#include "Modules/ModuleManager.h"
class GASA_API FGasaModule : public IModuleInterface
{
diff --git a/Project/Source/Gasa/GasaObject.cpp b/Project/Source/Gasa/GasaObject.cpp
new file mode 100644
index 0000000..978564f
--- /dev/null
+++ b/Project/Source/Gasa/GasaObject.cpp
@@ -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& 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(GetClass()))
+ {
+ BP->GetLifetimeBlueprintReplicationList(OutLifetimeProps);
+ }
+}
+
+bool UGasaObject::IsSupportedForNetworking() const
+{
+ return bReplicates;
+}
diff --git a/Project/Source/Gasa/GasaObject.h b/Project/Source/Gasa/GasaObject.h
new file mode 100644
index 0000000..1d800dc
--- /dev/null
+++ b/Project/Source/Gasa/GasaObject.h
@@ -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(); };
+
+ 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& OutLifetimeProps) const override;
+
+ bool IsSupportedForNetworking() const override;
+#pragma endregion UObject
+};
diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary.cpp b/Project/Source/Gasa/Networking/GasaNetLibrary.cpp
index 169c801..3741f56 100644
--- a/Project/Source/Gasa/Networking/GasaNetLibrary.cpp
+++ b/Project/Source/Gasa/Networking/GasaNetLibrary.cpp
@@ -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(Context);
+ else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
+ Actor = Cast(Context)->GetOwner();
+ // Its assumed that all GasaObjects have an outer actor
+ else if (Context->IsA(UGasaObject::StaticClass()))
+ Actor = Cast(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
+}
\ No newline at end of file
diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary.h b/Project/Source/Gasa/Networking/GasaNetLibrary.h
index 575ac03..98a9a33 100644
--- a/Project/Source/Gasa/Networking/GasaNetLibrary.h
+++ b/Project/Source/Gasa/Networking/GasaNetLibrary.h
@@ -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);
}
diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h b/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h
new file mode 100644
index 0000000..c2a621d
--- /dev/null
+++ b/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h
@@ -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(Context->GetOuter());
+
+ else if (Context->IsA(AActor::StaticClass()))
+ actor = Cast(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(Context);
+ else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
+ Actor = Cast(Context)->GetOwner();
+ // Its assumed that all GasaObjects have an outer actor
+ else if (Context->IsA(UGasaObject::StaticClass()))
+ Actor = Cast(Context->GetOuter());
+ else
+ {
+ UObject const* Outermost = Context->GetOutermostObject();
+ if (Outermost->IsA(AActor::StaticClass()))
+ Actor = Cast(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(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(Context);
+ else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
+ Actor = Cast(Context)->GetOwner();
+ // Its assumed that all GasaObjects have an outer actor
+ else if (Context->IsA(UGasaObject::StaticClass()))
+ Actor = Cast(Context->GetOuter());
+ else
+ {
+ UObject const* Outermost = Context->GetOutermostObject();
+ if (Outermost->IsA(AActor::StaticClass()))
+ Actor = Cast(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(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(Context);
+ else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass()))
+ Actor = Cast(Context)->GetOwner();
+ // Its assumed that all GasaObjects have an outer actor
+ else if (Context->IsA(UGasaObject::StaticClass()))
+ Actor = Cast(Context->GetOuter());
+ else
+ {
+ UObject const* Outermost = Context->GetOutermostObject();
+ if (Outermost->IsA(AActor::StaticClass()))
+ Actor = Cast(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(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;
+ }
+}
diff --git a/Project/Source/Gasa/UI/GasaHUD.cpp b/Project/Source/Gasa/UI/GasaHUD.cpp
index 5d1a502..3284826 100644
--- a/Project/Source/Gasa/UI/GasaHUD.cpp
+++ b/Project/Source/Gasa/UI/GasaHUD.cpp
@@ -8,10 +8,10 @@ using namespace Gasa;
void AGasaHUD::InitHostWidget(FWidgetControllerData const* WidgetControllerData)
{
- HostWidget = CreateWidget( GetWorld()
+ HostWidget = CreateWidget( GetWorld()
, GetDevOptions()->Template_HUD_HostUI.LoadSynchronous() );
- HostWidgetController = NewObject(this, GetDevOptions()->Template_HostWidgetController.Get());
+ HostWidgetController = NewObject(this, GetDevOptions()->Template_HostWidgetController.Get());
HostWidgetController->Data = (* WidgetControllerData);
HostWidget->SetWidgetController(HostWidgetController);
HostWidgetController->BindCallbacksToDependencies();
diff --git a/Project/Source/Gasa/UI/HostWIdgetController.cpp b/Project/Source/Gasa/UI/HostWIdgetController.cpp
index acafe03..34c3010 100644
--- a/Project/Source/Gasa/UI/HostWIdgetController.cpp
+++ b/Project/Source/Gasa/UI/HostWIdgetController.cpp
@@ -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
diff --git a/Project/Source/GasaEditor.Target.cs b/Project/Source/GasaEditor.Target.cs
index 028878a..5ed491e 100644
--- a/Project/Source/GasaEditor.Target.cs
+++ b/Project/Source/GasaEditor.Target.cs
@@ -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");
diff --git a/Project/Source/GasaGen/GasaGen.cpp b/Project/Source/GasaGen/GasaGen.cpp
index a8a1ffe..3b73540 100644
--- a/Project/Source/GasaGen/GasaGen.cpp
+++ b/Project/Source/GasaGen/GasaGen.cpp
@@ -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)
diff --git a/Project/Source/GasaGen/GasaGenCommon.cpp b/Project/Source/GasaGen/GasaGenCommon.cpp
index 5711000..1f1e09f 100644
--- a/Project/Source/GasaGen/GasaGenCommon.cpp
+++ b/Project/Source/GasaGen/GasaGenCommon.cpp
@@ -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(");
diff --git a/Project/Source/GasaGen/GasaGen_NetSlime.cpp b/Project/Source/GasaGen/GasaGen_NetSlime.cpp
new file mode 100644
index 0000000..04ea26f
--- /dev/null
+++ b/Project/Source/GasaGen/GasaGen_NetSlime.cpp
@@ -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 header_paths = Array::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();
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp
index 54ff97b..88c2a6d 100644
--- a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp
+++ b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp
@@ -272,6 +272,7 @@ void def_attribute_field_value_setters( CodeBody body, Array prope
void def_attribute_field_property_setter_inlines( CodeBody body, StrC class_name, Array 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 properties )
@@ -307,11 +309,10 @@ void def_attribute_field_initers ( CodeBody body, Array properties
void impl_attribute_fields( CodeBody body, StrC class_name, Array 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
body.append( field_impl );
}
body.append( def_pragma( txt("endregion Rep Notifies")));
+ body.append(fmt_newline);
}
inline
diff --git a/Project/Source/GasaGen/gen.cpp b/Project/Source/GasaGen/gen.cpp
index 04bdc4b..e610387 100644
--- a/Project/Source/GasaGen/gen.cpp
+++ b/Project/Source/GasaGen/gen.cpp
@@ -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::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array::Header ) ) / sizeof( Token ) );
defines_map_arena = Arena_256KB::init();
- defines = HashTable::init( defines_map_arena );
+ defines = HashTable::init_reserve( defines_map_arena, 256 );
}
internal void deinit()
diff --git a/Project/Source/GasaGen/gen.dep.hpp b/Project/Source/GasaGen/gen.dep.hpp
index a0423a5..cf2d6d5 100644
--- a/Project/Source/GasaGen/gen.dep.hpp
+++ b/Project/Source/GasaGen/gen.dep.hpp
@@ -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 result = { { nullptr }, { nullptr } };
-
- result.Hashes = Array::init( allocator );
- result.Entries = Array::init( allocator );
-
+ HashTable result = init_reserve(allocator, 8);
return result;
}
@@ -1834,21 +1832,19 @@ struct HashTable
{
HashTable result = { { nullptr }, { nullptr } };
- result.Hashes = Array::init_reserve( allocator, num );
+ result.Hashes = Array::init_reserve( allocator, num );
result.Hashes.get_header()->Num = num;
+ result.Hashes.resize( num );
+ result.Hashes.fill( 0, num, -1);
- result.Entries = Array::init_reserve( allocator, num );
-
+ result.Entries = Array::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 new_ht = init_reserve( Hashes.get_header()->Allocator, new_num );
-
- Array::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 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 )