diff --git a/Game/Config/DefaultSpatialGDKEditorSettings.ini b/Game/Config/DefaultSpatialGDKEditorSettings.ini new file mode 100644 index 00000000..ba37e33e --- /dev/null +++ b/Game/Config/DefaultSpatialGDKEditorSettings.ini @@ -0,0 +1,9 @@ + +[/Script/SpatialGDKEditor.SpatialGDKEditorSettings] +bDeleteDynamicEntities=True +bGenerateDefaultLaunchConfig=True +bStopSpatialOnExit=False +SpatialOSSnapshotFile=default.snapshot +LaunchConfigDesc=(Template="small",World=(Dimensions=(X=2000,Y=2000),ChunkEdgeLengthMeters=5,StreamingQueryIntervalSeconds=4,SnapshotWritePeriodSeconds=0,LegacyFlags=(("bridge_qos_max_timeout", "0"),("bridge_soft_handover_enabled", "false"),("enable_chunk_interest", "false")),LegacyJavaParams=()),Workers=((WorkerTypeName="UnrealWorker",Columns=1))) +bGeneratePlaceholderEntitiesInSnapshot=True + diff --git a/Game/Source/GDKShooter/Private/Characters/Core/GDKCharacter.cpp b/Game/Source/GDKShooter/Private/Characters/Core/GDKCharacter.cpp index 9380bb7e..2cac8ed5 100644 --- a/Game/Source/GDKShooter/Private/Characters/Core/GDKCharacter.cpp +++ b/Game/Source/GDKShooter/Private/Characters/Core/GDKCharacter.cpp @@ -195,10 +195,7 @@ void AGDKCharacter::RegenerateHealth() float AGDKCharacter::TakeDamage(float Damage, const FDamageEvent& DamageEvent, AController* EventInstigator, AActor* DamageCauser) { - - float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser); - - TakeDamageCrossServer(ActualDamage, DamageEvent, EventInstigator, DamageCauser); + TakeDamageCrossServer(Damage, DamageEvent, EventInstigator, DamageCauser); return Damage; } @@ -210,6 +207,10 @@ void AGDKCharacter::TakeDamageCrossServer_Implementation(float Damage, const FDa return; } + check(DamageCauser); + + Damage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser); + const AWeapon* DamageSourceWeapon = Cast(DamageCauser); const AGDKCharacter* Killer = Cast(DamageSourceWeapon->GetWeilder()); diff --git a/Game/Source/GDKShooter/Private/Characters/Core/GDKShooterCharacter.cpp b/Game/Source/GDKShooter/Private/Characters/Core/GDKShooterCharacter.cpp index 37d59ee1..ab650d01 100644 --- a/Game/Source/GDKShooter/Private/Characters/Core/GDKShooterCharacter.cpp +++ b/Game/Source/GDKShooter/Private/Characters/Core/GDKShooterCharacter.cpp @@ -248,7 +248,11 @@ void AGDKShooterCharacter::EquipWeapon_Implementation(int32 WeaponId) EquippedWeapon->SetIsActive(false); EquippedWeapon = AvailableWeapons[WeaponId]; - EquippedWeapon->SetIsActive(true); + + if (EquippedWeapon != nullptr) + { + EquippedWeapon->SetIsActive(true); + } } diff --git a/Game/Source/GDKShooter/Private/Deployments/DeploymentsPlayerController.cpp b/Game/Source/GDKShooter/Private/Deployments/DeploymentsPlayerController.cpp index f1944a64..c4b30128 100644 --- a/Game/Source/GDKShooter/Private/Deployments/DeploymentsPlayerController.cpp +++ b/Game/Source/GDKShooter/Private/Deployments/DeploymentsPlayerController.cpp @@ -76,7 +76,7 @@ void ADeploymentsPlayerController::QueryPIT() { Worker_Alpha_PlayerIdentityTokenRequest* PITParams = new Worker_Alpha_PlayerIdentityTokenRequest(); // Replace this string with a dev auth token, see docs for information on how to generate one of these - PITParams->development_authentication_token_id = "REPLACE ME"; + PITParams->development_authentication_token = "REPLACE ME"; PITParams->player_id = "Player Id"; PITParams->display_name = ""; PITParams->metadata = ""; diff --git a/Game/Source/GDKShooter/Private/Game/GDKSessionGameState.cpp b/Game/Source/GDKShooter/Private/Game/GDKSessionGameState.cpp index 0ca88a0f..62419446 100644 --- a/Game/Source/GDKShooter/Private/Game/GDKSessionGameState.cpp +++ b/Game/Source/GDKShooter/Private/Game/GDKSessionGameState.cpp @@ -2,13 +2,14 @@ #include "GDKSessionGameState.h" +#include "Interop/SpatialStaticComponentView.h" #include "TimerManager.h" #include "UnrealNetwork.h" #include "SpatialNetDriver.h" #include "SpatialWorkerConnection.h" -#include "c_worker.h" -#include "c_schema.h" +#include +#include void AGDKSessionGameState::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { @@ -44,6 +45,7 @@ void AGDKSessionGameState::RemovePlayerState(APlayerState* PlayerState) void AGDKSessionGameState::OnRep_SessionProgress() { + SendStateUpdate(SessionProgress); TimerEvent.Broadcast(SessionProgress, SessionTimer); } @@ -64,7 +66,9 @@ void AGDKSessionGameState::BeginTimer() void AGDKSessionGameState::TickGameTimer() { - if (GetNetMode() != NM_Client) + const bool bAuthoritativeOverSessionProgress = HasAuthority(); + + if (GetNetMode() != NM_Client && bAuthoritativeOverSessionProgress) { SessionTimer--; @@ -72,39 +76,51 @@ void AGDKSessionGameState::TickGameTimer() { UE_LOG(LogGDK, Log, TEXT("Advance GameState to Running")); SessionProgress = EGDKSessionProgress::Running; - SendStateUpdate(2); + SendStateUpdate(SessionProgress); SessionTimer = GameSessionLength; } if (SessionProgress == EGDKSessionProgress::Running && SessionTimer <= 0) { UE_LOG(LogGDK, Log, TEXT("Advance GameState to Results")); SessionProgress = EGDKSessionProgress::Results; - SendStateUpdate(3); + SendStateUpdate(SessionProgress); SessionTimer = ResultsSessionLength; } if (SessionProgress == EGDKSessionProgress::Results && SessionTimer <= 0) { UE_LOG(LogGDK, Log, TEXT("Advance GameState to Finished")); SessionProgress = EGDKSessionProgress::Finished; - SendStateUpdate(4); + SendStateUpdate(SessionProgress); } } } -void AGDKSessionGameState::SendStateUpdate(int NewState) +void AGDKSessionGameState::SendStateUpdate(EGDKSessionProgress SessionProgressState) { - if (!GetWorld()->GetNetDriver() || !GetWorld()->GetNetDriver()->IsA()) + // Only send the state update if we're using Spatial networking and if we have authority over the session entity. + UNetDriver* NetDriver = GetWorld()->GetNetDriver(); + if (NetDriver == nullptr || !NetDriver->IsA()) { return; } - Worker_EntityId target_entity_id = 39; + USpatialNetDriver* SpatialNetDriver = Cast(NetDriver); + bool bAuthoritativeOverSessionEntity = SpatialNetDriver->StaticComponentView->HasAuthority(SessionEntityId, SessionComponentId); + if (!bAuthoritativeOverSessionEntity) + { + return; + } + + // There's an offset of 1 between the corresponding states of session progress and session state. + int SessionState = static_cast(SessionProgressState) + 1; + + Worker_EntityId target_entity_id = SessionEntityId; Worker_ComponentUpdate component_update = {}; - component_update.component_id = 1000; - component_update.schema_type = Schema_CreateComponentUpdate(1000); + component_update.component_id = SessionComponentId; + component_update.schema_type = Schema_CreateComponentUpdate(SessionComponentId); Schema_Object* fields_object = Schema_GetComponentUpdateFields(component_update.schema_type); - Schema_AddInt32(fields_object, 1, NewState); - Cast(GetWorld()->GetNetDriver())->Connection->SendComponentUpdate(target_entity_id, &component_update); + Schema_AddInt32(fields_object, 1, SessionState); + SpatialNetDriver->Connection->SendComponentUpdate(target_entity_id, &component_update); } diff --git a/Game/Source/GDKShooter/Private/Weapons/Projectile.cpp b/Game/Source/GDKShooter/Private/Weapons/Projectile.cpp index c8cd1ad6..f252bf1d 100644 --- a/Game/Source/GDKShooter/Private/Weapons/Projectile.cpp +++ b/Game/Source/GDKShooter/Private/Weapons/Projectile.cpp @@ -80,7 +80,7 @@ void AProjectile::OnRep_MetaData() void AProjectile::Tick(float DeltaTime) { - if (GetNetMode() == NM_Client) + if (!HasAuthority()) { return; } @@ -95,7 +95,7 @@ void AProjectile::Tick(float DeltaTime) void AProjectile::OnStop(const FHitResult& ImpactResult) { - if (GetNetMode() == NM_Client) + if (!HasAuthority()) { return; } @@ -108,7 +108,7 @@ void AProjectile::OnStop(const FHitResult& ImpactResult) void AProjectile::OnBounce(const FHitResult& ImpactResult, const FVector& ImpactVelocity) { - if (GetNetMode() == NM_Client) + if (!HasAuthority()) { return; } @@ -133,7 +133,7 @@ void AProjectile::ExplosionVisuals_Implementation() void AProjectile::Explode() { - if (GetNetMode() == NM_Client) + if (!HasAuthority()) { return; } diff --git a/Game/Source/GDKShooter/Public/Game/GDKSessionGameState.h b/Game/Source/GDKShooter/Public/Game/GDKSessionGameState.h index ff8f80e2..990d2ea6 100644 --- a/Game/Source/GDKShooter/Public/Game/GDKSessionGameState.h +++ b/Game/Source/GDKShooter/Public/Game/GDKSessionGameState.h @@ -6,6 +6,9 @@ #include "GDKLogging.h" #include "Game/GDKGameState.h" #include "GDKSessionProgress.h" + +#include + #include "GDKSessionGameState.generated.h" DECLARE_EVENT_TwoParams(AGDKGameState, FSessionTimerEvent, EGDKSessionProgress, int); @@ -42,6 +45,9 @@ class GDKSHOOTER_API AGDKSessionGameState : public AGDKGameState UPROPERTY(EditAnywhere, Category = "Timers") int ResultsSessionLength = 60; + + Worker_EntityId SessionEntityId = 39; + Worker_ComponentId SessionComponentId = 1000; FTimerHandle TickTimer; @@ -55,7 +61,7 @@ class GDKSHOOTER_API AGDKSessionGameState : public AGDKGameState void TickGameTimer(); // Send a component update to the session manager entity to be picked up by the deployment manager - void SendStateUpdate(int NewState); + void SendStateUpdate(EGDKSessionProgress SessionProgressState); // Begin progressing through the different stages of game session, if not already started void BeginTimer(); diff --git a/Game/Source/GDKShooter/Public/Weapons/Projectile.h b/Game/Source/GDKShooter/Public/Weapons/Projectile.h index 74ee0937..3cec7f88 100644 --- a/Game/Source/GDKShooter/Public/Weapons/Projectile.h +++ b/Game/Source/GDKShooter/Public/Weapons/Projectile.h @@ -75,8 +75,10 @@ class GDKSHOOTER_API AProjectile : public AActor UFUNCTION(BlueprintNativeEvent) void ExplosionVisuals(); + UPROPERTY(Handover) AController* InstigatingController; + UPROPERTY(Handover) AWeapon* InstigatingWeapon; UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = Projectile)