Browse Source

Add channel support for notifications

pull/12/head
Anton Tarasenko 2 years ago
parent
commit
fc52110d16
  1. 12
      sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc
  2. 4
      sources/BaseAPI/API/Commands/Voting/Voting.uc
  3. 28
      sources/Players/EPlayer.uc
  4. 107
      sources/Players/PlayerNotificationQueue.uc

12
sources/BaseAPI/API/Commands/BuiltInCommands/ACommandNotify.uc

@ -34,6 +34,12 @@ protected function BuildData(CommandDataBuilder builder) {
builder.Option(P("title"));
builder.Describe(P("Specify the optional title of the notification."));
builder.ParamText(P("title"));
builder.Option(P("channel"));
builder.Describe(P("Specify the optional channel. A channel is a grouping mechanism used to"
@ "control the display of related notifications. Only last message from the same channel is"
@ "stored in queue."));
builder.ParamText(P("channel_name"));
}
protected function ExecutedFor(EPlayer target, CallData arguments, EPlayer instigator) {
@ -45,7 +51,11 @@ protected function ExecutedFor(EPlayer target, CallData arguments, EPlayer insti
}
title = _.text.FromFormatted(plainTitle);
message = _.text.FromFormatted(plainMessage);
target.Notify(title, message, arguments.parameters.GetFloat(P("duration")));
target.Notify(
title,
message,
arguments.parameters.GetFloat(P("duration")),
arguments.options.GetTextBy(P("/channel/channel_name")));
_.memory.Free4(title, message, plainTitle, plainMessage);
}

4
sources/BaseAPI/API/Commands/Voting/Voting.uc

@ -141,7 +141,7 @@ public final function Start(BaseText votingConfigName) {
voters = UpdateVoters();
howToVoteHint = MakeHowToVoteHint();
for (i = 0; i < voters.length; i += 1) {
voters[i].Notify(F(votingStartedLine), howToVoteHint);
voters[i].Notify(F(votingStartedLine), howToVoteHint,, P("voting"));
voters[i].BorrowConsole().WriteLine(F(votingStartedLine));
voters[i].BorrowConsole().WriteLine(howToVoteHint);
}
@ -420,7 +420,7 @@ private final function AnnounceOutcome(BaseText outcomeMessage) {
for (i = 0; i < currentPlayers.length; i += 1) {
currentPlayers[i].BorrowConsole().WriteLine(outcomeMessage);
currentPlayers[i].BorrowConsole().WriteLine(summaryLine);
currentPlayers[i].Notify(outcomeMessage, summaryLine);
currentPlayers[i].Notify(outcomeMessage, summaryLine,, P("voting"));
}
_.memory.FreeMany(currentPlayers);
_.memory.Free(summaryLine);

28
sources/Players/EPlayer.uc

@ -504,12 +504,28 @@ public final function /* borrow */ ConsoleWriter BorrowConsole()
return consoleInstance.ForPlayer(self);
}
/// Notifies player about something with a text message.
/// Sends a text message to notify the player about a particular event or situation, displayed
/// as a notification.
///
/// Header is allowed to be `none`, but it is recommended to set it to an actual value.
/// Duration is more of a suggestion and might be clamped to some reasonable value that depends on
/// implementation/server settings.
public final function Notify(BaseText header, BaseText body, optional float duration) {
/// While the header parameter is optional, it is recommended to provide a meaningful header
/// to give the player context about the notification. The duration parameter suggests the time
/// that the message should be displayed for, but the actual duration may be limited by the
/// implementation or server settings.
///
/// The [`channel`] parameter (case-sensitive) allows you to group related notifications together by
/// assigning them to a specific channel.
/// Each channel can only have one message in the queue at any given time.
/// For instance, when conducting a vote, the old message about the vote's start can be quickly
/// replaced with a newer message about the vote's end.
/// Notifications without a channel `none` can have any number of messages queued up.
///
/// Acedia uses "voting" channel for voting.
public final function Notify(
BaseText header,
BaseText body,
optional float duration,
optional BaseText channel
) {
local HashTable sessionData;
local PlayerNotificationQueue messageQueue;
@ -524,7 +540,7 @@ public final function Notify(BaseText header, BaseText body, optional float dura
sessionData.SetItem(P("MessageQueue"), messageQueue);
}
messageQueue.SetupController(controller);
messageQueue.AddNotification(header, body, duration);
messageQueue.AddNotification(header, body, duration, channel);
_.memory.Free2(sessionData, messageQueue);
}

107
sources/Players/PlayerNotificationQueue.uc

@ -22,15 +22,32 @@
class PlayerNotificationQueue extends AcediaObject
config(AcediaSystem);
/// Manages queue of notifications that should be displayed for a certain player.
//! Manages queue of notifications that should be displayed for a certain player.
//!
//! Pushes messages one-by-one, once its their time to be displayed and making sure that only up to
//! one message per channel is ever in queue.
//!
//! A channel is a way to group related notifications together.
//! Its purpose is to control the display of notifications in a more organized and efficient manner.
//! Each channel can only have one message in the queue at a time.
//! This ensures that only the most relevant or up-to-date message is displayed to the player.
/// Describes a single notification: title (optional, can be `none`) + message body and timeout
/// Describes a single notification: title (optional, can be `none`) + message body, channel
/// and timeout.
struct Notification {
var Text title;
var Text body;
var float duration;
var Text channel;
};
var private array<Notification> notificationQueue;
/// We need this variable to keep track of which channel the currently displayed message belongs to,
/// so that we can ensure that only one message is displayed at a time for each channel.
/// This variable allows us to easily check if a new message belongs to the same channel as
/// the currently displayed message, and if so, replace the currently displayed message with
/// the new one.
var private Text currentChannel;
/// Reference to the `PlayerController` for the player that owns this queue
var private NativeActorRef playerControllerRef;
/// Timer until next notification can be displayed
@ -80,8 +97,29 @@ public final /*native*/ function SetupController(NativeActorRef newPlayerControl
}
}
/// Add new notification to the queue
public final function AddNotification(BaseText title, BaseText body, float duration) {
/// Sends a text message to notify the player about a particular event or situation, displayed
/// as a notification.
///
/// While the header parameter is optional, it is recommended to provide a meaningful header
/// to give the player context about the notification. The duration parameter suggests the time
/// that the message should be displayed for, but the actual duration may be limited by the
/// implementation or server settings.
///
/// The [`channel`] parameter (case-sensitive) allows you to group related notifications together by
/// assigning them to a specific channel.
/// Each channel can only have one message in the queue at any given time.
/// For instance, when conducting a vote, the old message about the vote's start can be quickly
/// replaced with a newer message about the vote's end.
/// Notifications without a channel `none` can have any number of messages queued up.
///
/// Acedia uses "voting" channel for voting.
public final function AddNotification(
BaseText title,
BaseText body,
float duration,
BaseText channel
) {
local int i, newNotificationIndex;
local Notification newNotification;
if (body == none) {
@ -90,9 +128,30 @@ public final function AddNotification(BaseText title, BaseText body, float durat
if (title != none) {
newNotification.title = title.Copy();
}
if (channel != none) {
newNotification.channel = channel.Copy();
}
newNotification.body = body.Copy();
newNotification.duration = duration;
notificationQueue[notificationQueue.length] = newNotification;
newNotificationIndex = notificationQueue.length;
// Make sure there is only one message per channel by replacing the old one
if (currentChannel != none && currentChannel.Compare(channel)) {
SetupNotification(/*take*/ newNotification);
return;
}
if (channel != none) {
for (i = 0; i < notificationQueue.length; i += 1) {
if (channel.Compare(notificationQueue[i].channel)) {
_.memory.Free3(
notificationQueue[i].title,
notificationQueue[i].body,
notificationQueue[i].channel);
newNotificationIndex = i;
break;
}
}
}
notificationQueue[newNotificationIndex] = newNotification;
if (!IsUpdateScheduled()) {
SetupNextNotification(none);
}
@ -177,9 +236,20 @@ private function PrintNotifcationAt(
}
private function SetupNextNotification(Timer callerInstance) {
local Notification nextNotification;
if (notificationQueue.length <= 0) {
InterruptScheduling();
return;
}
nextNotification = notificationQueue[0];
notificationQueue.Remove(0, 1);
SetupNotification(/*take*/ nextNotification);
}
private function SetupNotification(/*take*/ Notification notification) {
local int titleShift;
local MutableText upperCaseTitle;
local Notification nextNotification;
local PlayerController playerController;
// Get appropriate [`PlayerController`] and next notification
@ -187,31 +257,30 @@ private function SetupNextNotification(Timer callerInstance) {
if (playerController == none) {
_.memory.Free(playerControllerRef);
playerControllerRef = none;
}
if (notificationQueue.length <= 0 || playerController == none) {
InterruptScheduling();
return;
}
nextNotification = notificationQueue[0];
notificationQueue.Remove(0, 1);
nextNotification.duration = FMin(nextNotification.duration, maximumNotifyTime);
if (nextNotification.duration <= 0) {
nextNotification.duration = 10.0;
_.memory.Free(currentChannel);
currentChannel = notification.channel;
notification.duration = FMin(notification.duration, maximumNotifyTime);
if (notification.duration <= 0) {
notification.duration = 10.0;
}
// And print
playerController.ClearProgressMessages();
playerController.SetProgressTime(nextNotification.duration);
if (nextNotification.title != none) {
upperCaseTitle = nextNotification.title.UpperMutableCopy();
playerController.SetProgressTime(notification.duration);
if (notification.title != none) {
upperCaseTitle = notification.title.UpperMutableCopy();
upperCaseTitle.ChangeDefaultColor(_.color.TextHeader);
PrintNotifcationAt(playerController, upperCaseTitle, 0, 1);
titleShift = 1;
_.memory.Free(upperCaseTitle);
}
PrintNotifcationAt(playerController, nextNotification.body, titleShift, 4 - titleShift);
ScheduleUpdate(nextNotification.duration);
_.memory.Free2(nextNotification.title, nextNotification.body);
PrintNotifcationAt(playerController, notification.body, titleShift, 4 - titleShift);
ScheduleUpdate(notification.duration);
// We moved channel's reference into `currentChannel`
_.memory.Free2(notification.title, notification.body);
}
defaultproperties {

Loading…
Cancel
Save