Merge branch 'main' into chat-prefix

This commit is contained in:
Life
2026-03-22 20:30:11 +01:00
committed by GitHub
24 changed files with 714 additions and 69 deletions
@@ -65,6 +65,7 @@ export interface IRoomEngine
getFurnitureWallName(typeId: number, extra?: string): string;
selectRoomObject(roomId: number, objectId: number, objectCategory: number): void;
setSelectedAvatar(roomId: number, objectId: number): void;
clearSelectedAvatar(roomId: number): void;
cancelRoomObjectInsert(): void;
getPetColorResult(petIndex: number, paletteIndex: number): IPetColorResult;
getPetColorResultsForTag(petIndex: number, tagName: string): IPetColorResult[];
@@ -97,7 +97,7 @@ export class AvatarAssetDownloadManager
const downloadLibrary = new AvatarAssetDownloadLibrary(libraryName, revision, downloadUrl, this._assets);
for(const part of library.parts)
for(const part of (library.parts || []))
{
const id = (part.id as string);
const type = (part.type as string);
+17 -2
View File
@@ -24,6 +24,7 @@ export class AvatarImageCache
private _canvas: AvatarCanvas;
private _disposed: boolean;
private _geometryType: string;
private _defaultAction: string = 'std';
private _unionImages: ImageData[];
private _matrix: Matrix;
@@ -140,6 +141,7 @@ export class AvatarImageCache
{
this._geometryType = k;
this._canvas = null;
this._defaultAction = (k === GeometryType.HORIZONTAL) ? 'lay' : 'std';
return;
}
@@ -147,7 +149,8 @@ export class AvatarImageCache
this.disposeInactiveActions(0);
this._geometryType = k;
this._canvas = null;
this._canvas = null;
this._defaultAction = (k === GeometryType.HORIZONTAL) ? 'lay' : 'std';
}
public getImageContainer(key: string, frameNumber: number, forceRefresh: boolean = false): AvatarImageBodyPartContainer
@@ -330,7 +333,19 @@ export class AvatarImageCache
if(!asset)
{
assetName = (this._scale + '_std_' + partType + '_' + partId + '_' + assetDirection + '_0');
assetName = (this._scale + '_' + assetPartDefinition + '_' + partType + '_' + partId + '_' + assetDirection + '_0');
asset = this._assets.getAsset(assetName);
}
if(!asset)
{
assetName = (this._scale + '_' + this._defaultAction + '_' + partType + '_' + partId + '_' + assetDirection + '_' + frameNumber);
asset = this._assets.getAsset(assetName);
}
if(!asset)
{
assetName = (this._scale + '_' + this._defaultAction + '_' + partType + '_' + partId + '_' + assetDirection + '_0');
asset = this._assets.getAsset(assetName);
}
@@ -1,5 +1,475 @@
export const HabboAvatarAnimations = {
'animations': [
{
'id': 'Default',
'parts': [
{
'setType': 'bd',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'bds',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'lg',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'sh',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'ch',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'cc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'lc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'rc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'lh',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'lhs',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'rh',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'rhs',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'ls',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'rs',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'he',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
},
{
'setType': 'wa',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
}
]
},
{
'id': 'Sit',
'parts': [
{
'setType': 'bd',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'bds',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'lg',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'sh',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'ch',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'cc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'lc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'rc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'sit' },
{ 'number': 1, 'assetPartDefinition': 'sit' },
{ 'number': 2, 'assetPartDefinition': 'sit' },
{ 'number': 3, 'assetPartDefinition': 'sit' },
{ 'number': 4, 'assetPartDefinition': 'sit' },
{ 'number': 5, 'assetPartDefinition': 'sit' },
{ 'number': 6, 'assetPartDefinition': 'sit' },
{ 'number': 7, 'assetPartDefinition': 'sit' }
]
},
{
'setType': 'wa',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'std' },
{ 'number': 1, 'assetPartDefinition': 'std' },
{ 'number': 2, 'assetPartDefinition': 'std' },
{ 'number': 3, 'assetPartDefinition': 'std' },
{ 'number': 4, 'assetPartDefinition': 'std' },
{ 'number': 5, 'assetPartDefinition': 'std' },
{ 'number': 6, 'assetPartDefinition': 'std' },
{ 'number': 7, 'assetPartDefinition': 'std' }
]
}
]
},
{
'id': 'Lay',
'parts': [
{
'setType': 'bd',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'bds',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'lg',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'sh',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'ch',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'cc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'lc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'rc',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'wa',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
},
{
'setType': 'he',
'frames': [
{ 'number': 0, 'assetPartDefinition': 'lay' },
{ 'number': 1, 'assetPartDefinition': 'lay' },
{ 'number': 2, 'assetPartDefinition': 'lay' },
{ 'number': 3, 'assetPartDefinition': 'lay' },
{ 'number': 4, 'assetPartDefinition': 'lay' },
{ 'number': 5, 'assetPartDefinition': 'lay' },
{ 'number': 6, 'assetPartDefinition': 'lay' },
{ 'number': 7, 'assetPartDefinition': 'lay' }
]
}
]
},
{
'id': 'Move',
'parts': [
@@ -93,6 +93,9 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.TARGET_OFFER_NOT_FOUND, TargetedOfferNotFoundEvent);
this._events.set(IncomingHeader.REDEEM_VOUCHER_ERROR, VoucherRedeemErrorMessageEvent);
this._events.set(IncomingHeader.REDEEM_VOUCHER_OK, VoucherRedeemOkMessageEvent);
// COMMANDS
this._events.set(IncomingHeader.AVAILABLE_COMMANDS, AvailableCommandsEvent);
// CLIENT
this._events.set(IncomingHeader.CLIENT_PING, ClientPingEvent);
@@ -587,6 +590,7 @@ export class NitroMessages implements IMessageConfiguration
{
// CUSTOM PACKETS
this._composers.set(OutgoingHeader.CLICK_FURNI, ClickFurniMessageComposer);
this._composers.set(OutgoingHeader.CLICK_USER, ClickUserMessageComposer);
// AUTHENTICATION
this._composers.set(OutgoingHeader.AUTHENTICATION, AuthenticationMessageComposer);
+2
View File
@@ -15,6 +15,7 @@ export * from './messages/incoming/camera';
export * from './messages/incoming/campaign';
export * from './messages/incoming/catalog';
export * from './messages/incoming/client';
export * from './messages/incoming/commands';
export * from './messages/incoming/competition';
export * from './messages/incoming/crafting';
export * from './messages/incoming/desktop';
@@ -167,6 +168,7 @@ export * from './messages/parser/camera';
export * from './messages/parser/campaign';
export * from './messages/parser/catalog';
export * from './messages/parser/client';
export * from './messages/parser/commands';
export * from './messages/parser/competition';
export * from './messages/parser/crafting';
export * from './messages/parser/desktop';
@@ -7,6 +7,7 @@ export class IncomingHeader
public static ACHIEVEMENT_LIST = 305;
public static AUTHENTICATED = 2491;
public static AUTHENTICATION = -1;
public static AVAILABLE_COMMANDS = 4050;
public static AVAILABILITY_STATUS = 2033;
public static BUILDERS_CLUB_EXPIRED = 1452;
public static CLUB_OFFERS = 2405;
@@ -0,0 +1,16 @@
import { IMessageEvent } from '@nitrots/api';
import { MessageEvent } from '@nitrots/events';
import { AvailableCommandsParser } from '../../parser';
export class AvailableCommandsEvent extends MessageEvent implements IMessageEvent
{
constructor(callBack: Function)
{
super(callBack, AvailableCommandsParser);
}
public getParser(): AvailableCommandsParser
{
return this.parser as AvailableCommandsParser;
}
}
@@ -0,0 +1 @@
export * from './AvailableCommandsEvent';
@@ -8,6 +8,7 @@ export * from './camera';
export * from './campaign';
export * from './catalog';
export * from './client';
export * from './commands';
export * from './competition';
export * from './crafting';
export * from './desktop';
@@ -1,6 +1,7 @@
export class OutgoingHeader
{
public static CLICK_FURNI = 6002;
public static CLICK_USER = 10020;
public static ACHIEVEMENT_LIST = 219;
public static AUTHENTICATION = -1;
@@ -0,0 +1,21 @@
import { IMessageComposer } from '@nitrots/api';
export class ClickUserMessageComposer implements IMessageComposer<ConstructorParameters<typeof ClickUserMessageComposer>>
{
private _data: ConstructorParameters<typeof ClickUserMessageComposer>;
constructor(roomUnitId: number)
{
this._data = [ roomUnitId ];
}
public getMessageArray()
{
return this._data;
}
public dispose(): void
{
return;
}
}
@@ -2,6 +2,7 @@ export * from './BotPlaceComposer';
export * from './BotRemoveComposer';
export * from './BotSkillSaveComposer';
export * from './ClickFurniMessageComposer';
export * from './ClickUserMessageComposer';
export * from './CompostPlantMessageComposer';
export * from './GetItemDataComposer';
export * from './HarvestPetMessageComposer';
@@ -0,0 +1,37 @@
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
export class AvailableCommandsParser implements IMessageParser
{
private _commands: { key: string; description: string }[];
public flush(): boolean
{
this._commands = [];
return true;
}
public parse(wrapper: IMessageDataWrapper): boolean
{
if(!wrapper) return false;
this._commands = [];
const count = wrapper.readInt();
for(let i = 0; i < count; i++)
{
this._commands.push({
key: wrapper.readString(),
description: wrapper.readString()
});
}
return true;
}
public get commands(): { key: string; description: string }[]
{
return this._commands;
}
}
@@ -0,0 +1 @@
export * from './AvailableCommandsParser';
@@ -7,6 +7,7 @@ export * from './camera';
export * from './campaign';
export * from './catalog';
export * from './client';
export * from './commands';
export * from './competition';
export * from './crafting';
export * from './desktop';
+8 -3
View File
@@ -2285,8 +2285,6 @@ export class RoomEngine implements IRoomEngine, IRoomCreator, IRoomEngineService
private handleRoomDragging(canvas: IRoomRenderingCanvas, x: number, y: number, type: string, altKey: boolean, ctrlKey: boolean, shiftKey: boolean): boolean
{
if(this.isPlayingGame()) return false;
if(this._areaSelectionManager.areaSelectionState === RoomAreaSelectionManager.SELECTING)
{
this._activeRoomIsDragged = false;
@@ -2521,11 +2519,18 @@ export class RoomEngine implements IRoomEngine, IRoomCreator, IRoomEngineService
public setSelectedAvatar(roomId: number, objectId: number): void
{
if(this._roomObjectEventHandler) return;
if(!this._roomObjectEventHandler) return;
this._roomObjectEventHandler.setSelectedAvatar(roomId, objectId, true);
}
public clearSelectedAvatar(roomId: number): void
{
if(!this._roomObjectEventHandler) return;
this._roomObjectEventHandler.clearSelectedAvatar(roomId);
}
public cancelRoomObjectInsert(): void
{
if(!this._roomObjectEventHandler) return;
+3 -8
View File
@@ -737,18 +737,13 @@ export class RoomMessageHandler
this._roomEngine.updateRoomObjectUserLocation(this._currentRoomId, status.id, location, goal, status.canStandUp, height, direction, status.headDirection);
this._roomEngine.updateRoomObjectUserFlatControl(this._currentRoomId, status.id, null);
// Save own user's position for reconnection (only when not locked by reconnect flow)
// Save own user's position for reconnection
if(status.id === this._ownRoomIndex)
{
try
{
const locked = sessionStorage.getItem('nitro.session.posLocked');
if(!locked)
{
sessionStorage.setItem('nitro.session.lastPosX', status.x.toString());
sessionStorage.setItem('nitro.session.lastPosY', status.y.toString());
}
sessionStorage.setItem('nitro.session.lastPosX', status.x.toString());
sessionStorage.setItem('nitro.session.lastPosY', status.y.toString());
}
catch(e) { /* ignore */ }
}
+50 -10
View File
@@ -1,5 +1,5 @@
import { IFurnitureStackingHeightMap, ILegacyWallGeometry, IObjectData, IRoomCanvasMouseListener, IRoomEngineServices, IRoomGeometry, IRoomObject, IRoomObjectController, IRoomObjectEventManager, ISelectedRoomObjectData, IVector3D, MouseEventType, RoomObjectCategory, RoomObjectOperationType, RoomObjectPlacementSource, RoomObjectType, RoomObjectUserType, RoomObjectVariable } from '@nitrots/api';
import { BotPlaceComposer, ClickFurniMessageComposer, FurnitureColorWheelComposer, FurnitureDiceActivateComposer, FurnitureDiceDeactivateComposer, FurnitureFloorUpdateComposer, FurnitureGroupInfoComposer, FurnitureMultiStateComposer, FurnitureOneWayDoorComposer, FurniturePickupComposer, FurniturePlaceComposer, FurniturePostItPlaceComposer, FurnitureRandomStateComposer, FurnitureWallMultiStateComposer, FurnitureWallUpdateComposer, GetCommunication, GetItemDataComposer, GetResolutionAchievementsMessageComposer, PetMoveComposer, PetPlaceComposer, RemoveWallItemComposer, RoomUnitLookComposer, RoomUnitWalkComposer, SetItemDataMessageComposer, SetObjectDataMessageComposer } from '@nitrots/communication';
import { BotPlaceComposer, ClickFurniMessageComposer, ClickUserMessageComposer, FurnitureColorWheelComposer, FurnitureDiceActivateComposer, FurnitureDiceDeactivateComposer, FurnitureFloorUpdateComposer, FurnitureGroupInfoComposer, FurnitureMultiStateComposer, FurnitureOneWayDoorComposer, FurniturePickupComposer, FurniturePlaceComposer, FurniturePostItPlaceComposer, FurnitureRandomStateComposer, FurnitureWallMultiStateComposer, FurnitureWallUpdateComposer, GetCommunication, GetItemDataComposer, GetResolutionAchievementsMessageComposer, PetMoveComposer, PetPlaceComposer, RemoveWallItemComposer, RoomUnitLookComposer, RoomUnitWalkComposer, SetItemDataMessageComposer, SetObjectDataMessageComposer } from '@nitrots/communication';
import { GetConfiguration } from '@nitrots/configuration';
import { GetEventDispatcher, RoomEngineDimmerStateEvent, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomEngineObjectPlacedOnUserEvent, RoomEngineObjectPlaySoundEvent, RoomEngineRoomAdEvent, RoomEngineSamplePlaybackEvent, RoomEngineTriggerWidgetEvent, RoomEngineUseProductEvent, RoomObjectBadgeAssetEvent, RoomObjectDataRequestEvent, RoomObjectDimmerStateUpdateEvent, RoomObjectEvent, RoomObjectFloorHoleEvent, RoomObjectFurnitureActionEvent, RoomObjectHSLColorEnableEvent, RoomObjectHSLColorEnabledEvent, RoomObjectMouseEvent, RoomObjectMoveEvent, RoomObjectPlaySoundIdEvent, RoomObjectRoomAdEvent, RoomObjectSamplePlaybackEvent, RoomObjectSoundMachineEvent, RoomObjectStateChangedEvent, RoomObjectTileMouseEvent, RoomObjectWallMouseEvent, RoomObjectWidgetRequestEvent, RoomSpriteMouseEvent } from '@nitrots/events';
import { GetRoomSessionManager, GetSessionDataManager } from '@nitrots/session';
@@ -10,6 +10,7 @@ import { SelectedRoomObjectData } from './utils';
export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomObjectEventManager
{
private static readonly CLICK_USER_LOOK_DELAY_MS = 120;
private _eventIds: Map<number, Map<string, string>> = new Map();
private _selectedAvatarId: number = -1;
@@ -17,6 +18,7 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
private _selectedObjectCategory: number = -2;
private _whereYouClickIsWhereYouGo: boolean = true;
private _objectPlacementSource: string = null;
private _pendingAvatarLookTimeout: ReturnType<typeof setTimeout> = null;
constructor(
private readonly _roomEngine: IRoomEngineServices)
@@ -297,7 +299,7 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
}
}
private clickRoomObject(event: RoomObjectMouseEvent): void
private clickRoomObject(event: RoomObjectMouseEvent, operation: string): void
{
if(!event || event.altKey || event.ctrlKey || event.shiftKey) return;
@@ -319,20 +321,25 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
return;
}
if((category === RoomObjectCategory.UNIT) && (operation === RoomObjectOperationType.OBJECT_UNDEFINED) && (objectType === RoomObjectUserType.USER))
{
GetCommunication().connection.send(new ClickUserMessageComposer(objectId));
}
}
private handleRoomObjectMouseClickEvent(event: RoomObjectMouseEvent, roomId: number): void
{
if(!event) return;
this.clickRoomObject(event);
let operation = RoomObjectOperationType.OBJECT_UNDEFINED;
const selectedData = this.getSelectedRoomObjectData(roomId);
if(selectedData) operation = selectedData.operation;
this.clickRoomObject(event, operation);
let didWalk = false;
let didMove = false;
@@ -2075,6 +2082,8 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
{
if(!this._roomEngine) return;
this.clearPendingAvatarLook();
const _local_4 = RoomObjectCategory.UNIT;
const _local_5 = this._roomEngine.getRoomObject(k, this._selectedAvatarId, _local_4);
@@ -2095,15 +2104,26 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
{
_local_5.logic.processUpdateMessage(new ObjectAvatarSelectedMessage(true));
_local_6 = true;
_local_6 = true;
this._selectedAvatarId = _arg_2;
this._selectedAvatarId = _arg_2;
const location = _local_5.getLocation();
const location = _local_5.getLocation();
if(location) GetCommunication().connection.send(new RoomUnitLookComposer(~~(location.x), ~~(location.y)));
}
}
if(location)
{
this._pendingAvatarLookTimeout = setTimeout(() =>
{
this._pendingAvatarLookTimeout = null;
if(this.shouldSuppressAvatarLook()) return;
if(this._selectedAvatarId !== _arg_2) return;
GetCommunication().connection.send(new RoomUnitLookComposer(~~(location.x), ~~(location.y)));
}, RoomObjectEventHandler.CLICK_USER_LOOK_DELAY_MS);
}
}
}
const selectionArrow = this._roomEngine.getRoomObjectSelectionArrow(k);
@@ -2114,6 +2134,26 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
}
}
public clearSelectedAvatar(roomId: number): void
{
this.setSelectedAvatar(roomId, 0, false);
}
private clearPendingAvatarLook(): void
{
if(!this._pendingAvatarLookTimeout) return;
clearTimeout(this._pendingAvatarLookTimeout);
this._pendingAvatarLookTimeout = null;
}
private shouldSuppressAvatarLook(): boolean
{
const control = (globalThis as any).__nitroAvatarClickControl;
return !!control && (control.suppressRotateUntil > Date.now());
}
private resetSelectedObjectData(roomId: number): void
{
if(!this._roomEngine) return;
@@ -25,6 +25,8 @@ export class FloorplanEditor
private _image: HTMLImageElement;
public onTilemapChange: (() => void) | null = null;
constructor()
{
const width = TILE_SIZE * MAX_NUM_TILE_PER_AXIS + 20;
@@ -297,6 +299,8 @@ export class FloorplanEditor
}
this.renderSquareSelectionPreview();
if(this.onTilemapChange) this.onTilemapChange();
}
private renderSquareSelectionPreview(): void
@@ -473,6 +477,7 @@ export class FloorplanEditor
this._squareSelectStart = null;
this._squareSelectEnd = null;
this.clearCanvas();
this.onTilemapChange = null;
}
+23 -1
View File
@@ -9,6 +9,8 @@ export class RoomSession implements IRoomSession
private _roomId: number = 0;
private _password: string = null;
private _spawnX: number = -1;
private _spawnY: number = -1;
private _state: string = RoomSessionEvent.CREATED;
private _tradeMode: number = RoomTradingLevelEnum.NO_TRADING;
private _doorMode: number = 0;
@@ -57,7 +59,7 @@ export class RoomSession implements IRoomSession
{
if(!GetCommunication().connection) return false;
GetCommunication().connection.send(new RoomEnterComposer(this._roomId, this._password));
GetCommunication().connection.send(new RoomEnterComposer(this._roomId, this._password, this._spawnX, this._spawnY));
return true;
}
@@ -326,6 +328,26 @@ export class RoomSession implements IRoomSession
this._password = password;
}
public get spawnX(): number
{
return this._spawnX;
}
public set spawnX(x: number)
{
this._spawnX = x;
}
public get spawnY(): number
{
return this._spawnY;
}
public set spawnY(y: number)
{
this._spawnY = y;
}
public get state(): string
{
return this._state;
+42 -38
View File
@@ -226,12 +226,10 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
{
NitroLogger.log('[RoomSessionManager] Existing session found for room ' + roomId + ' — sending room enter request');
// Re-send room enter request to the server. This handles two cases:
// 1. Session resume (habbo still in room on server): server treats it
// as a no-op or re-entry to the same room — harmless.
// 2. Server restart (habbo not in any room): server places the habbo
// in the room so the client view matches server state.
GetCommunication().connection.send(new RoomEnterComposer(roomId, password));
// Re-send room enter request to the server with saved spawn coordinates.
// The server will place the habbo directly at the saved position
// instead of the door tile, providing a seamless reconnection experience.
GetCommunication().connection.send(new RoomEnterComposer(roomId, password, this._savedPosX, this._savedPosY));
// Keep the guard up briefly to absorb any stray server-side redirects
// (DesktopViewEvent, etc.) from the login packet sequence, then drop it.
@@ -256,9 +254,9 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
this._sessions.clear();
this._viewerSession = null;
// Send the room enter request. The guard stays active to block
// DesktopViewEvent / home room redirects from the server's login sequence.
this.createSession(roomId, password);
// Send the room enter request with saved spawn coordinates. The server
// will place the habbo at the saved position instead of the door tile.
this.createSession(roomId, password, this._savedPosX, this._savedPosY);
// Keep the guard up for a generous window to absorb any DesktopViewEvent
// or other server-side redirects that arrive after authentication.
@@ -299,13 +297,32 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
const password = sessionStorage.getItem(STORAGE_KEY_ROOM_PASSWORD) || null;
NitroLogger.log('[RoomSessionManager] Restoring session for room ' + roomId + ' from sessionStorage');
// Read saved position for page-reload restore
let spawnX = -1;
let spawnY = -1;
try
{
const posX = sessionStorage.getItem(STORAGE_KEY_POS_X);
const posY = sessionStorage.getItem(STORAGE_KEY_POS_Y);
if(posX && posY)
{
spawnX = parseInt(posX, 10);
spawnY = parseInt(posY, 10);
if(isNaN(spawnX) || isNaN(spawnY)) { spawnX = -1; spawnY = -1; }
}
}
catch(e) { /* ignore */ }
NitroLogger.log('[RoomSessionManager] Restoring session for room ' + roomId + ' from sessionStorage (spawn: ' + spawnX + ', ' + spawnY + ')');
// Set the guard so DesktopViewEvent from the server's login sequence
// doesn't kick us to hotel view before we enter the room
this._isReconnecting = true;
this.createSession(roomId, password);
this.createSession(roomId, password, spawnX, spawnY);
// Drop the guard when room entry succeeds or after timeout
this.clearGuardTimer();
@@ -364,7 +381,7 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
sessionStorage.removeItem(STORAGE_KEY_ROOM_PASSWORD);
// Note: position keys (POS_X, POS_Y) are NOT cleared here.
// They persist across the disconnect→reconnect cycle and are
// consumed by walkToSavedPosition() after successful re-entry.
// sent to the server as spawn coordinates during re-entry.
}
catch(e)
{
@@ -378,7 +395,6 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
{
sessionStorage.removeItem(STORAGE_KEY_POS_X);
sessionStorage.removeItem(STORAGE_KEY_POS_Y);
sessionStorage.removeItem('nitro.session.posLocked');
}
catch(e)
{
@@ -398,9 +414,6 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
this._savedPosX = parseInt(posX, 10);
this._savedPosY = parseInt(posY, 10);
// Lock position saving so room re-entry doesn't overwrite saved position
sessionStorage.setItem('nitro.session.posLocked', '1');
NitroLogger.log('[RoomSessionManager] Snapshot saved position (' + this._savedPosX + ', ' + this._savedPosY + ')');
}
catch(e)
@@ -410,24 +423,6 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
}
}
private walkToSavedPosition(): void
{
const x = this._savedPosX;
const y = this._savedPosY;
// Reset after use
this._savedPosX = -1;
this._savedPosY = -1;
// Unlock position saving so normal movement is tracked again
try { sessionStorage.removeItem('nitro.session.posLocked'); } catch(e) { /* ignore */ }
if(x < 0 || y < 0 || isNaN(x) || isNaN(y)) return;
NitroLogger.log('[RoomSessionManager] Walking to saved position (' + x + ', ' + y + ')');
GetCommunication().connection.send(new RoomUnitWalkComposer(x, y));
}
private setHandlers(session: IRoomSession): void
{
if(!this._handlers || !this._handlers.length) return;
@@ -458,12 +453,14 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
return existing;
}
public createSession(roomId: number, password: string = null): boolean
public createSession(roomId: number, password: string = null, spawnX: number = -1, spawnY: number = -1): boolean
{
const session = new RoomSession();
session.roomId = roomId;
session.password = password;
session.spawnX = spawnX;
session.spawnY = spawnY;
return this.addSession(session);
}
@@ -579,9 +576,16 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
{
NitroLogger.log('[RoomSessionManager] Room ready confirmed - dropping guard in 3s');
// Walk to the saved position (where the user was before disconnect).
// Delay briefly so the server finishes placing the avatar in the room.
setTimeout(() => this.walkToSavedPosition(), 1000);
// If we have saved spawn coordinates, send a walk command so the
// avatar moves to their previous position. This handles the EMU-restart
// case where the server has no ghost session and spawns at the door.
if(this._savedPosX >= 0 && this._savedPosY >= 0)
{
NitroLogger.log('[RoomSessionManager] Walking to saved position (' + this._savedPosX + ', ' + this._savedPosY + ')');
GetCommunication().connection.send(new RoomUnitWalkComposer(this._savedPosX, this._savedPosY));
this._savedPosX = -1;
this._savedPosY = -1;
}
this.clearGuardTimer();
this._reconnectGuardTimer = setTimeout(() =>
@@ -54,7 +54,7 @@ export class RoomChatHandler extends BaseHandler
if(!parser) return;
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, parser.giverUserId, '', RoomSessionChatEvent.CHAT_TYPE_HAND_ITEM_RECEIVED, SystemChatStyleEnum.GENERIC, [], parser.handItemType));
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, parser.giverUserId, '', RoomSessionChatEvent.CHAT_TYPE_HAND_ITEM_RECEIVED, SystemChatStyleEnum.GENERIC, [], null, parser.handItemType));
}
private onRespectReceivedEvent(event: RespectReceivedEvent): void
@@ -136,7 +136,7 @@ export class RoomChatHandler extends BaseHandler
break;
}
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, petData.roomIndex, '', chatType, SystemChatStyleEnum.GENERIC, null, userRoomIndex));
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, petData.roomIndex, '', chatType, SystemChatStyleEnum.GENERIC, [], null, userRoomIndex));
}
private onFloodControlEvent(event: FloodControlEvent): void
@@ -168,6 +168,6 @@ export class RoomChatHandler extends BaseHandler
if(!parser) return;
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, session.ownRoomIndex, '', RoomSessionChatEvent.CHAT_TYPE_MUTE_REMAINING, SystemChatStyleEnum.GENERIC, [], parser.seconds));
GetEventDispatcher().dispatchEvent(new RoomSessionChatEvent(RoomSessionChatEvent.CHAT_EVENT, session, session.ownRoomIndex, '', RoomSessionChatEvent.CHAT_TYPE_MUTE_REMAINING, SystemChatStyleEnum.GENERIC, [], null, parser.seconds));
}
}
+4 -3
View File
@@ -63,13 +63,14 @@ export class WiredFilter extends Filter
void main(void) {
vec4 currentColor = texture(uTexture, vTextureCoord);
vec3 colorLine = uLineColor * currentColor.a;
vec3 colorOverlay = uColor * currentColor.a;
vec3 colorLine = uLineColor;
vec3 colorOverlay = uColor;
if(currentColor.r == 0.0 && currentColor.g == 0.0 && currentColor.b == 0.0 && currentColor.a > 0.0) {
finalColor = vec4(colorLine.r, colorLine.g, colorLine.b, currentColor.a);
} else if(currentColor.a > 0.0) {
finalColor = vec4(colorOverlay.r, colorOverlay.g, colorOverlay.b, currentColor.a);
vec3 blendedOverlay = mix(currentColor.rgb, colorOverlay, 0.28);
finalColor = vec4(blendedOverlay.r, blendedOverlay.g, blendedOverlay.b, currentColor.a);
}
}
`,