From 4f23c616f114aad9ec3c97d26411a540c557a63c Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Thu, 14 Jul 2022 04:18:17 +0700 Subject: [PATCH] Refactor how `BroadcastAPI` injects its handler This patch changes AcediaCore to only inject `BroadcastEventsObserver` when its events are actually needed. Other changes are making this change produce a log entry and adding a relevant `SideEffect` that describes this change. --- sources/BaseRealm/Global.uc | 6 +- sources/Chat/ChatAPI.uc | 17 ++++- .../API/SideEffects/SideEffectAPI.uc | 4 +- sources/Unreal/BroadcastsAPI/BroadcastAPI.uc | 51 +++++++++++++++ .../BroadcastsAPI/BroadcastSideEffect.uc | 65 +++++++++++++++++++ sources/Unreal/UnrealService.uc | 8 --- 6 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 sources/Unreal/BroadcastsAPI/BroadcastSideEffect.uc diff --git a/sources/BaseRealm/Global.uc b/sources/BaseRealm/Global.uc index 3671c34..15e96d8 100644 --- a/sources/BaseRealm/Global.uc +++ b/sources/BaseRealm/Global.uc @@ -66,13 +66,13 @@ protected function Initialize() box = BoxAPI(memory.Allocate(class'BoxAPI')); text = TextAPI(memory.Allocate(class'TextAPI')); collections = CollectionsAPI(memory.Allocate(class'CollectionsAPI')); - unreal = ServerUnrealAPI(memory.Allocate(class'ServerUnrealAPI')); - time = TimeAPI(memory.Allocate(class'TimeAPI')); logger = LoggerAPI(memory.Allocate(class'LoggerAPI')); + color = ColorAPI(memory.Allocate(class'ColorAPI')); alias = AliasesAPI(memory.Allocate(class'AliasesAPI')); + unreal = ServerUnrealAPI(memory.Allocate(class'ServerUnrealAPI')); + time = TimeAPI(memory.Allocate(class'TimeAPI')); console = ConsoleAPI(memory.Allocate(class'ConsoleAPI')); chat = ChatAPI(memory.Allocate(class'ChatAPI')); - color = ColorAPI(memory.Allocate(class'ColorAPI')); users = UserAPI(memory.Allocate(class'UserAPI')); players = PlayersAPI(memory.Allocate(class'PlayersAPI')); json = JSONAPI(memory.Allocate(class'JSONAPI')); diff --git a/sources/Chat/ChatAPI.uc b/sources/Chat/ChatAPI.uc index ffad999..540aa7f 100644 --- a/sources/Chat/ChatAPI.uc +++ b/sources/Chat/ChatAPI.uc @@ -19,6 +19,8 @@ */ class ChatAPI extends AcediaObject; +var protected bool connectedToBroadcastAPI; + var protected ChatAPI_OnMessage_Signal onMessageSignal; var protected ChatAPI_OnMessageFor_Signal onMessageForSignal; @@ -28,8 +30,6 @@ protected function Constructor() _.memory.Allocate(class'ChatAPI_OnMessage_Signal')); onMessageForSignal = ChatAPI_OnMessageFor_Signal( _.memory.Allocate(class'ChatAPI_OnMessageFor_Signal')); - _.unreal.broadcasts.OnHandleText(self).connect = HandleText; - _.unreal.broadcasts.OnHandleTextFor(self).connect = HandleTextFor; } protected function Finalizer() @@ -40,6 +40,17 @@ protected function Finalizer() onMessageForSignal = none; _.unreal.broadcasts.OnHandleText(self).Disconnect(); _.unreal.broadcasts.OnHandleTextFor(self).Disconnect(); + connectedToBroadcastAPI = false; +} + +private final function TryConnectingBroadcastSignals() +{ + if (connectedToBroadcastAPI) { + return; + } + connectedToBroadcastAPI = true; + _.unreal.broadcasts.OnHandleText(self).connect = HandleText; + _.unreal.broadcasts.OnHandleTextFor(self).connect = HandleTextFor; } /** @@ -67,6 +78,7 @@ protected function Finalizer() public function ChatAPI_OnMessage_Slot OnMessage( AcediaObject receiver) { + TryConnectingBroadcastSignals(); return ChatAPI_OnMessage_Slot(onMessageSignal.NewSlot(receiver)); } @@ -97,6 +109,7 @@ public function ChatAPI_OnMessage_Slot OnMessage( public function ChatAPI_OnMessageFor_Slot OnMessageFor( AcediaObject receiver) { + TryConnectingBroadcastSignals(); return ChatAPI_OnMessageFor_Slot(onMessageForSignal.NewSlot(receiver)); } diff --git a/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc b/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc index 396fd5d..614bde1 100644 --- a/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc +++ b/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc @@ -103,7 +103,7 @@ public final function array GetFromPackage(BaseText packageName) * active side effect. Must not be `none`. * @return `true` if new side effect was added and `false` otherwise. */ -public final function bool AddSideEffect(SideEffect newSideEffect) +public final function bool Add(SideEffect newSideEffect) { local int i; @@ -130,7 +130,7 @@ public final function bool AddSideEffect(SideEffect newSideEffect) * and `false` otherise (even if there was no side effect of specified * class to begin with). */ -public final function bool RemoveSideEffectClass( +public final function bool RemoveClass( class sideEffectClass) { local int i; diff --git a/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc b/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc index 024c9cc..23d653b 100644 --- a/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc +++ b/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc @@ -57,6 +57,8 @@ struct LocalizedMessage var Object relatedObject; }; +var private LoggerAPI.Definition infoInjectedBroadcastEventsObserver; + /** * Called before text message is sent to any player, during the check for * whether it is at all allowed to be broadcasted. Corresponds to @@ -87,7 +89,9 @@ public final function Broadcast_OnBroadcastCheck_Slot OnBroadcastCheck( { local Signal signal; local UnrealService service; + service = UnrealService(class'UnrealService'.static.Require()); + TryInjectBroadcastHandler(service); signal = service.GetSignal(class'Broadcast_OnBroadcastCheck_Signal'); return Broadcast_OnBroadcastCheck_Slot(signal.NewSlot(receiver)); } @@ -138,7 +142,9 @@ public final function Broadcast_OnHandleText_Slot OnHandleText( { local Signal signal; local UnrealService service; + service = UnrealService(class'UnrealService'.static.Require()); + TryInjectBroadcastHandler(service); signal = service.GetSignal(class'Broadcast_OnHandleText_Signal'); return Broadcast_OnHandleText_Slot(signal.NewSlot(receiver)); } @@ -176,7 +182,9 @@ public final function Broadcast_OnHandleTextFor_Slot OnHandleTextFor( { local Signal signal; local UnrealService service; + service = UnrealService(class'UnrealService'.static.Require()); + TryInjectBroadcastHandler(service); signal = service.GetSignal(class'Broadcast_OnHandleTextFor_Signal'); return Broadcast_OnHandleTextFor_Slot(signal.NewSlot(receiver)); } @@ -219,7 +227,9 @@ public final function Broadcast_OnHandleLocalized_Slot OnHandleLocalized( { local Signal signal; local UnrealService service; + service = UnrealService(class'UnrealService'.static.Require()); + TryInjectBroadcastHandler(service); signal = service.GetSignal(class'Broadcast_OnHandleLocalized_Signal'); return Broadcast_OnHandleLocalized_Slot(signal.NewSlot(receiver)); } @@ -254,11 +264,51 @@ public final function Broadcast_OnHandleLocalizedFor_Slot OnHandleLocalizedFor( { local Signal signal; local UnrealService service; + service = UnrealService(class'UnrealService'.static.Require()); + TryInjectBroadcastHandler(service); signal = service.GetSignal(class'Broadcast_OnHandleLocalizedFor_Signal'); return Broadcast_OnHandleLocalizedFor_Slot(signal.NewSlot(receiver)); } +private final function TryInjectBroadcastHandler(UnrealService service) +{ + local InjectionLevel usedLevel; + local BroadcastSideEffect sideEffect; + local BroadcastEventsObserver broadcastObserver; + + if (IsAdded(class'BroadcastEventsObserver')) { + return; + } + usedLevel = class'BroadcastEventsObserver'.default.usedInjectionLevel; + broadcastObserver = BroadcastEventsObserver(_.unreal.broadcasts.Add( + class'BroadcastEventsObserver', usedLevel)); + if (broadcastObserver != none) + { + broadcastObserver.Initialize(service); + sideEffect = + BroadcastSideEffect(_.memory.Allocate(class'BroadcastSideEffect')); + sideEffect.Initialize(usedLevel); + _server.sideEffects.Add(sideEffect); + _.memory.Free(sideEffect); + _.logger + .Auto(infoInjectedBroadcastEventsObserver) + .Arg(InjectionLevelIntoText(usedLevel)); + } +} + +private final function Text InjectionLevelIntoText( + InjectionLevel injectionLevel) +{ + if (injectionLevel == BHIJ_Root) { + return P("BHIJ_Root"); + } + if (injectionLevel == BHIJ_Registered) { + return P("BHIJ_Registered"); + } + return P("BHIJ_None"); +} + /** * Adds new `BroadcastHandler` class to the current `GameInfo`. * Does nothing if given `BroadcastHandler` class was already added before. @@ -397,4 +447,5 @@ public final function bool IsAdded(class BHClassToFind) defaultproperties { + infoInjectedBroadcastEventsObserver = (l=LOG_Info,m="Injected AcediaCore's `BroadcastEventsObserver` with level `%1`.") } \ No newline at end of file diff --git a/sources/Unreal/BroadcastsAPI/BroadcastSideEffect.uc b/sources/Unreal/BroadcastsAPI/BroadcastSideEffect.uc new file mode 100644 index 0000000..0a136d7 --- /dev/null +++ b/sources/Unreal/BroadcastsAPI/BroadcastSideEffect.uc @@ -0,0 +1,65 @@ +/** + * Object representing a side effect introduced into the game/server. + * Side effects in Acedia refer to changes that aren't a part of mod's main + * functionality, but rather something necessary to make that functionality + * possible that might also affect how other mods work. + * This is a simple data container that is meant to describe relevant + * changes to the human user. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class BroadcastSideEffect extends SideEffect + dependson(BroadcastAPI); + +public final function Initialize(BroadcastAPI.InjectionLevel usedInjectionLevel) +{ + sideEffectName = + _.text.FromString("AcediaCore's `BroadcastHandler` injected"); + sideEffectDescription = + _.text.FromString("Handling text and localized messages between server" + @ "and clients requires AcediaCore to add its own `BroadcastHandler`" + @ "into their linked list." + @ "This is normal, since `BroadcastHandler` class was designed to allow" + @ "mods to do that, however, for full functionality Acedia requires to" + @ "inject it as the very first element (`BHIJ_Root` level injection)," + @ "since some of the events become otherwise inaccessible." + @ "This can result in incompatibility with other mods that are trying" + @ "to do the same." + @ "For that reason AcediaCore can also inject its `BroadcastHandler` as" + @ "`BHIJ_Registered`."); + sideEffectPackage = _.text.FromString("AcediaCore"); + sideEffectSource = _.text.FromString("UnrealAPI"); + if (usedInjectionLevel == BHIJ_Root) + { + sideEffectStatus = + _.text.FromFormattedString("{$TextPositive BHIJ_Root}"); + } + else if (usedInjectionLevel == BHIJ_Registered) + { + sideEffectStatus = + _.text.FromFormattedString("{$TextNetutral BHIJ_Registered}"); + } + else + { + sideEffectStatus = + _.text.FromFormattedString("{$TextNegative BHIJ_None (???)}"); + } +} + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/Unreal/UnrealService.uc b/sources/Unreal/UnrealService.uc index e931f11..c444ce4 100644 --- a/sources/Unreal/UnrealService.uc +++ b/sources/Unreal/UnrealService.uc @@ -31,15 +31,7 @@ var private AcediaGameRules gameRules; protected function OnLaunch() { - local BroadcastEventsObserver broadcastObserver; CreateSignals(); - // Create broadcast handler - broadcastObserver = BroadcastEventsObserver(_.unreal.broadcasts.Add( - class'BroadcastEventsObserver', - class'BroadcastEventsObserver'.default.usedInjectionLevel)); - if (broadcastObserver != none) { - broadcastObserver.Initialize(self); - } // Create game rules gameRules = AcediaGameRules(_.unreal.gameRules.Add(class'AcediaGameRules')); if (gameRules != none) {