// Aesir Interactive GmbH, (c) 2019 #pragma once #include "CoreMinimal.h" #include "Templates/Casts.h" /* namespace TIsUBaseInterfacePrivate { template struct TIsUBaseInterfaceImpl; template<> struct TIsUBaseInterfaceImpl { enum { Value = false };}; template<> struct TIsUBaseInterfaceImpl { enum { Value = true };}; } template struct TIsUBaseInterface { enum { Value = TIsUBaseInterfacePrivate::TIsUBaseInterfaceImpl::Value}; }; */ struct CHasStaticUClass { template auto Requires() -> decltype( ObjectClass::StaticClass() ); }; struct CHasInterfaceUClass { template auto Requires() -> decltype( InterfaceClass::UClassType::StaticClass() ); }; template struct TIsUInterface { enum { Value = !TModels::Value && TModels::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 bool IsServiceProvided() const { const UClass* ServiceUClass = GetServiceUClass(); return Services.Contains(ServiceUClass); } /** * @return the registered service cast to ServiceClass or nullptr if the service is not registered or the cast failed */ template typename TEnableIf::Value, ServiceType*>::Type GetService() const { const UClass* ServiceUClass = GetServiceUClass(); UObject* const* ServiceInstance = Services.Find(ServiceUClass); if (!ServiceInstance) return nullptr; return Cast(*ServiceInstance); } /** * @return the registered service as TScriptInterface or nullptr if the service is not registered or the cast failed */ template typename TEnableIf::Value, TScriptInterface>::Type GetService() const { const UClass* ServiceUClass = GetServiceUClass(); UObject* const* ServiceInstance = Services.Find(ServiceUClass); if (!ServiceInstance) return nullptr; return TScriptInterface(&**ServiceInstance); } /** * @return the registered service cast to ServiceClass. Crashes if the service is not available. */ template typename TEnableIf::Value, ServiceClass*>::Type GetServiceChecked() const { const UClass* ServiceUClass = GetServiceUClass(); UObject *const* ServiceObject = Services.Find(ServiceUClass); checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName()); UObject *const ServiceCast = Cast(*ServiceObject); checkf(IsValid(ServiceCast), TEXT("Object %s is not of type %s"), *((*ServiceObject)->GetName()), *ServiceUClass->GetName()); return Cast(*ServiceObject); } /** * @return the registered service cast to ServiceClass. Crashes if the service is not available. */ template typename TEnableIf::Value, TScriptInterface>::Type GetServiceChecked() const { const UClass* ServiceUClass = GetServiceUClass(); UObject *const* ServiceObject = Services.Find(ServiceUClass); checkf(ServiceObject, TEXT("No object was found for service %s"), *ServiceUClass->GetName()); checkf(Cast(ServiceObject), TEXT("Object %s does not implement %s"), *(*ServiceObject)->GetName(), *ServiceUClass->GetName()); return TScriptInterface(*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 typename TEnableIf::Value, void>::Type ProvideService(ChildType* ServiceInstance) { const UClass* ServiceUClass = GetServiceUClass(); 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 void WithdrawService(ServiceClass* ServiceInstance) { const UClass* ServiceClassObject = GetServiceUClass(); ensureMsgf(GetService() == 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 void WaitForService(FServiceRegisteredEvent::FDelegate Callback) { const UClass* ServiceUClass = GetServiceUClass(); ServiceRegisteredCallbacks.FindOrAdd(ServiceUClass).Add(Callback); } template struct ServiceTypeMapper { using MappedType = decltype(DeclVal().GetService()); }; template auto GetServices() -> decltype(auto) { return MakeTuple::MappedType...>(GetService()...); } 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 static typename TEnableIf::Value,const UClass*>::Type GetServiceUClass() { return ServiceInterface::UClassType::StaticClass(); } /** This statically gets the UClass from any given UObject-Type */ template static typename TEnableIf::Value,const UClass*>::Type GetServiceUClass() { return ServiceClass::StaticClass(); } TMap ServiceRegisteredCallbacks; TMap Services; };