Merge branch 'main' into chat-prefix

This commit is contained in:
DuckieTM
2026-03-24 11:13:46 +01:00
committed by GitHub
38 changed files with 2949 additions and 202 deletions
File diff suppressed because one or more lines are too long
+1 -18
View File
@@ -11,19 +11,13 @@ export class SocketConnection implements IConnection
private _codec: ICodec = new EvaWireFormat();
private _dataBuffer: ArrayBuffer = null;
private _isReady: boolean = false;
private _pendingClientMessages: IMessageComposer<unknown[]>[] = [];
private _pendingServerMessages: IMessageDataWrapper[] = [];
private _isAuthenticated: boolean = false;
// Store callbacks for cleanup
private _onOpenCallback: (event: Event) => void = null;
private _onCloseCallback: (event: Event) => void = null;
private _onErrorCallback: (event: Event) => void = null;
private _onMessageCallback: (event: MessageEvent) => void = null;
// Reconnection state
private _socketUrl: string = null;
private _reconnectAttempt: number = 0;
private _reconnectTimer: ReturnType<typeof setTimeout> = null;
@@ -51,8 +45,6 @@ export class SocketConnection implements IConnection
this._socket = new WebSocket(socketUrl);
this._socket.binaryType = 'arraybuffer';
// Store callbacks for cleanup
this._onOpenCallback = () => this.onSocketOpened();
this._onCloseCallback = (event: Event) => this.onSocketClosed(event as CloseEvent);
this._onErrorCallback = () => this.onSocketError();
@@ -72,8 +64,6 @@ export class SocketConnection implements IConnection
{
if(this._isReconnecting)
{
NitroLogger.log('[SocketConnection] Reconnected successfully after ' + this._reconnectAttempt + ' attempt(s)');
this._reconnectAttempt = 0;
this._isReconnecting = false;
@@ -99,8 +89,6 @@ export class SocketConnection implements IConnection
if(code === 1000 || code === 1001)
{
NitroLogger.log('[SocketConnection] Server closed cleanly (code ' + code + ') - not reconnecting');
this._isAuthenticated = false;
this._isReady = false;
@@ -122,7 +110,6 @@ export class SocketConnection implements IConnection
{
if(this._isReconnecting)
{
NitroLogger.log('[SocketConnection] Reconnect attempt ' + this._reconnectAttempt + ' failed');
return;
}
@@ -136,8 +123,6 @@ export class SocketConnection implements IConnection
{
if(this._reconnectAttempt >= SocketConnection.MAX_RECONNECT_ATTEMPTS)
{
NitroLogger.log('[SocketConnection] Max reconnect attempts reached (' + SocketConnection.MAX_RECONNECT_ATTEMPTS + ')');
this._isReconnecting = false;
this._wasAuthenticated = false;
@@ -160,8 +145,6 @@ export class SocketConnection implements IConnection
SocketConnection.MAX_RECONNECT_DELAY_MS
);
NitroLogger.log('[SocketConnection] Reconnecting in ' + Math.round(delay) + 'ms (attempt ' + this._reconnectAttempt + '/' + SocketConnection.MAX_RECONNECT_ATTEMPTS + ')');
GetEventDispatcher().dispatchEvent(new ReconnectEvent(
NitroEventType.SOCKET_RECONNECTING,
this._reconnectAttempt,
@@ -189,7 +172,7 @@ export class SocketConnection implements IConnection
if(this._socket.readyState === WebSocket.OPEN || this._socket.readyState === WebSocket.CONNECTING)
{
try { this._socket.close(); } catch(e) { /* ignore */ }
try { this._socket.close(); } catch(e) {}
}
this._socket = null;
@@ -181,6 +181,7 @@ export class IncomingHeader
public static ROOM_RIGHTS_LIST_REMOVE = 1327;
public static ROOM_RIGHTS_OWNER = 339;
public static ROOM_ROLLING = 3207;
public static WIRED_MOVEMENTS = 3999;
public static ROOM_SCORE = 482;
public static ROOM_SETTINGS = 1498;
public static ROOM_SETTINGS_CHAT = 1191;
@@ -475,6 +476,9 @@ export class IncomingHeader
public static WEEKLY_GAME2_LEADERBOARD = 2196;
public static RENTABLE_FURNI_RENT_OR_BUYOUT_OFFER = 35;
public static HANDSHAKE_IDENTITY_ACCOUNT = 3523;
// Catalog Admin
public static CATALOG_ADMIN_RESULT = 10059;
// Custom Prefixes
public static USER_PREFIXES = 7001;
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { CatalogAdminResultMessageParser } from '../../parser';
export class CatalogAdminResultEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, CatalogAdminResultMessageParser);
}
public getParser(): CatalogAdminResultMessageParser
{
return this.parser as CatalogAdminResultMessageParser;
}
}
@@ -1,4 +1,5 @@
export * from './BonusRareInfoMessageEvent';
export * from './CatalogAdminResultEvent';
export * from './BuildersClubFurniCountMessageEvent';
export * from './BuildersClubSubscriptionStatusMessageEvent';
export * from './BundleDiscountRulesetMessageEvent';
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { WiredMovementsParser } from '../../../parser';
export class WiredMovementsEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, WiredMovementsParser);
}
public getParser(): WiredMovementsParser
{
return this.parser as WiredMovementsParser;
}
}
@@ -1,3 +1,4 @@
export * from './FavoriteMembershipUpdateMessageEvent';
export * from './ObjectsDataUpdateEvent';
export * from './ObjectsRollingEvent';
export * from './WiredMovementsEvent';
@@ -475,6 +475,17 @@ export class OutgoingHeader
public static RENTABLE_GET_RENT_OR_BUYOUT_OFFER = 2518;
public static FURNITURE_PICKUP_ALL = 10017;
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;
public static DELETE_ITEM = 10018;
public static DELETE_PET = 10030;
public static DELETE_BADGE = 10031;
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminCreateOfferComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminCreateOfferComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminCreateOfferComposer>;
constructor(pageId: number, itemId: number, catalogName: string, costCredits: number, costPoints: number, pointsType: number, amount: number, clubOnly: number, extradata: string, haveOffer: boolean, offerIdGroup: number, limitedStack: number, orderNumber: number)
{
this._data = [ pageId, itemId, catalogName, costCredits, costPoints, pointsType, amount, clubOnly, extradata, haveOffer, offerIdGroup, limitedStack, orderNumber ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminCreatePageComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminCreatePageComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminCreatePageComposer>;
constructor(caption: string, caption2: string, layout: string, iconType: number, minRank: number, visible: boolean, enabled: boolean, orderNum: number, parentId: number)
{
this._data = [ caption, caption2, layout, iconType, minRank, visible, enabled, orderNum, parentId ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminDeleteOfferComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminDeleteOfferComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminDeleteOfferComposer>;
constructor(offerId: number)
{
this._data = [ offerId ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminDeletePageComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminDeletePageComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminDeletePageComposer>;
constructor(pageId: number)
{
this._data = [ pageId ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminMoveOfferComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminMoveOfferComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminMoveOfferComposer>;
constructor(offerId: number, orderNumber: number)
{
this._data = [ offerId, orderNumber ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminMovePageComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminMovePageComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminMovePageComposer>;
constructor(pageId: number, newParentId: number, newIndex: number)
{
this._data = [ pageId, newParentId, newIndex ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminPublishComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminPublishComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminPublishComposer>;
constructor()
{
this._data = [];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminSaveOfferComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminSaveOfferComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminSaveOfferComposer>;
constructor(offerId: number, pageId: number, itemId: number, catalogName: string, costCredits: number, costPoints: number, pointsType: number, amount: number, clubOnly: number, extradata: string, haveOffer: boolean, offerIdGroup: number, limitedStack: number, orderNumber: number)
{
this._data = [ offerId, pageId, itemId, catalogName, costCredits, costPoints, pointsType, amount, clubOnly, extradata, haveOffer, offerIdGroup, limitedStack, orderNumber ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class CatalogAdminSavePageComposer implements IMessageComposer<ConstructorParameters<typeof CatalogAdminSavePageComposer>>
{
private _data: ConstructorParameters<typeof CatalogAdminSavePageComposer>;
constructor(pageId: number, caption: string, caption2: string, layout: string, iconType: number, minRank: number, visible: boolean, enabled: boolean, orderNum: number, parentId: number, headline: string, teaser: string, textDetails: string)
{
this._data = [ pageId, caption, caption2, layout, iconType, minRank, visible, enabled, orderNum, parentId, headline, teaser, textDetails ];
}
dispose(): void
{
this._data = null;
}
public getMessageArray()
{
return this._data;
}
}
@@ -1,6 +1,24 @@
export * from './BuildersClubPlaceRoomItemMessageComposer';
export * from './CatalogAdminCreateOfferComposer';
export * from './CatalogAdminCreatePageComposer';
export * from './CatalogAdminDeleteOfferComposer';
export * from './CatalogAdminDeletePageComposer';
export * from './CatalogAdminMoveOfferComposer';
export * from './CatalogAdminMovePageComposer';
export * from './CatalogAdminPublishComposer';
export * from './CatalogAdminSaveOfferComposer';
export * from './CatalogAdminSavePageComposer';
export * from './BuildersClubPlaceWallItemMessageComposer';
export * from './BuildersClubQueryFurniCountMessageComposer';
export * from './CatalogAdminCreateOfferComposer';
export * from './CatalogAdminCreatePageComposer';
export * from './CatalogAdminDeleteOfferComposer';
export * from './CatalogAdminDeletePageComposer';
export * from './CatalogAdminMoveOfferComposer';
export * from './CatalogAdminMovePageComposer';
export * from './CatalogAdminPublishComposer';
export * from './CatalogAdminSaveOfferComposer';
export * from './CatalogAdminSavePageComposer';
export * from './GetBonusRareInfoMessageComposer';
export * from './GetBundleDiscountRulesetComposer';
export * from './GetCatalogIndexComposer';
@@ -0,0 +1,35 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class CatalogAdminResultMessageParser implements IMessageParser
{
private _success: boolean;
private _message: string;
public flush(): boolean
{
this._success = false;
this._message = '';
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._success = wrapper.readBoolean();
this._message = wrapper.readString();
return true;
}
public get success(): boolean
{
return this._success;
}
public get message(): string
{
return this._message;
}
}
@@ -10,7 +10,7 @@ export class CatalogLocalizationData
this._images = [];
this._texts = [];
let totalImages = wrapper.readInt();
let totalImages = Math.min(wrapper.readInt(), 100);
while(totalImages > 0)
{
@@ -19,7 +19,7 @@ export class CatalogLocalizationData
totalImages--;
}
let totalTexts = wrapper.readInt();
let totalTexts = Math.min(wrapper.readInt(), 100);
while(totalTexts > 0)
{
@@ -28,7 +28,7 @@ export class CatalogPageMessageOfferData
this._products = [];
let totalProducts = wrapper.readInt();
let totalProducts = Math.min(wrapper.readInt(), 200);
while(totalProducts > 0)
{
@@ -37,7 +37,7 @@ export class CatalogPageMessageParser implements IMessageParser
this._layoutCode = wrapper.readString();
this._localization = new CatalogLocalizationData(wrapper);
let totalOffers = wrapper.readInt();
let totalOffers = Math.min(wrapper.readInt(), 1000);
while(totalOffers > 0)
{
@@ -51,7 +51,7 @@ export class CatalogPageMessageParser implements IMessageParser
if(wrapper.bytesAvailable)
{
let totalFrontPageItems = wrapper.readInt();
let totalFrontPageItems = Math.min(wrapper.readInt(), 100);
while(totalFrontPageItems > 0)
{
@@ -31,6 +31,11 @@ export class NodeData
return true;
}
private static readonly MAX_OFFERS: number = 1000;
private static readonly MAX_CHILDREN: number = 500;
private static _parseDepth: number = 0;
private static readonly MAX_DEPTH: number = 20;
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
@@ -41,7 +46,7 @@ export class NodeData
this._pageName = wrapper.readString();
this._localization = wrapper.readString();
let totalOffers = wrapper.readInt();
let totalOffers = Math.min(wrapper.readInt(), NodeData.MAX_OFFERS);
while(totalOffers > 0)
{
@@ -50,7 +55,15 @@ export class NodeData
totalOffers--;
}
let totalChildren = wrapper.readInt();
let totalChildren = Math.min(wrapper.readInt(), NodeData.MAX_CHILDREN);
NodeData._parseDepth++;
if(NodeData._parseDepth > NodeData.MAX_DEPTH)
{
NodeData._parseDepth--;
return true;
}
while(totalChildren > 0)
{
@@ -59,6 +72,8 @@ export class NodeData
totalChildren--;
}
NodeData._parseDepth--;
return true;
}
@@ -3,6 +3,7 @@ export * from './BuildersClubFurniCountMessageParser';
export * from './BuildersClubSubscriptionStatusMessageParser';
export * from './BundleDiscountRuleset';
export * from './BundleDiscountRulesetMessageParser';
export * from './CatalogAdminResultMessageParser';
export * from './CatalogIndexMessageParser';
export * from './CatalogLocalizationData';
export * from './CatalogPageExpirationParser';
@@ -0,0 +1,240 @@
import { IMessageDataWrapper, IMessageParser, ObjectRolling } from '@nitrots/api';
import { Vector3d } from '@nitrots/utils';
function parseLocaleFloat(value: string): number
{
if(!value) return 0;
return parseFloat(value.replace(',', '.'));
}
function parseDirection(value: number): number
{
return ((((value % 8) + 8) % 8) * 45);
}
export class WiredUserMovementData extends ObjectRolling
{
constructor(id: number, location: Vector3d, targetLocation: Vector3d, movementType: string, private _bodyDirection: number, private _headDirection: number, private _duration: number)
{
super(id, location, targetLocation, movementType);
}
public get bodyDirection(): number
{
return this._bodyDirection;
}
public get headDirection(): number
{
return this._headDirection;
}
public get duration(): number
{
return this._duration;
}
}
export class WiredFurniMovementData extends ObjectRolling
{
constructor(id: number, location: Vector3d, targetLocation: Vector3d, private _rotation: number, private _duration: number)
{
super(id, location, targetLocation, ObjectRolling.SLIDE);
}
public get rotation(): number
{
return this._rotation;
}
public get duration(): number
{
return this._duration;
}
}
export class WiredWallItemMovementData
{
constructor(private _id: number, private _enabled: boolean, private _values: number[])
{
}
public get id(): number
{
return this._id;
}
public get enabled(): boolean
{
return this._enabled;
}
public get values(): number[]
{
return this._values;
}
}
export class WiredUserDirectionUpdateData
{
constructor(private _id: number, private _headDirection: number, private _bodyDirection: number)
{
}
public get id(): number
{
return this._id;
}
public get headDirection(): number
{
return this._headDirection;
}
public get bodyDirection(): number
{
return this._bodyDirection;
}
}
export class WiredMovementsParser implements IMessageParser
{
private _furniMovements: WiredFurniMovementData[];
private _userMovements: WiredUserMovementData[];
private _wallItemMovements: WiredWallItemMovementData[];
private _userDirectionUpdates: WiredUserDirectionUpdateData[];
public flush(): boolean
{
this._furniMovements = [];
this._userMovements = [];
this._wallItemMovements = [];
this._userDirectionUpdates = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
let totalMovements = wrapper.readInt();
while(totalMovements > 0)
{
const type = wrapper.readInt();
switch(type)
{
case 0:
{
const fromX = wrapper.readInt();
const fromY = wrapper.readInt();
const toX = wrapper.readInt();
const toY = wrapper.readInt();
const fromZ = parseLocaleFloat(wrapper.readString());
const toZ = parseLocaleFloat(wrapper.readString());
const id = wrapper.readInt();
const animationType = wrapper.readInt();
const bodyDirection = parseDirection(wrapper.readInt());
const headDirection = parseDirection(wrapper.readInt());
const duration = wrapper.readInt();
const movementType = (animationType === 0) ? ObjectRolling.MOVE : ObjectRolling.SLIDE;
this._userMovements.push(new WiredUserMovementData(
id,
new Vector3d(fromX, fromY, fromZ),
new Vector3d(toX, toY, toZ),
movementType,
bodyDirection,
headDirection,
duration));
break;
}
case 1:
{
const fromX = wrapper.readInt();
const fromY = wrapper.readInt();
const toX = wrapper.readInt();
const toY = wrapper.readInt();
const fromZ = parseLocaleFloat(wrapper.readString());
const toZ = parseLocaleFloat(wrapper.readString());
const id = wrapper.readInt();
const rotation = wrapper.readInt();
const duration = wrapper.readInt();
this._furniMovements.push(new WiredFurniMovementData(
id,
new Vector3d(fromX, fromY, fromZ),
new Vector3d(toX, toY, toZ),
rotation,
duration));
break;
}
case 2:
{
const id = wrapper.readInt();
const enabled = wrapper.readBoolean();
const values: number[] = [];
let index = 0;
while(index < 9)
{
values.push(wrapper.readInt());
index++;
}
this._wallItemMovements.push(new WiredWallItemMovementData(id, enabled, values));
break;
}
case 3:
{
const id = wrapper.readInt();
const headDirection = parseDirection(wrapper.readInt());
const bodyDirection = parseDirection(wrapper.readInt());
this._userDirectionUpdates.push(new WiredUserDirectionUpdateData(id, headDirection, bodyDirection));
break;
}
default:
return false;
}
totalMovements--;
}
return true;
}
public get itemMovements(): WiredFurniMovementData[]
{
return this._furniMovements;
}
public get unitMovements(): WiredUserMovementData[]
{
return this._userMovements;
}
public get furniMovements(): WiredFurniMovementData[]
{
return this._furniMovements;
}
public get userMovements(): WiredUserMovementData[]
{
return this._userMovements;
}
public get wallItemMovements(): WiredWallItemMovementData[]
{
return this._wallItemMovements;
}
public get userDirectionUpdates(): WiredUserDirectionUpdateData[]
{
return this._userDirectionUpdates;
}
}
@@ -3,3 +3,4 @@ export * from './FavoriteMembershipUpdateMessageParser';
export * from './ObjectData';
export * from './ObjectsDataUpdateParser';
export * from './ObjectsRollingParser';
export * from './WiredMovementsParser';