this.bga.notifications
Notifications
When something happens on the server side, your game interface JavaScript logic receives a notification. If you have not done so yet, check what notifications are in Main_game_logic:_yourgamename.game.php#Notifications
Here's how you can handle these notifications on the client side.
this.bga.notifications.setupPromiseNotifications(params = undefined)
- params - the call parameters, by default { prefix: 'notif_', minDuration: 500, minDurationNoText: 1, logger: null, ignoreNotifications: [], onStart: undefined, onEnd: undefined, }.
Auto-detect all notifications declared on the game object (functions starting with `notif_`) and register them with dojo.subscribe.
Registered notifications will be synchronous and will have a minimum duration (if animations are active, by default 500ms with text and 1ms without).
If the notification function returns a Promise, the notification will end when the promise AND the minimum durations are over. If the notification function does not return a promise, it is considered as already resolved as soon as the minimum durations are over.
In case of a notification function returning a Promise, the dev is responsible for making it resolve instantaneously if animations are not active.
See `bgaAnimationsActive` to know if animations are active : Animations
See `bgaPlayDojoAnimation` to handle dojo animations with promises : https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Animation_Callbacks
setupNotifications: function() {
this.bga.notifications.setupPromiseNotifications();
}
notif_playedCard: async function(args) {
await this.getPlayerTable(args.playerId).playCard(args.card);
}
Side note : you can receive the full notification data as the second argument of the notification function. This gives you access to the notification message and more technical data such as move_id, table_id and uid.
Example of setting custom values for all params :
setupNotifications: function() {
this.bga.notifications.setupPromiseNotifications({
prefix: 'notif_', // default is 'notif_'
minDuration: 1200, // for longer animations (500 by default)
minDurationNoText: 1,
handlers: [this.notificationsManager], // if you write your notification function in a subclass instead of this (default this)
logger: console.log, // show notification debug information in the console. Could be console.warn or any custom debug function (default null = no logs)
ignoreNotifications: ['updateAutoPlay'], // the notif_updateAutoPlay function will be ignored by bgaSetupPromiseNotifications. You'll need to subscribe to it manually
onStart: (notifName, msg, args) => $('pagemaintitletext').innerHTML = `${_('Animation for:')} ${msg}`,
onEnd: (notifName, msg, args) => $('pagemaintitletext').innerHTML = '',
});
}
Using Notifications in JS State Classes
You can put the notification function (notif_doSomething) in your JS State Class and register this with 'handlers' :
setupNotifications: function() {
this.bga.notifications.setupPromiseNotifications({
(...)
handlers: [this, ...this.bga.states.getStateClasses()],
(...)
});
}
Warning : if the same notif_doSomething is defined in Game.js and several State classes, they all will be called!
this.bga.gameui.wait(delay)
- delay - the time to wait, in milliseconds
Return a Promise that resolves at the end of a given number of ms. If animations are not active, resolve instantaneously.
Subscribe to notifications manually
dojo.subscribe(notif_type: string, callback_obj: Object, handler: string|handler)
- notif_type - notification type/name sent by the PHP server
- callback_obj - usually this
- handler - if a string, the method of callback_obj with that name is called when the notification arrives, with the notification object as a parameter (see below)
Your JavaScript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code.
Here's how you associate one of your JavaScript methods with a notification "playDisc" (from the Reversi example):
Note: the "playDisc" corresponds to the name of the notification you define in your PHP code, in your "notify->all" or "notify->player" method.
Then, you have to define your "notif_playDisc" method:
notif_playDisc: function(notif) {
// Remove current possible moves (makes the board clearer)
dojo.query( '.possibleMove' ).removeClass( 'possibleMove' );
this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id );
},
In a notification handler like our "notif_playDisc" method, you can access all notification arguments with "notif.args".
Example:
PHP
$this->bga->notify->all( "apples", clienttranslate('player takes ${count} apples'), [ "count" => 3 ] );
JavaScript
setupNotifications: function() {
dojo.subscribe( 'apples', this, 'notif_apples' );
},
notif_apples: function(notif) {
//You can access the "count" like this:
alert("count = " + notif.args.count);
}
Ignoring notifications
Tip
With the new _private arg in \$this->bga->notify->all, you should not need to ignore notifications anymore. See https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Sending_private_data_to_some_players for the new way of handling private data in notifications.
setIgnoreNotificationCheck(notif_type: string, predicate: ((notif: Notif)=>boolean))
This method sets a check to determine whether notifications of a specific type should be ignored.
The parameters are:
- notif_type: type of the notification
- predicate (notif => boolean): a function that will receive a notif object and will return true if this specific notification should be ignored
Before dispatching any notification of this type, the framework will call predicate to check whether the notification should be ignored. If it returns true, the notification will be dispatched, i.e. logged or handled.
this.bga.gameui.notifqueue.setIgnoreNotificationCheck( 'dealCard', (notif) => notif.args.player_id == this.bga.players.getCurrentPlayerId() );
Notif is still sent
Remember that this notification is ignored on the client side, but was still received by the client. Therefore it shouldn't contain any private information as cheaters can get it.
That's why sending private in a notify->all is better.
Reload and ignored notifications
When a game is reloaded with F5 or when opening a turn-based game, old notifications are replayed as history notifications. They are used just to update the game log and are stripped of all arguments except player_id, i18n and any argument present in message. If you use any other argument in your predicate you should preserve it as explained here.
Handle synchronous notifications manually
When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in exactly the same order they were generated in your PHP game logic.
However, sometimes, you need to give some time to the players to figure out what happened in the game before jumping to the next notification. Indeed, in many games, there are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so.
As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else so that the opponent can figure out what move has been played.
Here's how we do this, right after our subscription:
dojo.subscribe( 'playDisc', this, "notif_playDisc" );
this.bga.gameui.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler
It is also possible to control the delay timing dynamically (e.g., using notification args). As an example, maybe your notification 'cardPlayed' should pause for a different amount of time depending on the number or type of cards played.
For this case, use setSynchronous without specifying the duration and use setSynchronousDuration within the notification callback.
- NOTE: If you forget to invoke setSynchronousDuration, the game will remain paused forever!
setupNotifications: function () {
dojo.subscribe( 'cardPlayed', this, 'notif_cardPlayed' );
this.bga.gameui.notifqueue.setSynchronous( 'cardPlayed' ); // wait time is dynamic
...
},
notif_cardPlayed: function (notif) {
// MUST call setSynchronousDuration
// Example 1: From notification args (PHP)
this.bga.gameui.notifqueue.setSynchronousDuration(notif.args.duration);
...
// Or, example 2: Match the duration to a Dojo animation
var anim = dojo.fx.combine([
...
]);
anim.play();
this.bga.gameui.notifqueue.setSynchronousDuration(anim.duration);
},
You can also manually call this.bga.gameui.notifqueue.setSynchronousDuration(0) once client operations are finished, but be careful that even fast replay still has a path to call it.
WARNING: combining synchronous and ignored notifications You must be careful when combining dynamic synchronous durations (as described above) with ignored notifications. If you have a conditionally ignored notification like this (see the section below):
this.bga.gameui.notifqueue.setIgnoreNotificationCheck( 'myNotif', (notif) => (notif.args.player_id == this.bga.players.getCurrentPlayerId()) /* or any other condition */ )
then you CANNOT do
as, when the ignored check passes, the notification handler, in which `this.bga.gameui.notifqueue.setSychronousDuration` is called, is never called and so the duration is never set and the interface remains locked.
The workaround is to set a "dummy" time:
whose value is irrelevant but must be large enough to cover the time before the notification handler is called. The large value never actually comes into play because the notification is either ignored, or the synchronous duration is reset to a sensible value inside the handler.
Pre-defined notification types
tableWindow - This defines a notification to display Scoring Dialogs, see below.
message - This defines a notification that appears in the players' logs and has no other effect (technically, any unhandled notification will do the same, but it is recommended to use this keyword for consistency).
// You can call this on the PHP side without doing anything on the client side
$this->bga->notify->all( 'message', clienttranslate('hello'), [] );
simplePause - This notification will just delay other notifications, maybe useful if you know you need some extra time for animation or something. Requires a time parameter.
Reserved names
The following types are RESERVED by the framework; do not use them:
gameStateChange gameStateChangePrivateArg gameStateMultipleActiveUpdate newActivePlayer playerstatus yourturnack clockalert tableInfosChanged playerEliminated tableDecision archivewaitingdelay end_archivewaitingdelay replaywaitingdelay end_replaywaitingdelay replayinitialwaitingdelay end_replayinitialwaitingdelay aiPlayerWaitingDelay replay_has_ended updateSpectatorList wouldlikethink updateReflexionTime undoRestorePoint resetInterfaceWithAllDatas zombieModeFail zombieModeFailWarning aiError skipTurnOfPlayer zombieBack allPlayersAreZombie gameResultNeutralized playerConcedeGame showTutorial showCursor showCursorClick skipTurnOfPlayerWarning banFromTable resultsAvailable switchToTurnbased newPrivateState infomsg