Browse Source

Add support for broadcast events (BHIJ_Registered)

Some of the broadcasting events only properly work with full injection
method (`BHIJ_Root`) and some additional work was required to implement
them for `BHIJ_Registered`.

Althought support for `HandleText()` and `HandleLocalized()` events
cannot be perfect with `BHIJ_Registered`, current method should work
good enough for most cases.
pull/8/head
Anton Tarasenko 4 years ago
parent
commit
0ff8ff1776
  1. 193
      sources/Events/Broadcast/BroadcastEventsObserver.uc

193
sources/Events/Broadcast/BroadcastEventsObserver.uc

@ -1,7 +1,9 @@
/** /**
* 'BroadcastHandler' class that used by Acedia to catch * `BroadcastHandler` class that used by Acedia to catch
* broadcasting events. For Acedia to work properly it needs to be added to * broadcasting events. For Acedia to work properly it needs to be added to
* the very beginning of the broadcast handlers' chain. * the very beginning of the broadcast handlers' chain.
* However, for compatibility reasons Acedia also supports less invasive
* methods to add it at the cost of some functionality degradation.
* Copyright 2020 Anton Tarasenko * Copyright 2020 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
@ -36,32 +38,138 @@ enum InjectionLevel
// effectively disable `BroadcastEvents`. // effectively disable `BroadcastEvents`.
BHIJ_None, BHIJ_None,
// `BroadcastEventsObserver` will be places in the broadcast handlers' // `BroadcastEventsObserver` will be places in the broadcast handlers'
// chain as a normal `BroadcastHandler`, which can lead to incorrect // chain as a normal `BroadcastHandler`
// (through `RegisterBroadcastHandler()` call), which can lead to incorrect
// handling of `HandleText` and `HandleLocalized` events. // handling of `HandleText` and `HandleLocalized` events.
BHIJ_Registered, BHIJ_Registered,
// `BroadcastEventsObserver` will be injected at the very beginning of // `BroadcastEventsObserver` will be injected at the very beginning of
// the broadcast handlers' chain. // the broadcast handlers' chain.
// This provides full Acedia's functionality. // This option provides full Acedia's functionality.
BHIJ_Root BHIJ_Root
}; };
var public config const InjectionLevel usedInjectionLevel; var public config const InjectionLevel usedInjectionLevel;
// The way vanilla 'BroadcastHandler' works - it can check if broadcast is // The way vanilla `BroadcastHandler` works - it can check if broadcast is
// possible for any actor, but for actually sending the text messages it will // possible for any actor, but for actually sending the text messages it will
// try to extract player's data from it // try to extract player's data from it and will simply pass `none` for
// and will simply pass 'none' if it can't. // a sender if it can't.
// We remember senders in this array in order to pass real ones to our events. // We remember senders in this array in order to pass real ones to
// Array instead of variable is to account for folded calls // our events.
// (when handling of broadcast events leads to another message generation). // We use an array instead of a single variable is to account for possible
// folded calls (when handling of broadcast events leads to another
// message generation).
// This is only relevant for `BHIJ_Root` injection level.
var private array<Actor> storedSenders; var private array<Actor> storedSenders;
// We want to insert our code in some of the functions between // We want to insert our code in some of the functions between
// 'AllowsBroadcast' check and actual broadcasting, // `AllowsBroadcast` check and actual broadcasting,
// so we can't just use a 'super.AllowsBroadcast()' call. // so we can't just use a `super.AllowsBroadcast()` call.
// Instead we first manually do this check, then perform our logic and then // Instead we first manually do this check, then perform our logic and then
// make a super call, but with 'blockAllowsBroadcast' flag set to 'true', // make a super call, but with `blockAllowsBroadcast` flag set to `true`,
// which causes overloaded 'AllowsBroadcast()' to omit actual checks. // which causes overloaded `AllowsBroadcast()` to omit checks that we've
// already performed.
var private bool blockAllowsBroadcast; var private bool blockAllowsBroadcast;
/*
* In case of `BHIJ_Registered` injection level, we do not get notified
* when a message starts getting broadcasted through `Broadcast()`,
* `BroadcastTeam()` and `AcceptBroadcastLocalized()`.
* Instead we are only notified when a message is broadcasted to
* a particular player, so with 2 players instead of sequence `Broadcast()`,
* `AcceptBroadcastText()`, `AcceptBroadcastText()`
* we get `AcceptBroadcastText()`, `AcceptBroadcastText()`.
* This means that we can only guess when new broadcast was initiated.
* We do this by:
* 1. Recording broadcast instigator (sender) and his message. If any of
* these variables change - we assume it's a new broadcast.
* 2. Recording players that already received that message, - if message is
* resend to one of them - it's a new broadcast
* (of possibly duplicate message).
* 3. All broadcasted messages are sent to all players within 1 tick, so
* any first message within each tick is a start of a new broadcast.
*
* Check logic is implemented in `IsFromNewTextBroadcast()` and
* `IsFromNewLocalizedBroadcast()` methods.
*/
// Are we already already tracking any broadcast? Helps to track for point 3.
var private bool trackingBroadcast;
// Sender of the current broadcast. Helps to track for point 1.
var private Actor currentBroadcastInstigator;
// Players that already received current broadcast. Helps to track for point 2.
var private array<PlayerController> currentBroadcastReceivers;
// Is current broadcast sending a
// text message (`Broadcast()` and `BroadcastTeam()`)
// or localized message (`AcceptBroadcastLocalized()`)?
// Helps to track message for point 1.
var private bool broadcastingLocalizedMessage;
// Variables to stored text message. Helps to track for point 1.
var private string currentTextMessageContent;
var private name currentTextMessageType;
// Variables to stored localized message. Helps to track for point 1.
var private BroadcastEvents.LocalizedMessage currentLocalizedMessage;
private function bool IsCurrentBroadcastReceiver(PlayerController receiver)
{
local int i;
for (i = 0; i < currentBroadcastReceivers.length; i += 1)
{
if (currentBroadcastReceivers[i] == receiver) {
return true;
}
}
return false;
}
private function bool IsFromNewTextBroadcast(
PlayerReplicationInfo senderPRI,
PlayerController receiver,
string message,
name messageType)
{
local bool isCurrentBroadcastContinuation;
if (usedInjectionLevel != BHIJ_Registered) return false;
isCurrentBroadcastContinuation = trackingBroadcast
&& (senderPRI == currentBroadcastInstigator)
&& (!broadcastingLocalizedMessage)
&& (message == currentTextMessageContent)
&& (currentTextMessageType == currentTextMessageType)
&& !IsCurrentBroadcastReceiver(receiver);
if (isCurrentBroadcastContinuation) {
return false;
}
trackingBroadcast = true;
broadcastingLocalizedMessage = false;
currentBroadcastInstigator = senderPRI;
currentTextMessageContent = message;
currentTextMessageType = messageType;
currentBroadcastReceivers.length = 0;
return true;
}
private function bool IsFromNewLocalizedBroadcast(
Actor sender,
PlayerController receiver,
BroadcastEvents.LocalizedMessage localizedMessage)
{
local bool isCurrentBroadcastContinuation;
if (usedInjectionLevel != BHIJ_Registered) return false;
isCurrentBroadcastContinuation = trackingBroadcast
&& (sender == currentBroadcastInstigator)
&& (broadcastingLocalizedMessage)
&& (localizedMessage == currentLocalizedMessage)
&& !IsCurrentBroadcastReceiver(receiver);
if (isCurrentBroadcastContinuation) {
return false;
}
trackingBroadcast = true;
broadcastingLocalizedMessage = true;
currentBroadcastInstigator = sender;
currentLocalizedMessage = localizedMessage;
currentBroadcastReceivers.length = 0;
return true;
}
// Functions below simply reroute vanilla's broadcast events to // Functions below simply reroute vanilla's broadcast events to
// Acedia's 'BroadcastEvents', while keeping original senders // Acedia's 'BroadcastEvents', while keeping original senders
// and blocking 'AllowsBroadcast()' as described in comments for // and blocking 'AllowsBroadcast()' as described in comments for
@ -85,8 +193,7 @@ public function bool HandlerAllowsBroadcast(Actor broadcaster, int sentTextNum)
function Broadcast(Actor sender, coerce string message, optional name type) function Broadcast(Actor sender, coerce string message, optional name type)
{ {
local bool canTryToBroadcast; local bool canTryToBroadcast;
if (!AllowsBroadcast(sender, Len(message))) if (!AllowsBroadcast(sender, Len(message))) return;
return;
canTryToBroadcast = class'BroadcastEvents'.static canTryToBroadcast = class'BroadcastEvents'.static
.CallHandleText(sender, message, type); .CallHandleText(sender, message, type);
if (canTryToBroadcast) if (canTryToBroadcast)
@ -99,16 +206,14 @@ function Broadcast(Actor sender, coerce string message, optional name type)
} }
} }
function BroadcastTeam function BroadcastTeam(
(
Controller sender, Controller sender,
coerce string message, coerce string message,
optional name type optional name type
) )
{ {
local bool canTryToBroadcast; local bool canTryToBroadcast;
if (!AllowsBroadcast(sender, Len(message))) if (!AllowsBroadcast(sender, Len(message))) return;
return;
canTryToBroadcast = class'BroadcastEvents'.static canTryToBroadcast = class'BroadcastEvents'.static
.CallHandleText(sender, message, type); .CallHandleText(sender, message, type);
if (canTryToBroadcast) if (canTryToBroadcast)
@ -121,8 +226,7 @@ function BroadcastTeam
} }
} }
event AllowBroadcastLocalized event AllowBroadcastLocalized(
(
Actor sender, Actor sender,
class<LocalMessage> message, class<LocalMessage> message,
optional int switch, optional int switch,
@ -133,8 +237,6 @@ event AllowBroadcastLocalized
{ {
local bool canTryToBroadcast; local bool canTryToBroadcast;
local BroadcastEvents.LocalizedMessage packedMessage; local BroadcastEvents.LocalizedMessage packedMessage;
if (!AllowsBroadcast(sender, Len(message)))
return;
packedMessage.class = message; packedMessage.class = message;
packedMessage.id = switch; packedMessage.id = switch;
packedMessage.relatedPRI1 = relatedPRI1; packedMessage.relatedPRI1 = relatedPRI1;
@ -150,15 +252,14 @@ event AllowBroadcastLocalized
} }
} }
function bool AllowsBroadcast(actor broadcaster, int len) function bool AllowsBroadcast(Actor broadcaster, int len)
{ {
if (blockAllowsBroadcast) if (blockAllowsBroadcast)
return true; return true;
return super.AllowsBroadcast(broadcaster, len); return super.AllowsBroadcast(broadcaster, len);
} }
function bool AcceptBroadcastText function bool AcceptBroadcastText(
(
PlayerController receiver, PlayerController receiver,
PlayerReplicationInfo senderPRI, PlayerReplicationInfo senderPRI,
out string message, out string message,
@ -167,26 +268,31 @@ function bool AcceptBroadcastText
{ {
local bool canBroadcast; local bool canBroadcast;
local Actor sender; local Actor sender;
if (senderPRI != none) if (senderPRI != none) {
{
sender = PlayerController(senderPRI.owner); sender = PlayerController(senderPRI.owner);
} }
if (sender == none && storedSenders.length > 0) if (sender == none && storedSenders.length > 0) {
{
sender = storedSenders[storedSenders.length - 1]; sender = storedSenders[storedSenders.length - 1];
} }
if (usedInjectionLevel == BHIJ_Registered)
{
if (IsFromNewTextBroadcast(senderPRI, receiver, message, type))
{
class'BroadcastEvents'.static.CallHandleText(sender, message, type);
currentBroadcastReceivers.length = 0;
}
currentBroadcastReceivers[currentBroadcastReceivers.length] = receiver;
}
canBroadcast = class'BroadcastEvents'.static canBroadcast = class'BroadcastEvents'.static
.CallHandleTextFor(receiver, sender, message, type); .CallHandleTextFor(receiver, sender, message, type);
if (!canBroadcast) if (!canBroadcast) {
{
return false; return false;
} }
return super.AcceptBroadcastText(receiver, senderPRI, message, type); return super.AcceptBroadcastText(receiver, senderPRI, message, type);
} }
function bool AcceptBroadcastLocalized function bool AcceptBroadcastLocalized(
(
PlayerController receiver, PlayerController receiver,
Actor sender, Actor sender,
class<LocalMessage> message, class<LocalMessage> message,
@ -203,16 +309,31 @@ function bool AcceptBroadcastLocalized
packedMessage.relatedPRI1 = relatedPRI1; packedMessage.relatedPRI1 = relatedPRI1;
packedMessage.relatedPRI2 = relatedPRI2; packedMessage.relatedPRI2 = relatedPRI2;
packedMessage.relatedObject = obj; packedMessage.relatedObject = obj;
if (usedInjectionLevel == BHIJ_Registered)
{
if (IsFromNewLocalizedBroadcast(sender, receiver, packedMessage))
{
class'BroadcastEvents'.static
.CallHandleLocalized(sender, packedMessage);
currentBroadcastReceivers.length = 0;
}
currentBroadcastReceivers[currentBroadcastReceivers.length] = receiver;
}
canBroadcast = class'BroadcastEvents'.static canBroadcast = class'BroadcastEvents'.static
.CallHandleLocalizedFor(receiver, sender, packedMessage); .CallHandleLocalizedFor(receiver, sender, packedMessage);
if (!canBroadcast) if (!canBroadcast) {
{
return false; return false;
} }
return super.AcceptBroadcastLocalized( receiver, sender, message, switch, return super.AcceptBroadcastLocalized( receiver, sender, message, switch,
relatedPRI1, relatedPRI2, obj); relatedPRI1, relatedPRI2, obj);
} }
event Tick(float delta)
{
trackingBroadcast = false;
currentBroadcastReceivers.length = 0;
}
defaultproperties defaultproperties
{ {
blockAllowsBroadcast = false blockAllowsBroadcast = false

Loading…
Cancel
Save