205 lines
6.6 KiB
C++
205 lines
6.6 KiB
C++
// Aesir Interactive GmbH, (c) 2019
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Templates/Casts.h"
|
|
|
|
|
|
/*
|
|
namespace TIsUBaseInterfacePrivate
|
|
{
|
|
template<class InterfaceClass>
|
|
struct TIsUBaseInterfaceImpl;
|
|
template<>
|
|
struct TIsUBaseInterfaceImpl<bool> { enum { Value = false };};
|
|
template<>
|
|
struct TIsUBaseInterfaceImpl<UClass*> { enum { Value = true };};
|
|
}
|
|
template<class InterfaceClass>
|
|
struct TIsUBaseInterface { enum { Value = TIsUBaseInterfacePrivate::TIsUBaseInterfaceImpl<decltype(&InterfaceClass::StaticClass, true)>::Value}; };
|
|
*/
|
|
|
|
struct CHasStaticUClass
|
|
{
|
|
template<class ObjectClass>
|
|
auto Requires() -> decltype(
|
|
ObjectClass::StaticClass()
|
|
);
|
|
};
|
|
|
|
struct CHasInterfaceUClass
|
|
{
|
|
template<class InterfaceClass>
|
|
auto Requires() -> decltype(
|
|
InterfaceClass::UClassType::StaticClass()
|
|
);
|
|
};
|
|
|
|
|
|
template<class InterfaceClass>
|
|
struct TIsUInterface
|
|
{
|
|
enum
|
|
{
|
|
Value = !TModels<CHasStaticUClass, InterfaceClass>::Value && TModels<CHasInterfaceUClass, InterfaceClass>::Value
|
|
};
|
|
};
|
|
|
|
/**
|
|
* General purpose service locator that is available from the world settings
|
|
*/
|
|
class LYRAGAME_API FServiceLocator
|
|
{
|
|
public:
|
|
DECLARE_EVENT_OneParam(FServiceLocator, FServiceRegisteredEvent, FServiceLocator&);
|
|
|
|
/**
|
|
* @return the registered service cast to ServiceClass or nullptr if the service is not registered or the cast failed
|
|
*/
|
|
template <class ServiceClass>
|
|
bool IsServiceProvided() const
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
|
return Services.Contains(ServiceUClass);
|
|
}
|
|
|
|
/**
|
|
* @return the registered service cast to ServiceClass or nullptr if the service is not registered or the cast failed
|
|
*/
|
|
template <class ServiceType>
|
|
typename TEnableIf<!TIsIInterface<ServiceType>::Value, ServiceType*>::Type
|
|
GetService() const
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceType>();
|
|
UObject* const* ServiceInstance = Services.Find(ServiceUClass);
|
|
if (!ServiceInstance)
|
|
return nullptr;
|
|
|
|
return Cast<ServiceType>(*ServiceInstance);
|
|
}
|
|
|
|
/**
|
|
* @return the registered service as TScriptInterface or nullptr if the service is not registered or the cast failed
|
|
*/
|
|
template<class ServiceInterface>
|
|
typename TEnableIf<TIsIInterface<ServiceInterface>::Value, TScriptInterface<ServiceInterface>>::Type
|
|
GetService() const
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
|
UObject* const* ServiceInstance = Services.Find(ServiceUClass);
|
|
if (!ServiceInstance)
|
|
return nullptr;
|
|
|
|
return TScriptInterface<ServiceInterface>(&**ServiceInstance);
|
|
}
|
|
|
|
/**
|
|
* @return the registered service cast to ServiceClass. Crashes if the service is not available.
|
|
*/
|
|
template <class ServiceClass>
|
|
typename TEnableIf<!TIsIInterface<ServiceClass>::Value, ServiceClass*>::Type
|
|
GetServiceChecked() const
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
|
|
|
UObject *const* ServiceObject = Services.Find(ServiceUClass);
|
|
checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName());
|
|
UObject *const ServiceCast = Cast<ServiceClass>(*ServiceObject);
|
|
checkf(IsValid(ServiceCast), TEXT("Object %s is not of type %s"), *((*ServiceObject)->GetName()), *ServiceUClass->GetName());
|
|
return Cast<ServiceClass>(*ServiceObject);
|
|
}
|
|
|
|
/**
|
|
* @return the registered service cast to ServiceClass. Crashes if the service is not available.
|
|
*/
|
|
template<class ServiceInterface>
|
|
typename TEnableIf<TIsIInterface<ServiceInterface>::Value, TScriptInterface<ServiceInterface>>::Type
|
|
GetServiceChecked() const
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
|
|
|
UObject *const* ServiceObject = Services.Find(ServiceUClass);
|
|
checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName());
|
|
checkf(Cast<ServiceInterface>(ServiceObject), TEXT("Object %s does not implement %s"), *(*ServiceObject)->GetName(), *ServiceUClass->GetName());
|
|
return TScriptInterface<ServiceInterface>(*ServiceObject);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Registers the given object interface with the service locator.
|
|
* Will refuse to work if the type is not actually derived from the interface.
|
|
* Note: you should always explicitly provide the template list to make sure you register the service as the intended type
|
|
* @return void
|
|
*/
|
|
template <class ServiceInterface, class ChildType>
|
|
typename TEnableIf<TIsDerivedFrom<ChildType, ServiceInterface>::Value, void>::Type
|
|
ProvideService(ChildType* ServiceInstance)
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceInterface>();
|
|
Services.Emplace(ServiceUClass, ServiceInstance);
|
|
NotifyServiceProvided(ServiceUClass);
|
|
}
|
|
|
|
/**
|
|
* Unregisters the given object with the service locator
|
|
* Note: you should always explicitly provide the template list to make sure you unregister the service as the intended type
|
|
*/
|
|
template<class ServiceClass>
|
|
void WithdrawService(ServiceClass* ServiceInstance)
|
|
{
|
|
const UClass* ServiceClassObject = GetServiceUClass<ServiceClass>();
|
|
ensureMsgf(GetService<ServiceClass>() == ServiceInstance, TEXT("The service you are trying to unregister is not the one that has been provided!"));
|
|
Services.Remove(ServiceClassObject);
|
|
}
|
|
|
|
/** Waits for the service class to be registered or calls the callback immediately if the service is already registerd */
|
|
template<class ServiceClass>
|
|
void WaitForService(FServiceRegisteredEvent::FDelegate Callback)
|
|
{
|
|
const UClass* ServiceUClass = GetServiceUClass<ServiceClass>();
|
|
ServiceRegisteredCallbacks.FindOrAdd(ServiceUClass).Add(Callback);
|
|
}
|
|
|
|
template<class ServiceType>
|
|
struct ServiceTypeMapper
|
|
{
|
|
using MappedType = decltype(DeclVal<FServiceLocator>().GetService<ServiceType>());
|
|
};
|
|
|
|
template<typename...ServiceTypes>
|
|
auto GetServices() -> decltype(auto)
|
|
{
|
|
return MakeTuple<typename ServiceTypeMapper<ServiceTypes>::MappedType...>(GetService<ServiceTypes>()...);
|
|
}
|
|
|
|
private:
|
|
void NotifyServiceProvided(const UClass* ServiceUClass)
|
|
{
|
|
FServiceRegisteredEvent& ServiceRegisteredEvent = ServiceRegisteredCallbacks.FindOrAdd(ServiceUClass);
|
|
ServiceRegisteredEvent.Broadcast(*this);
|
|
ServiceRegisteredEvent.Clear();
|
|
}
|
|
|
|
/** This statically gets the UClass from any given IInterface-Type */
|
|
template <typename ServiceInterface>
|
|
static typename TEnableIf<TIsUInterface<ServiceInterface>::Value,const UClass*>::Type
|
|
GetServiceUClass()
|
|
{
|
|
return ServiceInterface::UClassType::StaticClass();
|
|
}
|
|
|
|
/** This statically gets the UClass from any given UObject-Type */
|
|
template <typename ServiceClass>
|
|
static typename TEnableIf<TModels<CHasStaticUClass, ServiceClass>::Value,const UClass*>::Type
|
|
GetServiceUClass()
|
|
{
|
|
return ServiceClass::StaticClass();
|
|
}
|
|
|
|
TMap<const UClass*, FServiceRegisteredEvent> ServiceRegisteredCallbacks;
|
|
|
|
TMap<const UClass*, UObject*> Services;
|
|
};
|