Reacting to Events
Kalamba SDK emits the following events. Each section states how the Game Client MUST or MAY react:
Balance
When received, the Game Client MUST update in-game balance if separate balance indicator exists in game.
type BalancePayload = { balance: number }
sdk.on('balance', payload => {
game.setBalance(payload.balance)
})
Legal Bets
When received, the Game Client MUST update legal bets available in game. The payload maps each legal base bet value to the list of legal bet multipliers for that base.
type LegalBetsPayload = Record<number, number[]>
sdk.on('legalBets', payload => {
game.setLegalBets(payload)
})
Bet
When received, the Game Client MUST update in-game bet if separate bet indicator exists in game.
type BetPayload = { base: number; multiplier: number }
sdk.on('bet', payload => {
game.setBet(payload)
})
Cashier
Emitted when the casino's cashier was opened. The type field tells why — on the player's demand, or because the balance ran low or became insufficient.
The Game Client MAY use this event to adjust its UI while the cashier is shown.
type CashierPayload = { type: 'ON_DEMAND' | 'BALANCE_LOW' | 'BALANCE_INSUFFICIENT' }
sdk.on('cashier', payload => {
game.onCashierOpened(payload.type)
})
Choice
When received, the Game Client MUST resolve a pending choice previously requested with sdk.send('choice', ...).
The payload carries the id of the option selected by the player.
type ChoicePayload = { id: string }
sdk.on('choice', payload => {
game.resolveChoice(payload.id)
})
Close
When received, the Game Client MAY use this event to do some last minute housekeeping. The game will be closed imminently, so the Game Client SHOULD use the Beacon API if it plans to make any network requests. This event has no payload.
sdk.on('close', () => {
game.close()
})
Configured
When received, the Game Client MAY read the resolved SdkConfig to adjust available features and UI.
The SDK also stores this payload internally and exposes it as sdk.config after the game has been opened.
sdk.on('configured', config => {
game.applyConfig(config.ui.feature)
})
Wrapper Configured
Emitted when the wrapper has processed the configure message.
The Game Client normally does not need to listen to this event directly — the promise returned by sdk.configure(...) resolves when it is received.
This event has no payload.
sdk.on('wrapperConfigured', () => {
game.onWrapperReady()
})
Error
When received, the Game Client MAY react to an error surfaced by the wrapper.
The type field describes how the wrapper resolves the error: CLOSE, RELOAD or CONTINUE.
type SdkError = {
messageCode: string
type: 'CLOSE' | 'RELOAD' | 'CONTINUE'
} & ({ message: string } | { messageKey: string })
sdk.on('error', payload => {
game.onError(payload)
})
Freeze
When received, the Game Client MUST pause the game and prevent further gameplay and interactions with the user interface. The Game Client MUST stop autoplay if it is engaged. The Game Client MAY finish the animations that were already playing when the event was received, but it is RECOMMENDED that all animations are paused as well. This event has no payload.
sdk.on('freeze', () => {
game.freeze()
})
Game Help
When received, the Game Client MUST toggle game help.
The payload is OPTIONAL — when present, show indicates whether help should be shown or hidden; when absent, the Game Client SHOULD toggle the current state.
type HelpPayload = { show: boolean } | undefined
sdk.on('help', payload => {
game.toggleHelp(payload?.show)
})
Game Paytable
When received, the Game Client MUST toggle game paytable.
The payload is OPTIONAL — when present, show indicates whether the paytable should be shown or hidden; when absent, the Game Client SHOULD toggle the current state.
type PaytablePayload = { show: boolean } | undefined
sdk.on('paytable', payload => {
game.togglePaytable(payload?.show)
})
History
Emitted when the game history was opened. The source field tells where it was opened from.
The Game Client MAY use this event to adjust its UI while the history is shown.
type HistoryPayload = { source: 'realityCheck' | 'settings' | 'casino' }
sdk.on('history', payload => {
game.onHistoryOpened(payload.source)
})
Play Ready
Sent after playEnd, once all pending play blockers (e.g. wrapper popups) have resolved, signaling that the Game Client may start a new play.
This event has no payload.
sdk.on('playReady', () => {
game.enableSpin()
})
Reality Check
Carries the reality check data reported by the RGS: the session duration and the cumulative bet and win amounts.
The wrapper shows its own reality check popup by default; a Game Client that disables it with showRealityCheck: false MUST present the reality check to the player itself (see Wrapper Features).
type RealityCheckPayload = {
duration: number
sumBetAmount: number
sumWinAmount: number
}
sdk.on('realityCheck', payload => {
game.showRealityCheck(payload)
})
Resume
When received, the Game Client MUST resume autoplay if it was previously paused in response to suspend event.
When received, the Game Client MAY ignore the event if resuming autoplay is not possible.
This event has no payload.
sdk.on('resume', () => {
game.resumeAutoplay()
})
Settings
When received, the Game Client MUST update in-game settings. The payload MUST include only settings that have been changed.
type SettingsPayload = {
fastPlay?: boolean
sounds?: boolean
music?: boolean
}
sdk.on('settings', payload => {
game.updateSettings(payload)
})
Suspend
When received, the Game Client MUST pause autoplay if it is engaged. When received, the Game Client MAY stop autoplay instead if pausing autoplay is not possible. This event has no payload.
sdk.on('suspend', () => {
game.pauseAutoplay()
})
Unfreeze
When received, the Game Client MUST unpause the game and allow further gameplay and interactions with the user interface.
The Game Client MUST resume any animations that were paused by the freeze event.
This event has no payload.
sdk.on('unfreeze', () => {
game.unfreeze()
})
Free Rounds
See Free Rounds for the full lifecycle. The events below share these payload shapes:
type FreeRound = {
conf: {
base: number
multiplier: number
numAwarded: number
}
data: {
numPlayed: number
win: number
numLeft: number
}
id: string
rejectable: boolean
skippable: boolean
status: 'PENDING' | 'ACTIVE' | 'FINISHED'
type: string
}
type FreeRoundsAction = {
action: 'ACCEPT' | 'DECLINE' | 'SKIP'
id: string
}
freeRounds
Carries a FreeRoundsAction — the action taken on a free rounds offer.
sdk.on('freeRounds', payload => {
game.onFreeRoundsAction(payload.action, payload.id)
})
freeRoundsOffer
Carries a FreeRound describing an offered free rounds campaign.
sdk.on('freeRoundsOffer', freeRound => {
game.onFreeRoundsOffer(freeRound)
})
freeRoundsInfo
Carries a FreeRound with the current state of an active free rounds campaign.
sdk.on('freeRoundsInfo', freeRound => {
game.onFreeRoundsInfo(freeRound)
})
freeRoundsComplete
Carries the final FreeRound state when a free rounds campaign has finished.
sdk.on('freeRoundsComplete', freeRound => {
game.onFreeRoundsComplete(freeRound)
})