Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1304 from spatialos/0.6.1-hotfix
Browse files Browse the repository at this point in the history
0.6.1 hotfix
  • Loading branch information
improbable-valy authored Aug 15, 2019
2 parents 6bc4fa6 + e2539b2 commit caa2981
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 56 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased-`x.y.z`] - 2019-xx-xx

## [`0.6.1`] - 2019-08-15

### Features:
- The [Multiserver zoning shooter tutorial](https://docs.improbable.io/unreal/alpha/content/tutorials/multiserver-shooter/tutorial-multiserver-intro) has been updated to use the Example Project.

### Bug fixes:
- Simulated player launch configurations are no longer invalid when the GDK is installed as an Engine Plugin.
- RPCs that have been queued for execution for more than 1 second (the default value in `SpatialGDKSettings QueuedIncomingRPCWaitTime`) are now executed even if there are unresolved parameters. This stops unresolved parameters from blocking the execution queue.
- Offloading is no longer enabled by default in the Example Project. You can toggle offloading on using [these steps](https://docs.improbable.io/unreal/alpha/content/tutorials/offloading-tutorial/offloading-setup#step-4-enable-offloading).
- Guns no longer intermittently detatch from simulated players in the Example Project.
- Default cloud deployment settings are now correctly set. This means you don't need to manually reset them before doing a cloud deployment.

## [`0.6.0`] - 2019-07-31

### Breaking Changes:
Expand Down
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@

![](SpatialGDK/Documentation/spatialos-gdkforunreal-header.png)

The SpatialOS Game Development Kit (GDK) for Unreal is an Unreal Engine plugin which gives you the features of [SpatialOS](https://spatialos.improbable.io/docs/reference/latest), within the familiar workflows and APIs of Unreal Engine. For more information, please see the GDK's [documentation website](https://docs.improbable.io/unreal/latest).

If you’re an Unreal game developer and you’re ready to try out the GDK, follow the [Get started guide](https://docs.improbable.io/unreal/latest/content/get-started/introduction).
The SpatialOS Game Development Kit (GDK) for Unreal is an Unreal Engine fork and plugin with associated projects. It gives you the features of [SpatialOS](https://spatialos.improbable.io/docs/reference/latest), within the familiar workflows and APIs of Unreal Engine. For more information, please see the GDK's [documentation website](https://docs.improbable.io/unreal/latest).

> The SpatialOS GDK for Unreal is in alpha. It is ready to use for development of single-server games, but not recommended for public releases. We are committed to rapid development of the GDK to provide a performant release - for information on this, see our [development roadmap](https://github.com/spatialos/UnrealGDK/projects/1) and [Unreal features support](https://docs.improbable.io/unreal/latest/unreal-features-support) pages, and contact us via our forums, or on Discord.
## Where to get the GDK and related projects
The GDK and its related projects are available on GitHub.
* [GDK: github.com/spatialos/UnrealGDK](https://github.com/spatialos/UnrealGDK)
This is the repository for the GDK plugin, which includes the Starter Template (a blank starter project).

In addition to the plugin, the GDK also includes:

* [The SpatialOS Unreal Engine fork](https://github.com/improbableio/UnrealEngine)
* [The Example Project](https://github.com/spatialos/UnrealGDKExampleProject)

## Unreal Engine changes
In order to transform Unreal from a single server engine to a distributed model, we have made a number of small changes to the UE4 code. We will attempt to consolidate and remove (or submit as PR to Epic) as many of these changes as possible. You can see the changes in our forked [Unreal Engine repo](https://github.com/improbableio/UnrealEngine).
You must be a member of the [Epic Games organization](https://github.com/EpicGames) on GitHub to access this. If you aren't, the link returns a 404 error.
* [The Example Project](https://github.com/spatialos/UnrealGDKExampleProject)

If you’re an Unreal game developer and you’re ready to try out the GDK, follow the [Get started guide](https://docs.improbable.io/unreal/latest/content/get-started/introduction).

## SpatialOS Unreal Engine fork changes
In order to transform Unreal from a single-server engine to a distributed model, we made a number of small changes to Unreal Engine code. We are attempting to consolidate and remove (or submit as PR to Epic) as many of these changes as possible. You can see the changes in the [SpatialOS Unreal Engine fork repository](https://github.com/improbableio/UnrealEngine).

> In order to get access to this fork, you need to link your GitHub account to a verified Epic Games account, and to have agreed to Epic's license. You will not be able to use the GDK for Unreal without doing this first. To do this, see the [Unreal documentation](https://www.unrealengine.com/en-US/ue4-on-github).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
#include "EngineClasses/SpatialNetConnection.h"
#include "EngineClasses/SpatialNetDriver.h"
#include "EngineClasses/SpatialPackageMapClient.h"
#include "Interop/SpatialSender.h"
#include "Interop/SpatialReceiver.h"
#include "Interop/GlobalStateManager.h"
#include "Interop/SpatialReceiver.h"
#include "Interop/SpatialSender.h"
#include "Schema/ClientRPCEndpoint.h"
#include "Schema/ServerRPCEndpoint.h"
#include "SpatialConstants.h"
#include "SpatialGDKSettings.h"
#include "Utils/RepLayoutUtils.h"
Expand Down Expand Up @@ -77,7 +79,6 @@ USpatialActorChannel::USpatialActorChannel(const FObjectInitializer& ObjectIniti
, bCreatingNewEntity(false)
, EntityId(SpatialConstants::INVALID_ENTITY_ID)
, bInterestDirty(false)
, bIsListening(false)
, bNetOwned(false)
, NetDriver(nullptr)
, LastPositionSinceUpdate(FVector::ZeroVector)
Expand Down Expand Up @@ -549,6 +550,26 @@ void USpatialActorChannel::DynamicallyAttachSubobject(UObject* Object)
}
}

bool USpatialActorChannel::IsListening() const
{
if (NetDriver->IsServer())
{
if (SpatialGDK::ClientRPCEndpoint* Endpoint = NetDriver->StaticComponentView->GetComponentData<SpatialGDK::ClientRPCEndpoint>(EntityId))
{
return Endpoint->bReady;
}
}
else
{
if (SpatialGDK::ServerRPCEndpoint* Endpoint = NetDriver->StaticComponentView->GetComponentData<SpatialGDK::ServerRPCEndpoint>(EntityId))
{
return Endpoint->bReady;
}
}

return false;
}

const FClassInfo* USpatialActorChannel::TryResolveNewDynamicSubobjectAndGetClassInfo(UObject* Object)
{
const FClassInfo* Info = nullptr;
Expand Down Expand Up @@ -1014,6 +1035,21 @@ void USpatialActorChannel::ServerProcessOwnershipChange()
return;
}

UpdateEntityACLToNewOwner();

for (AActor* Child : Actor->Children)
{
Worker_EntityId ChildEntityId = NetDriver->PackageMap->GetEntityIdFromObject(Child);

if (USpatialActorChannel* Channel = NetDriver->GetActorChannelByEntityId(ChildEntityId))
{
Channel->ServerProcessOwnershipChange();
}
}
}

void USpatialActorChannel::UpdateEntityACLToNewOwner()
{
FString NewOwnerWorkerAttribute = SpatialGDK::GetOwnerWorkerAttribute(Actor);

if (SavedOwnerWorkerAttribute != NewOwnerWorkerAttribute)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ FNetworkGUID USpatialPackageMapClient::TryResolveObjectAsEntity(UObject* Value)
}

AActor* Actor = Value->IsA<AActor>() ? Cast<AActor>(Value) : Cast<AActor>(Value->GetOuter());
if (!Actor->GetIsReplicated())
{
return NetGUID;
}

if (Actor->GetClass()->HasAnySpatialClassFlags(SPATIALCLASS_Singleton))
{
Expand Down
47 changes: 13 additions & 34 deletions SpatialGDK/Source/SpatialGDK/Private/Interop/SpatialReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ void USpatialReceiver::OnAddComponent(const Worker_AddComponentOp& Op)
case SpatialConstants::RPCS_ON_ENTITY_CREATION_ID:
case SpatialConstants::DEBUG_METRICS_COMPONENT_ID:
case SpatialConstants::ALWAYS_RELEVANT_COMPONENT_ID:
case SpatialConstants::CLIENT_RPC_ENDPOINT_COMPONENT_ID:
case SpatialConstants::SERVER_RPC_ENDPOINT_COMPONENT_ID:
// Ignore static spatial components as they are managed by the SpatialStaticComponentView.
return;
case SpatialConstants::SINGLETON_MANAGER_COMPONENT_ID:
Expand All @@ -131,11 +133,6 @@ void USpatialReceiver::OnAddComponent(const Worker_AddComponentOp& Op)
case SpatialConstants::STARTUP_ACTOR_MANAGER_COMPONENT_ID:
GlobalStateManager->ApplyStartupActorManagerData(Op.data);
return;
case SpatialConstants::CLIENT_RPC_ENDPOINT_COMPONENT_ID:
case SpatialConstants::SERVER_RPC_ENDPOINT_COMPONENT_ID:
Schema_Object* FieldsObject = Schema_GetComponentDataFields(Op.data.schema_type);
RegisterListeningEntityIfReady(Op.entity_id, FieldsObject);
return;
}

if (ClassInfoManager->IsSublevelComponent(Op.data.component_id))
Expand Down Expand Up @@ -1058,13 +1055,6 @@ void USpatialReceiver::ApplyComponentData(UObject* TargetObject, USpatialActorCh

void USpatialReceiver::OnComponentUpdate(const Worker_ComponentUpdateOp& Op)
{
if (Op.update.component_id == SpatialConstants::SERVER_RPC_ENDPOINT_COMPONENT_ID ||
Op.update.component_id == SpatialConstants::CLIENT_RPC_ENDPOINT_COMPONENT_ID)
{
Schema_Object* FieldsObject = Schema_GetComponentUpdateFields(Op.update.schema_type);
RegisterListeningEntityIfReady(Op.entity_id, FieldsObject);
}

if (StaticComponentView->GetAuthority(Op.entity_id, Op.update.component_id) == WORKER_AUTHORITY_AUTHORITATIVE)
{
UE_LOG(LogSpatialReceiver, Verbose, TEXT("Entity: %d Component: %d - Skipping update because this was short circuited"), Op.entity_id, Op.update.component_id);
Expand Down Expand Up @@ -1451,26 +1441,7 @@ void USpatialReceiver::ApplyComponentUpdate(const Worker_ComponentUpdate& Compon
QueueIncomingRepUpdates(ChannelObjectPair, ObjectReferencesMap, UnresolvedRefs);
}

void USpatialReceiver::RegisterListeningEntityIfReady(Worker_EntityId EntityId, Schema_Object* Object)
{
if (Schema_GetBoolCount(Object, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID) > 0)
{
bool bReady = GetBoolFromSchema(Object, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID);
if (bReady)
{
if (USpatialActorChannel* Channel = NetDriver->GetActorChannelByEntityId(EntityId))
{
Channel->StartListening();
if (UObject* TargetObject = Channel->GetActor())
{
Sender->SendOutgoingRPCs();
}
}
}
}
}

bool USpatialReceiver::ApplyRPC(UObject* TargetObject, UFunction* Function, const RPCPayload& Payload, const FString& SenderWorkerId)
bool USpatialReceiver::ApplyRPC(UObject* TargetObject, UFunction* Function, const RPCPayload& Payload, const FString& SenderWorkerId, bool bApplyWithUnresolvedRefs /* = false */)
{
bool bApplied = false;

Expand All @@ -1494,7 +1465,7 @@ bool USpatialReceiver::ApplyRPC(UObject* TargetObject, UFunction* Function, cons
TSharedPtr<FRepLayout> RepLayout = NetDriver->GetFunctionRepLayout(Function);
RepLayout_ReceivePropertiesForRPC(*RepLayout, PayloadReader, Parms);

if (UnresolvedRefs.Num() == 0)
if ((UnresolvedRefs.Num() == 0) || bApplyWithUnresolvedRefs)
{
if (GetDefault<USpatialGDKSettings>()->bCheckRPCOrder)
{
Expand Down Expand Up @@ -1538,7 +1509,15 @@ bool USpatialReceiver::ApplyRPC(const FPendingRPCParams& Params)
return false;
}

return ApplyRPC(TargetObjectWeakPtr.Get(), Function, Params.Payload, FString{});
bool bApplyWithUnresolvedRefs = false;
const float TimeDiff = (FDateTime::Now() - Params.QueuedTimestamp).GetTotalSeconds();
if (GetDefault<USpatialGDKSettings>()->QueuedIncomingRPCWaitTime < TimeDiff)
{
UE_LOG(LogSpatialReceiver, Warning, TEXT("Executing RPC %s::%s with unresolved references after %f seconds of queueing"), *TargetObjectWeakPtr->GetName(), *Function->GetName(), TimeDiff);
bApplyWithUnresolvedRefs = true;
}

return ApplyRPC(TargetObjectWeakPtr.Get(), Function, Params.Payload, FString{}, bApplyWithUnresolvedRefs);
}

void USpatialReceiver::OnReserveEntityIdsResponse(const Worker_ReserveEntityIdsResponseOp& Op)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

#include "Interop/SpatialStaticComponentView.h"

#include "Schema/ClientRPCEndpoint.h"
#include "Schema/Component.h"
#include "Schema/Heartbeat.h"
#include "Schema/Interest.h"
#include "Schema/RPCPayload.h"
#include "Schema/ServerRPCEndpoint.h"
#include "Schema/Singleton.h"
#include "Schema/SpawnData.h"

Expand Down Expand Up @@ -73,6 +75,12 @@ void USpatialStaticComponentView::OnAddComponent(const Worker_AddComponentOp& Op
case SpatialConstants::RPCS_ON_ENTITY_CREATION_ID:
Data = MakeUnique<SpatialGDK::ComponentStorage<SpatialGDK::RPCsOnEntityCreation>>(Op.data);
break;
case SpatialConstants::CLIENT_RPC_ENDPOINT_COMPONENT_ID:
Data = MakeUnique<SpatialGDK::ComponentStorage<SpatialGDK::ClientRPCEndpoint>>(Op.data);
break;
case SpatialConstants::SERVER_RPC_ENDPOINT_COMPONENT_ID:
Data = MakeUnique<SpatialGDK::ComponentStorage<SpatialGDK::ServerRPCEndpoint>>(Op.data);
break;
default:
// Component is not hand written, but we still want to know the existence of it on this entity.
Data = nullptr;
Expand Down Expand Up @@ -111,6 +119,12 @@ void USpatialStaticComponentView::OnComponentUpdate(const Worker_ComponentUpdate
case SpatialConstants::POSITION_COMPONENT_ID:
Component = GetComponentData<SpatialGDK::Position>(Op.entity_id);
break;
case SpatialConstants::CLIENT_RPC_ENDPOINT_COMPONENT_ID:
Component = GetComponentData<SpatialGDK::ClientRPCEndpoint>(Op.entity_id);
break;
case SpatialConstants::SERVER_RPC_ENDPOINT_COMPONENT_ID:
Component = GetComponentData<SpatialGDK::ServerRPCEndpoint>(Op.entity_id);
break;
default:
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ USpatialGDKSettings::USpatialGDKSettings(const FObjectInitializer& ObjectInitial
, OpsUpdateRate(1000.0f)
, bEnableHandover(true)
, MaxNetCullDistanceSquared(900000000.0f) // Set to twice the default Actor NetCullDistanceSquared (300m)
, QueuedIncomingRPCWaitTime(1.0f)
, bUsingQBI(true)
, PositionUpdateFrequency(1.0f)
, PositionDistanceThreshold(100.0f) // 1m (100cm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ FPendingRPCParams::FPendingRPCParams(const FUnrealObjectRef& InTargetObjectRef,
: ReliableRPCIndex(InReliableRPCIndex)
, ObjectRef(InTargetObjectRef)
, Payload(MoveTemp(InPayload))
, QueuedTimestamp(FDateTime::Now())
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ class SPATIALGDK_API USpatialActorChannel : public UActorChannel
FORCEINLINE void MarkInterestDirty() { bInterestDirty = true; }
FORCEINLINE bool GetInterestDirty() const { return bInterestDirty; }

FORCEINLINE void StartListening() { bIsListening = true; }
FORCEINLINE bool IsListening() { return bIsListening; }
bool IsListening() const;
const FClassInfo* TryResolveNewDynamicSubobjectAndGetClassInfo(UObject* Object);

protected:
Expand All @@ -174,6 +173,8 @@ class SPATIALGDK_API USpatialActorChannel : public UActorChannel

void InitializeHandoverShadowData(TArray<uint8>& ShadowData, UObject* Object);
FHandoverChangeState GetHandoverChangeList(TArray<uint8>& ShadowData, UObject* Object);

void UpdateEntityACLToNewOwner();

public:
// If this actor channel is responsible for creating a new entity, this will be set to true once the entity is created.
Expand All @@ -187,7 +188,6 @@ class SPATIALGDK_API USpatialActorChannel : public UActorChannel
private:
Worker_EntityId EntityId;
bool bInterestDirty;
bool bIsListening;

// Used on the client to track gaining/losing ownership.
bool bNetOwned;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,8 @@ class USpatialReceiver : public UObject

void ApplyComponentUpdate(const Worker_ComponentUpdate& ComponentUpdate, UObject* TargetObject, USpatialActorChannel* Channel, bool bIsHandover);

void RegisterListeningEntityIfReady(Worker_EntityId EntityId, Schema_Object* Object);

bool ApplyRPC(const FPendingRPCParams& Params);
bool ApplyRPC(UObject* TargetObject, UFunction* Function, const SpatialGDK::RPCPayload& Payload, const FString& SenderWorkerId);
bool ApplyRPC(UObject* TargetObject, UFunction* Function, const SpatialGDK::RPCPayload& Payload, const FString& SenderWorkerId, bool bApplyWithUnresolvedRefs = false);

void ReceiveCommandResponse(const Worker_CommandResponseOp& Op);

Expand Down
15 changes: 15 additions & 0 deletions SpatialGDK/Source/SpatialGDK/Public/Schema/ClientRPCEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ struct ClientRPCEndpoint : Component

ClientRPCEndpoint() = default;

ClientRPCEndpoint(const Worker_ComponentData& Data)
{
Schema_Object* EndpointObject = Schema_GetComponentDataFields(Data.schema_type);
bReady = GetBoolFromSchema(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID);
}

void ApplyComponentUpdate(const Worker_ComponentUpdate& Update)
{
Schema_Object* EndpointObject = Schema_GetComponentUpdateFields(Update.schema_type);
if (Schema_GetBoolCount(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID) > 0)
{
bReady = GetBoolFromSchema(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID);
}
}

Worker_ComponentData CreateRPCEndpointData()
{
Worker_ComponentData Data{};
Expand Down
15 changes: 15 additions & 0 deletions SpatialGDK/Source/SpatialGDK/Public/Schema/ServerRPCEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ struct ServerRPCEndpoint : Component

ServerRPCEndpoint() = default;

ServerRPCEndpoint(const Worker_ComponentData& Data)
{
Schema_Object* EndpointObject = Schema_GetComponentDataFields(Data.schema_type);
bReady = GetBoolFromSchema(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID);
}

void ApplyComponentUpdate(const Worker_ComponentUpdate& Update)
{
Schema_Object* EndpointObject = Schema_GetComponentUpdateFields(Update.schema_type);
if (Schema_GetBoolCount(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID) > 0)
{
bReady = GetBoolFromSchema(EndpointObject, SpatialConstants::UNREAL_RPC_ENDPOINT_READY_ID);
}
}

Worker_ComponentData CreateRPCEndpointData()
{
Worker_ComponentData Data{};
Expand Down
4 changes: 4 additions & 0 deletions SpatialGDK/Source/SpatialGDK/Public/SpatialGDKSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class SPATIALGDK_API USpatialGDKSettings : public UObject
UPROPERTY(EditAnywhere, config, Category = "Replication", meta = (ConfigRestartRequired = false))
float MaxNetCullDistanceSquared;

/** Seconds to wait before executing a received RPC substituting nullptr for unresolved UObjects*/
UPROPERTY(EditAnywhere, config, Category = "Replication", meta = (ConfigRestartRequired = false, DisplayName = "Wait Time Before Processing Received RPC With Unresolved Refs"))
float QueuedIncomingRPCWaitTime;

/** Query Based Interest is required for level streaming and the AlwaysInterested UPROPERTY specifier to be supported when using spatial networking, however comes at a performance cost for larger-scale projects.*/
UPROPERTY(config, meta = (ConfigRestartRequired = false))
bool bUsingQBI;
Expand Down
2 changes: 2 additions & 0 deletions SpatialGDK/Source/SpatialGDK/Public/Utils/RPCContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct FPendingRPCParams
int ReliableRPCIndex;
FUnrealObjectRef ObjectRef;
SpatialGDK::RPCPayload Payload;

FDateTime QueuedTimestamp;
};

class FRPCContainer
Expand Down
Loading

0 comments on commit caa2981

Please sign in to comment.