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