feat: rare values + fortune wheel protocol + prize editor

Composers/parsers/events for rare values + wheel (open/spin/buy/data/result/
recent-wins) + admin (get/save prizes), headers 9300-9305 / 9400-9404.
fix: figure map uses split-aware loadGamedata (raw fetch broke on tier-manifest
gamedata, silently empty avatars).
This commit is contained in:
medievalshell
2026-05-28 02:39:01 +02:00
parent 4a74fb948d
commit 87eec0563d
29 changed files with 431 additions and 17 deletions
@@ -8,6 +8,9 @@ import { HanditemBlockStateMessageEvent } from './messages';
import { TranslationLanguagesEvent, TranslationLanguagesRequestComposer, TranslationResultEvent, TranslationTextRequestComposer } from './messages';
import { YouTubeRoomBroadcastEvent, YouTubeRoomPlayComposer, YouTubeRoomSettingsComposer, YouTubeRoomSettingsEvent, YouTubeRoomWatchersEvent, YouTubeRoomWatchingComposer } from './messages';
import { HousekeepingActionLogEvent, HousekeepingActionResultEvent, HousekeepingBanUserComposer, HousekeepingDashboardEvent, HousekeepingDeleteRoomComposer, HousekeepingFindRoomByIdComposer, HousekeepingFindUserByIdComposer, HousekeepingFindUserByNameComposer, HousekeepingForceDisconnectUserComposer, HousekeepingGetDashboardComposer, HousekeepingGiveCreditsComposer, HousekeepingGiveCurrencyComposer, HousekeepingGrantItemComposer, HousekeepingKickAllFromRoomComposer, HousekeepingKickUserComposer, HousekeepingListActionLogComposer, HousekeepingMuteRoomComposer, HousekeepingMuteUserComposer, HousekeepingResetUserPasswordComposer, HousekeepingRoomDetailEvent, HousekeepingRoomListEvent, HousekeepingRoomStateComposer, HousekeepingSearchRoomsComposer, HousekeepingSendHotelAlertComposer, HousekeepingSetHcSubscriptionComposer, HousekeepingSetUserRankComposer, HousekeepingTradeLockUserComposer, HousekeepingTransferRoomOwnershipComposer, HousekeepingUnbanUserComposer, HousekeepingUserDetailEvent } from './messages';
import { RareValuesEvent, RequestRareValuesComposer } from './messages';
import { WheelBuySpinComposer, WheelDataEvent, WheelOpenComposer, WheelRecentWinsEvent, WheelResultEvent, WheelSpinComposer } from './messages';
import { WheelAdminGetPrizesComposer, WheelAdminPrizesEvent, WheelAdminSavePrizesComposer } from './messages';
export class NitroMessages implements IMessageConfiguration
{
private _events: Map<number, Function>;
@@ -514,6 +517,13 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.HOUSEKEEPING_ROOM_LIST, HousekeepingRoomListEvent);
this._events.set(IncomingHeader.HOUSEKEEPING_DASHBOARD, HousekeepingDashboardEvent);
this._events.set(IncomingHeader.HOUSEKEEPING_ACTION_LOG, HousekeepingActionLogEvent);
// Custom features
this._events.set(IncomingHeader.RARE_VALUES, RareValuesEvent);
this._events.set(IncomingHeader.WHEEL_DATA, WheelDataEvent);
this._events.set(IncomingHeader.WHEEL_RESULT, WheelResultEvent);
this._events.set(IncomingHeader.WHEEL_RECENT_WINS, WheelRecentWinsEvent);
this._events.set(IncomingHeader.WHEEL_ADMIN_PRIZES, WheelAdminPrizesEvent);
this._events.set(IncomingHeader.WIRED_REWARD, WiredRewardResultMessageEvent);
this._events.set(IncomingHeader.WIRED_SAVE, WiredSaveSuccessEvent);
this._events.set(IncomingHeader.WIRED_ERROR, WiredValidationErrorEvent);
@@ -1290,6 +1300,14 @@ export class NitroMessages implements IMessageConfiguration
this._composers.set(OutgoingHeader.HOUSEKEEPING_SEND_HOTEL_ALERT, HousekeepingSendHotelAlertComposer);
this._composers.set(OutgoingHeader.HOUSEKEEPING_GET_DASHBOARD, HousekeepingGetDashboardComposer);
this._composers.set(OutgoingHeader.HOUSEKEEPING_LIST_ACTION_LOG, HousekeepingListActionLogComposer);
// Custom features
this._composers.set(OutgoingHeader.REQUEST_RARE_VALUES, RequestRareValuesComposer);
this._composers.set(OutgoingHeader.WHEEL_OPEN, WheelOpenComposer);
this._composers.set(OutgoingHeader.WHEEL_SPIN, WheelSpinComposer);
this._composers.set(OutgoingHeader.WHEEL_BUY_SPIN, WheelBuySpinComposer);
this._composers.set(OutgoingHeader.WHEEL_ADMIN_GET_PRIZES, WheelAdminGetPrizesComposer);
this._composers.set(OutgoingHeader.WHEEL_ADMIN_SAVE_PRIZES, WheelAdminSavePrizesComposer);
}
public get events(): Map<number, Function>
@@ -511,4 +511,11 @@ export class IncomingHeader
public static HOUSEKEEPING_ROOM_LIST = 9203;
public static HOUSEKEEPING_DASHBOARD = 9204;
public static HOUSEKEEPING_ACTION_LOG = 9205;
// Custom features — IDs 9400+ reserved
public static RARE_VALUES = 9400;
public static WHEEL_DATA = 9401;
public static WHEEL_RESULT = 9402;
public static WHEEL_RECENT_WINS = 9403;
public static WHEEL_ADMIN_PRIZES = 9404;
}
@@ -25,6 +25,8 @@ export * from './groupforums';
export * from './handshake';
export * from './help';
export * from './housekeeping';
export * from './rarevalues';
export * from './wheel';
export * from './inventory';
export * from './inventory/achievements';
export * from './inventory/avatareffect';
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { RareValuesParser } from '../../parser';
export class RareValuesEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, RareValuesParser);
}
public getParser(): RareValuesParser
{
return this.parser as RareValuesParser;
}
}
@@ -0,0 +1 @@
export * from './RareValuesEvent';
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { WheelAdminPrizesParser } from '../../parser';
export class WheelAdminPrizesEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, WheelAdminPrizesParser);
}
public getParser(): WheelAdminPrizesParser
{
return this.parser as WheelAdminPrizesParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { WheelDataParser } from '../../parser';
export class WheelDataEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, WheelDataParser);
}
public getParser(): WheelDataParser
{
return this.parser as WheelDataParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { WheelRecentWinsParser } from '../../parser';
export class WheelRecentWinsEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, WheelRecentWinsParser);
}
public getParser(): WheelRecentWinsParser
{
return this.parser as WheelRecentWinsParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { WheelResultParser } from '../../parser';
export class WheelResultEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, WheelResultParser);
}
public getParser(): WheelResultParser
{
return this.parser as WheelResultParser;
}
}
@@ -0,0 +1,4 @@
export * from './WheelDataEvent';
export * from './WheelResultEvent';
export * from './WheelRecentWinsEvent';
export * from './WheelAdminPrizesEvent';
@@ -548,4 +548,12 @@ export class OutgoingHeader
public static HOUSEKEEPING_SEND_HOTEL_ALERT = 9121;
public static HOUSEKEEPING_GET_DASHBOARD = 9122;
public static HOUSEKEEPING_LIST_ACTION_LOG = 9123;
// Custom features — IDs 9300+ reserved
public static REQUEST_RARE_VALUES = 9300;
public static WHEEL_OPEN = 9301;
public static WHEEL_SPIN = 9302;
public static WHEEL_BUY_SPIN = 9303;
public static WHEEL_ADMIN_GET_PRIZES = 9304;
public static WHEEL_ADMIN_SAVE_PRIZES = 9305;
}
@@ -22,6 +22,8 @@ export * from './groupforums';
export * from './handshake';
export * from './help';
export * from './housekeeping';
export * from './rarevalues';
export * from './wheel';
export * from './inventory';
export * from './inventory/avatareffect';
export * from './inventory/badges';
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class RequestRareValuesComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1 @@
export * from './RequestRareValuesComposer';
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class WheelAdminGetPrizesComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,32 @@
import { IMessageComposer } from '@nitrots/api';
export interface IWheelAdminPrizeEdit
{
id: number;
type: string;
value: string;
amount: number;
pointsType: number;
weight: number;
label: string;
}
export class WheelAdminSavePrizesComposer implements IMessageComposer<(number | string)[]>
{
private _data: (number | string)[];
constructor(prizes: IWheelAdminPrizeEdit[])
{
const data: (number | string)[] = [ prizes.length ];
for(const prize of prizes)
{
data.push(prize.id, prize.type, prize.value, prize.amount, prize.pointsType, prize.weight, prize.label);
}
this._data = data;
}
public getMessageArray(): (number | string)[] { return this._data; }
public dispose(): void { return; }
}
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class WheelBuySpinComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class WheelOpenComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,7 @@
import { IMessageComposer } from '@nitrots/api';
export class WheelSpinComposer implements IMessageComposer<[]>
{
public getMessageArray(): [] { return []; }
public dispose(): void { return; }
}
@@ -0,0 +1,5 @@
export * from './WheelOpenComposer';
export * from './WheelSpinComposer';
export * from './WheelBuySpinComposer';
export * from './WheelAdminGetPrizesComposer';
export * from './WheelAdminSavePrizesComposer';
@@ -25,6 +25,8 @@ export * from './groupforums';
export * from './handshake';
export * from './help';
export * from './housekeeping';
export * from './rarevalues';
export * from './wheel';
export * from './inventory';
export * from './inventory/achievements';
export * from './inventory/avatareffect';
@@ -0,0 +1,41 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export interface IRareValue
{
credits: number;
points: number;
pointsType: number;
}
export class RareValuesParser implements IMessageParser
{
private _values: Map<number, IRareValue> = new Map();
public flush(): boolean
{
this._values = new Map();
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
for(let i = 0; i < count; i++)
{
const spriteId = wrapper.readInt();
const credits = wrapper.readInt();
const points = wrapper.readInt();
const pointsType = wrapper.readInt();
this._values.set(spriteId, { credits, points, pointsType });
}
return true;
}
public get values(): Map<number, IRareValue> { return this._values; }
}
@@ -0,0 +1 @@
export * from './RareValuesParser';
@@ -0,0 +1,49 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export interface IWheelAdminPrize
{
id: number;
type: string;
value: string;
amount: number;
pointsType: number;
weight: number;
label: string;
}
export class WheelAdminPrizesParser implements IMessageParser
{
private _prizes: IWheelAdminPrize[] = [];
public flush(): boolean
{
this._prizes = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._prizes = [];
for(let i = 0; i < count; i++)
{
this._prizes.push({
id: wrapper.readInt(),
type: wrapper.readString(),
value: wrapper.readString(),
amount: wrapper.readInt(),
pointsType: wrapper.readInt(),
weight: wrapper.readInt(),
label: wrapper.readString()
});
}
return true;
}
public get prizes(): IWheelAdminPrize[] { return this._prizes; }
}
@@ -0,0 +1,66 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export interface IWheelPrize
{
id: number;
type: string; // item | badge | credits | points | spin | nothing
spriteId: number; // item only (furni icon), else 0
badgeCode: string; // badge only, else ''
amount: number;
pointsType: number;
label: string;
}
export class WheelDataParser implements IMessageParser
{
private _freeSpins: number = 0;
private _extraSpins: number = 0;
private _spinCost: number = 0;
private _spinCostType: number = 0;
private _prizes: IWheelPrize[] = [];
public flush(): boolean
{
this._freeSpins = 0;
this._extraSpins = 0;
this._spinCost = 0;
this._spinCostType = 0;
this._prizes = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._freeSpins = wrapper.readInt();
this._extraSpins = wrapper.readInt();
this._spinCost = wrapper.readInt();
this._spinCostType = wrapper.readInt();
const count = wrapper.readInt();
this._prizes = [];
for(let i = 0; i < count; i++)
{
this._prizes.push({
id: wrapper.readInt(),
type: wrapper.readString(),
spriteId: wrapper.readInt(),
badgeCode: wrapper.readString(),
amount: wrapper.readInt(),
pointsType: wrapper.readInt(),
label: wrapper.readString()
});
}
return true;
}
public get freeSpins(): number { return this._freeSpins; }
public get extraSpins(): number { return this._extraSpins; }
public get spinCost(): number { return this._spinCost; }
public get spinCostType(): number { return this._spinCostType; }
public get prizes(): IWheelPrize[] { return this._prizes; }
}
@@ -0,0 +1,41 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export interface IWheelRecentWin
{
username: string;
look: string;
prizeLabel: string;
}
export class WheelRecentWinsParser implements IMessageParser
{
private _wins: IWheelRecentWin[] = [];
public flush(): boolean
{
this._wins = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._wins = [];
for(let i = 0; i < count; i++)
{
this._wins.push({
username: wrapper.readString(),
look: wrapper.readString(),
prizeLabel: wrapper.readString()
});
}
return true;
}
public get wins(): IWheelRecentWin[] { return this._wins; }
}
@@ -0,0 +1,24 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class WheelResultParser implements IMessageParser
{
private _prizeId: number = 0;
public flush(): boolean
{
this._prizeId = 0;
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._prizeId = wrapper.readInt();
return true;
}
public get prizeId(): number { return this._prizeId; }
}
@@ -0,0 +1,4 @@
export * from './WheelDataParser';
export * from './WheelResultParser';
export * from './WheelRecentWinsParser';
export * from './WheelAdminPrizesParser';