feat(furni-editor): add WebSocket packets for furniture editor

Add composers, parsers, and events for the Furni Editor feature
communicating via WebSocket with the Arcturus emulator.

Packet handlers:
- Search (10040/10041): search furniture by name, ID, or type
- Detail (10042/10043): get/receive full furniture details by sprite ID
- Update (10044): save furniture property changes
- Create (10045): create new furniture items
- Delete (10046): delete furniture items
- Interactions (10047/10048): list available interaction types
- Result (10049): confirmation response for save/delete/create

New files:
- 7 outgoing composers (Search, Detail, BySprite, Interactions, Update, Create, Delete)
- 4 incoming events (Search, Detail, Interactions, Result)
- 4 parsers (Search, Detail, Interactions, Result)
- Updated IncomingHeader, OutgoingHeader, NitroMessages, and barrel exports
This commit is contained in:
Life
2026-03-22 18:04:27 +01:00
parent 427f7c66e9
commit 35f55d5add
24 changed files with 469 additions and 1 deletions
File diff suppressed because one or more lines are too long
@@ -476,6 +476,15 @@ export class IncomingHeader
public static RENTABLE_FURNI_RENT_OR_BUYOUT_OFFER = 35;
public static HANDSHAKE_IDENTITY_ACCOUNT = 3523;
// Catalog Admin
public static CATALOG_ADMIN_RESULT = 10059;
// Furni Editor
public static FURNI_EDITOR_SEARCH_RESULT = 10040;
public static FURNI_EDITOR_DETAIL_RESULT = 10041;
public static FURNI_EDITOR_INTERACTIONS_RESULT = 10043;
public static FURNI_EDITOR_RESULT = 10044;
// Custom Prefixes
public static USER_PREFIXES = 7001;
public static PREFIX_RECEIVED = 7002;
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { FurniEditorDetailParser } from '../../parser';
export class FurniEditorDetailEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, FurniEditorDetailParser);
}
public getParser(): FurniEditorDetailParser
{
return this.parser as FurniEditorDetailParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { FurniEditorInteractionsParser } from '../../parser';
export class FurniEditorInteractionsEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, FurniEditorInteractionsParser);
}
public getParser(): FurniEditorInteractionsParser
{
return this.parser as FurniEditorInteractionsParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { FurniEditorResultParser } from '../../parser';
export class FurniEditorResultEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, FurniEditorResultParser);
}
public getParser(): FurniEditorResultParser
{
return this.parser as FurniEditorResultParser;
}
}
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { FurniEditorSearchParser } from '../../parser';
export class FurniEditorSearchEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, FurniEditorSearchParser);
}
public getParser(): FurniEditorSearchParser
{
return this.parser as FurniEditorSearchParser;
}
}
@@ -0,0 +1,4 @@
export * from './FurniEditorSearchEvent';
export * from './FurniEditorDetailEvent';
export * from './FurniEditorInteractionsEvent';
export * from './FurniEditorResultEvent';
@@ -13,6 +13,7 @@ export * from './competition';
export * from './crafting';
export * from './desktop';
export * from './friendlist';
export * from './furnieditor';
export * from './game';
export * from './game/directory';
export * from './game/lobby';
@@ -479,6 +479,26 @@ export class OutgoingHeader
public static DELETE_PET = 10030;
public static DELETE_BADGE = 10031;
// Catalog Admin
public static CATALOG_ADMIN_SAVE_PAGE = 10050;
public static CATALOG_ADMIN_CREATE_PAGE = 10051;
public static CATALOG_ADMIN_DELETE_PAGE = 10052;
public static CATALOG_ADMIN_SAVE_OFFER = 10053;
public static CATALOG_ADMIN_CREATE_OFFER = 10054;
public static CATALOG_ADMIN_DELETE_OFFER = 10055;
public static CATALOG_ADMIN_MOVE_OFFER = 10056;
public static CATALOG_ADMIN_MOVE_PAGE = 10057;
public static CATALOG_ADMIN_PUBLISH = 10058;
// Furni Editor
public static FURNI_EDITOR_SEARCH = 10040;
public static FURNI_EDITOR_DETAIL = 10041;
public static FURNI_EDITOR_BY_SPRITE = 10042;
public static FURNI_EDITOR_INTERACTIONS = 10043;
public static FURNI_EDITOR_UPDATE = 10044;
public static FURNI_EDITOR_CREATE = 10045;
public static FURNI_EDITOR_DELETE = 10046;
// Custom Prefixes
public static REQUEST_PREFIXES = 7011;
public static SET_ACTIVE_PREFIX = 7012;
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorBySpriteComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorBySpriteComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorBySpriteComposer>;
constructor(spriteId: number)
{
this._data = [ spriteId ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorCreateComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorCreateComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorCreateComposer>;
constructor(fieldsJson: string)
{
this._data = [ fieldsJson ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorDeleteComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorDeleteComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorDeleteComposer>;
constructor(id: number)
{
this._data = [ id ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorDetailComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorDetailComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorDetailComposer>;
constructor(id: number)
{
this._data = [ id ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorInteractionsComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorInteractionsComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorInteractionsComposer>;
constructor()
{
this._data = [];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorSearchComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorSearchComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorSearchComposer>;
constructor(query: string, type: string, page: number)
{
this._data = [ query, type, page ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class FurniEditorUpdateComposer implements IMessageComposer<ConstructorParameters<typeof FurniEditorUpdateComposer>>
{
private _data: ConstructorParameters<typeof FurniEditorUpdateComposer>;
constructor(id: number, fieldsJson: string)
{
this._data = [ id, fieldsJson ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,7 @@
export * from './FurniEditorSearchComposer';
export * from './FurniEditorDetailComposer';
export * from './FurniEditorBySpriteComposer';
export * from './FurniEditorInteractionsComposer';
export * from './FurniEditorUpdateComposer';
export * from './FurniEditorCreateComposer';
export * from './FurniEditorDeleteComposer';
@@ -8,6 +8,7 @@ export * from './competition';
export * from './crafting';
export * from './desktop';
export * from './friendfurni';
export * from './furnieditor';
export * from './friendlist';
export * from './game';
export * from './game/arena';
@@ -0,0 +1,76 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class FurniEditorDetailParser implements IMessageParser
{
private _item: any;
private _catalogItems: any[];
private _furniDataJson: string;
public flush(): boolean
{
this._item = null;
this._catalogItems = [];
this._furniDataJson = '';
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._item = {
id: wrapper.readInt(),
spriteId: wrapper.readInt(),
itemName: wrapper.readString(),
publicName: wrapper.readString(),
type: wrapper.readString(),
width: wrapper.readInt(),
length: wrapper.readInt(),
stackHeight: wrapper.readDouble(),
allowStack: wrapper.readBoolean(),
allowWalk: wrapper.readBoolean(),
allowSit: wrapper.readBoolean(),
allowLay: wrapper.readBoolean(),
interactionType: wrapper.readString(),
interactionModesCount: wrapper.readInt(),
// Extended fields
allowGift: wrapper.readBoolean(),
allowTrade: wrapper.readBoolean(),
allowRecycle: wrapper.readBoolean(),
allowMarketplaceSell: wrapper.readBoolean(),
allowInventoryStack: wrapper.readBoolean(),
vendingIds: wrapper.readString(),
customparams: wrapper.readString(),
effectIdMale: wrapper.readInt(),
effectIdFemale: wrapper.readInt(),
clothingOnWalk: wrapper.readString(),
multiheight: wrapper.readString(),
description: wrapper.readString(),
usageCount: wrapper.readInt()
};
const catalogCount = wrapper.readInt();
this._catalogItems = [];
for(let i = 0; i < catalogCount; i++)
{
this._catalogItems.push({
id: wrapper.readInt(),
catalogName: wrapper.readString(),
costCredits: wrapper.readInt(),
costPoints: wrapper.readInt(),
pointsType: wrapper.readInt(),
pageId: wrapper.readInt(),
pageName: wrapper.readString()
});
}
this._furniDataJson = wrapper.readString();
return true;
}
public get item(): any { return this._item; }
public get catalogItems(): any[] { return this._catalogItems; }
public get furniDataJson(): string { return this._furniDataJson; }
}
@@ -0,0 +1,29 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class FurniEditorInteractionsParser implements IMessageParser
{
private _interactions: string[];
public flush(): boolean
{
this._interactions = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._interactions = [];
for(let i = 0; i < count; i++)
{
this._interactions.push(wrapper.readString());
}
return true;
}
public get interactions(): string[] { return this._interactions; }
}
@@ -0,0 +1,31 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class FurniEditorResultParser implements IMessageParser
{
private _success: boolean;
private _message: string;
private _id: number;
public flush(): boolean
{
this._success = false;
this._message = '';
this._id = -1;
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._success = wrapper.readBoolean();
this._message = wrapper.readString();
this._id = wrapper.readInt();
return true;
}
public get success(): boolean { return this._success; }
public get message(): string { return this._message; }
public get id(): number { return this._id; }
}
@@ -0,0 +1,53 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class FurniEditorSearchParser implements IMessageParser
{
private _items: any[];
private _total: number;
private _page: number;
public flush(): boolean
{
this._items = [];
this._total = 0;
this._page = 1;
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
const count = wrapper.readInt();
this._items = [];
for(let i = 0; i < count; i++)
{
this._items.push({
id: wrapper.readInt(),
spriteId: wrapper.readInt(),
itemName: wrapper.readString(),
publicName: wrapper.readString(),
type: wrapper.readString(),
width: wrapper.readInt(),
length: wrapper.readInt(),
stackHeight: wrapper.readDouble(),
allowStack: wrapper.readBoolean(),
allowWalk: wrapper.readBoolean(),
allowSit: wrapper.readBoolean(),
allowLay: wrapper.readBoolean(),
interactionType: wrapper.readString(),
interactionModesCount: wrapper.readInt()
});
}
this._total = wrapper.readInt();
this._page = wrapper.readInt();
return true;
}
public get items(): any[] { return this._items; }
public get total(): number { return this._total; }
public get page(): number { return this._page; }
}
@@ -0,0 +1,4 @@
export * from './FurniEditorSearchParser';
export * from './FurniEditorDetailParser';
export * from './FurniEditorInteractionsParser';
export * from './FurniEditorResultParser';
@@ -12,6 +12,7 @@ export * from './competition';
export * from './crafting';
export * from './desktop';
export * from './friendlist';
export * from './furnieditor';
export * from './game';
export * from './game/directory';
export * from './game/lobby';