feat(earnings): add Earnings Center packets (9308-9310 / 9407-9408)

Client-side counterpart to the emulator earnings contract
(emulatore/docs/earnings-packet-contract.md). Outgoing: RequestEarningsCenter /
ClaimEarningsReward(categoryKey) / ClaimAllEarningsRewards. Incoming:
EarningsCenter + EarningsClaimResult sharing one entry/reward shape. Registered
in NitroMessages + barrel chain (incoming/outgoing/parser).
This commit is contained in:
simoleo89
2026-06-15 22:28:17 +02:00
parent b8bc7135c2
commit 80df492753
16 changed files with 202 additions and 0 deletions
@@ -11,6 +11,7 @@ import { RareValuesEvent, RequestRareValuesComposer } from './messages';
import { WheelBuySpinComposer, WheelDataEvent, WheelOpenComposer, WheelRecentWinsEvent, WheelResultEvent, WheelSpinComposer } from './messages';
import { WheelAdminGetPrizesComposer, WheelAdminPrizesEvent, WheelAdminSavePrizesComposer } from './messages';
import { SoundboardPlayEvent, SoundboardSettingsEvent, SoundboardPlayComposer, SoundboardSetEnabledComposer } from './messages';
import { EarningsCenterEvent, EarningsClaimResultEvent, RequestEarningsCenterComposer, ClaimEarningsRewardComposer, ClaimAllEarningsRewardsComposer } from './messages';
import { DeleteMentionComposer, MarkMentionsReadComposer, MentionReceivedEvent, MentionsListEvent, RequestMentionsComposer } from './messages';
export class NitroMessages implements IMessageConfiguration
{
@@ -537,6 +538,8 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.WHEEL_ADMIN_PRIZES, WheelAdminPrizesEvent);
this._events.set(IncomingHeader.SOUNDBOARD_SETTINGS, SoundboardSettingsEvent);
this._events.set(IncomingHeader.SOUNDBOARD_PLAY, SoundboardPlayEvent);
this._events.set(IncomingHeader.EARNINGS_CENTER, EarningsCenterEvent);
this._events.set(IncomingHeader.EARNINGS_CLAIM_RESULT, EarningsClaimResultEvent);
this._events.set(IncomingHeader.WIRED_REWARD, WiredRewardResultMessageEvent);
this._events.set(IncomingHeader.WIRED_SAVE, WiredSaveSuccessEvent);
this._events.set(IncomingHeader.WIRED_ERROR, WiredValidationErrorEvent);
@@ -1345,6 +1348,9 @@ export class NitroMessages implements IMessageConfiguration
this._composers.set(OutgoingHeader.WHEEL_ADMIN_SAVE_PRIZES, WheelAdminSavePrizesComposer);
this._composers.set(OutgoingHeader.SOUNDBOARD_PLAY, SoundboardPlayComposer);
this._composers.set(OutgoingHeader.SOUNDBOARD_SET_ENABLED, SoundboardSetEnabledComposer);
this._composers.set(OutgoingHeader.EARNINGS_REQUEST, RequestEarningsCenterComposer);
this._composers.set(OutgoingHeader.EARNINGS_CLAIM, ClaimEarningsRewardComposer);
this._composers.set(OutgoingHeader.EARNINGS_CLAIM_ALL, ClaimAllEarningsRewardsComposer);
}
public get events(): Map<number, Function>
@@ -531,4 +531,6 @@ export class IncomingHeader
public static WHEEL_ADMIN_PRIZES = 9404;
public static SOUNDBOARD_SETTINGS = 9405;
public static SOUNDBOARD_PLAY = 9406;
public static EARNINGS_CENTER = 9407;
public static EARNINGS_CLAIM_RESULT = 9408;
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { EarningsCenterParser } from '../../parser';
export class EarningsCenterEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, EarningsCenterParser);
}
public getParser(): EarningsCenterParser
{
return this.parser as EarningsCenterParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { EarningsClaimResultParser } from '../../parser';
export class EarningsClaimResultEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, EarningsClaimResultParser);
}
public getParser(): EarningsClaimResultParser
{
return this.parser as EarningsClaimResultParser;
}
}
@@ -0,0 +1,2 @@
export * from './EarningsCenterEvent';
export * from './EarningsClaimResultEvent';
@@ -10,6 +10,7 @@ export * from './catalog';
export * from './client';
export * from './commands';
export * from './competition';
export * from './earnings';
export * from './crafting';
export * from './desktop';
export * from './friendlist';
@@ -574,4 +574,7 @@ export class OutgoingHeader
public static WHEEL_ADMIN_SAVE_PRIZES = 9305;
public static SOUNDBOARD_PLAY = 9306;
public static SOUNDBOARD_SET_ENABLED = 9307;
public static EARNINGS_REQUEST = 9308;
public static EARNINGS_CLAIM = 9309;
public static EARNINGS_CLAIM_ALL = 9310;
}
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class ClaimAllEarningsRewardsComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class ClaimEarningsRewardComposer implements IMessageComposer<ConstructorParameters<typeof ClaimEarningsRewardComposer>>
{
private _data: ConstructorParameters<typeof ClaimEarningsRewardComposer>;
constructor(categoryKey: string)
{
this._data = [categoryKey];
}
public dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class RequestEarningsCenterComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,3 @@
export * from './RequestEarningsCenterComposer';
export * from './ClaimEarningsRewardComposer';
export * from './ClaimAllEarningsRewardsComposer';
@@ -7,6 +7,7 @@ export * from './catalog';
export * from './competition';
export * from './crafting';
export * from './desktop';
export * from './earnings';
export * from './friendfurni';
export * from './friendlist';
export * from './furnieditor';
@@ -0,0 +1,68 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export interface IEarningsReward
{
type: string; // credits | pixels | points | badge | item | hc_days
amount: number;
pointsType: number; // currency type when type === 'points'
data: string; // badge code (badge), items_base.id (item), else ''
}
export interface IEarningsEntry
{
categoryKey: string;
enabled: boolean;
claimable: boolean;
nextClaimAt: number;
rewards: IEarningsReward[];
}
// Shared entry reader — the claim-result parser embeds the same shape.
export function readEarningsEntry(wrapper: IMessageDataWrapper): IEarningsEntry
{
const categoryKey = wrapper.readString();
const enabled = wrapper.readBoolean();
const claimable = wrapper.readBoolean();
const nextClaimAt = wrapper.readInt();
const rewardCount = wrapper.readInt();
const rewards: IEarningsReward[] = [];
for(let i = 0; i < rewardCount; i++)
{
rewards.push({
type: wrapper.readString(),
amount: wrapper.readInt(),
pointsType: wrapper.readInt(),
data: wrapper.readString()
});
}
return { categoryKey, enabled, claimable, nextClaimAt, rewards };
}
export class EarningsCenterParser implements IMessageParser
{
private _entries: IEarningsEntry[] = [];
public flush(): boolean
{
this._entries = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._entries = [];
for(let i = 0; i < count; i++) this._entries.push(readEarningsEntry(wrapper));
return true;
}
public get entries(): IEarningsEntry[] { return this._entries; }
}
@@ -0,0 +1,46 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
import { IEarningsEntry, readEarningsEntry } from './EarningsCenterParser';
export interface IEarningsClaimResult
{
categoryKey: string;
status: string; // success | disabled | unknown_category | already_claimed | no_reward | error
success: boolean;
hasEntry: boolean;
entry: IEarningsEntry | null; // refreshed entry when hasEntry === true
}
export class EarningsClaimResultParser implements IMessageParser
{
private _results: IEarningsClaimResult[] = [];
public flush(): boolean
{
this._results = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._results = [];
for(let i = 0; i < count; i++)
{
const categoryKey = wrapper.readString();
const status = wrapper.readString();
const success = wrapper.readBoolean();
const hasEntry = wrapper.readBoolean();
const entry = hasEntry ? readEarningsEntry(wrapper) : null;
this._results.push({ categoryKey, status, success, hasEntry, entry });
}
return true;
}
public get results(): IEarningsClaimResult[] { return this._results; }
}
@@ -0,0 +1,2 @@
export * from './EarningsCenterParser';
export * from './EarningsClaimResultParser';
@@ -10,6 +10,7 @@ export * from './client';
export * from './commands';
export * from './competition';
export * from './crafting';
export * from './earnings';
export * from './desktop';
export * from './friendlist';
export * from './furnieditor';