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; getFurnitureWallName(typeId: number, extra?: string): string;
selectRoomObject(roomId: number, objectId: number, objectCategory: number): void; selectRoomObject(roomId: number, objectId: number, objectCategory: number): void;
setSelectedAvatar(roomId: number, objectId: number): void; setSelectedAvatar(roomId: number, objectId: number): void;
clearSelectedAvatar(roomId: number): void;
cancelRoomObjectInsert(): void; cancelRoomObjectInsert(): void;
getPetColorResult(petIndex: number, paletteIndex: number): IPetColorResult; getPetColorResult(petIndex: number, paletteIndex: number): IPetColorResult;
getPetColorResultsForTag(petIndex: number, tagName: string): IPetColorResult[]; getPetColorResultsForTag(petIndex: number, tagName: string): IPetColorResult[];
@@ -97,7 +97,7 @@ export class AvatarAssetDownloadManager
const downloadLibrary = new AvatarAssetDownloadLibrary(libraryName, revision, downloadUrl, this._assets); 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 id = (part.id as string);
const type = (part.type as string); const type = (part.type as string);
+16 -1
View File
@@ -24,6 +24,7 @@ export class AvatarImageCache
private _canvas: AvatarCanvas; private _canvas: AvatarCanvas;
private _disposed: boolean; private _disposed: boolean;
private _geometryType: string; private _geometryType: string;
private _defaultAction: string = 'std';
private _unionImages: ImageData[]; private _unionImages: ImageData[];
private _matrix: Matrix; private _matrix: Matrix;
@@ -140,6 +141,7 @@ export class AvatarImageCache
{ {
this._geometryType = k; this._geometryType = k;
this._canvas = null; this._canvas = null;
this._defaultAction = (k === GeometryType.HORIZONTAL) ? 'lay' : 'std';
return; return;
} }
@@ -148,6 +150,7 @@ export class AvatarImageCache
this._geometryType = k; 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 public getImageContainer(key: string, frameNumber: number, forceRefresh: boolean = false): AvatarImageBodyPartContainer
@@ -330,7 +333,19 @@ export class AvatarImageCache
if(!asset) 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); asset = this._assets.getAsset(assetName);
} }
@@ -1,5 +1,475 @@
export const HabboAvatarAnimations = { export const HabboAvatarAnimations = {
'animations': [ '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', 'id': 'Move',
'parts': [ 'parts': [
@@ -94,6 +94,9 @@ export class NitroMessages implements IMessageConfiguration
this._events.set(IncomingHeader.REDEEM_VOUCHER_ERROR, VoucherRedeemErrorMessageEvent); this._events.set(IncomingHeader.REDEEM_VOUCHER_ERROR, VoucherRedeemErrorMessageEvent);
this._events.set(IncomingHeader.REDEEM_VOUCHER_OK, VoucherRedeemOkMessageEvent); this._events.set(IncomingHeader.REDEEM_VOUCHER_OK, VoucherRedeemOkMessageEvent);
// COMMANDS
this._events.set(IncomingHeader.AVAILABLE_COMMANDS, AvailableCommandsEvent);
// CLIENT // CLIENT
this._events.set(IncomingHeader.CLIENT_PING, ClientPingEvent); this._events.set(IncomingHeader.CLIENT_PING, ClientPingEvent);
@@ -587,6 +590,7 @@ export class NitroMessages implements IMessageConfiguration
{ {
// CUSTOM PACKETS // CUSTOM PACKETS
this._composers.set(OutgoingHeader.CLICK_FURNI, ClickFurniMessageComposer); this._composers.set(OutgoingHeader.CLICK_FURNI, ClickFurniMessageComposer);
this._composers.set(OutgoingHeader.CLICK_USER, ClickUserMessageComposer);
// AUTHENTICATION // AUTHENTICATION
this._composers.set(OutgoingHeader.AUTHENTICATION, AuthenticationMessageComposer); 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/campaign';
export * from './messages/incoming/catalog'; export * from './messages/incoming/catalog';
export * from './messages/incoming/client'; export * from './messages/incoming/client';
export * from './messages/incoming/commands';
export * from './messages/incoming/competition'; export * from './messages/incoming/competition';
export * from './messages/incoming/crafting'; export * from './messages/incoming/crafting';
export * from './messages/incoming/desktop'; export * from './messages/incoming/desktop';
@@ -167,6 +168,7 @@ export * from './messages/parser/camera';
export * from './messages/parser/campaign'; export * from './messages/parser/campaign';
export * from './messages/parser/catalog'; export * from './messages/parser/catalog';
export * from './messages/parser/client'; export * from './messages/parser/client';
export * from './messages/parser/commands';
export * from './messages/parser/competition'; export * from './messages/parser/competition';
export * from './messages/parser/crafting'; export * from './messages/parser/crafting';
export * from './messages/parser/desktop'; export * from './messages/parser/desktop';
@@ -7,6 +7,7 @@ export class IncomingHeader
public static ACHIEVEMENT_LIST = 305; public static ACHIEVEMENT_LIST = 305;
public static AUTHENTICATED = 2491; public static AUTHENTICATED = 2491;
public static AUTHENTICATION = -1; public static AUTHENTICATION = -1;
public static AVAILABLE_COMMANDS = 4050;
public static AVAILABILITY_STATUS = 2033; public static AVAILABILITY_STATUS = 2033;
public static BUILDERS_CLUB_EXPIRED = 1452; public static BUILDERS_CLUB_EXPIRED = 1452;
public static CLUB_OFFERS = 2405; 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 './campaign';
export * from './catalog'; export * from './catalog';
export * from './client'; export * from './client';
export * from './commands';
export * from './competition'; export * from './competition';
export * from './crafting'; export * from './crafting';
export * from './desktop'; export * from './desktop';
@@ -1,6 +1,7 @@
export class OutgoingHeader export class OutgoingHeader
{ {
public static CLICK_FURNI = 6002; public static CLICK_FURNI = 6002;
public static CLICK_USER = 10020;
public static ACHIEVEMENT_LIST = 219; public static ACHIEVEMENT_LIST = 219;
public static AUTHENTICATION = -1; 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 './BotRemoveComposer';
export * from './BotSkillSaveComposer'; export * from './BotSkillSaveComposer';
export * from './ClickFurniMessageComposer'; export * from './ClickFurniMessageComposer';
export * from './ClickUserMessageComposer';
export * from './CompostPlantMessageComposer'; export * from './CompostPlantMessageComposer';
export * from './GetItemDataComposer'; export * from './GetItemDataComposer';
export * from './HarvestPetMessageComposer'; 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 './campaign';
export * from './catalog'; export * from './catalog';
export * from './client'; export * from './client';
export * from './commands';
export * from './competition'; export * from './competition';
export * from './crafting'; export * from './crafting';
export * from './desktop'; 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 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) if(this._areaSelectionManager.areaSelectionState === RoomAreaSelectionManager.SELECTING)
{ {
this._activeRoomIsDragged = false; this._activeRoomIsDragged = false;
@@ -2521,11 +2519,18 @@ export class RoomEngine implements IRoomEngine, IRoomCreator, IRoomEngineService
public setSelectedAvatar(roomId: number, objectId: number): void public setSelectedAvatar(roomId: number, objectId: number): void
{ {
if(this._roomObjectEventHandler) return; if(!this._roomObjectEventHandler) return;
this._roomObjectEventHandler.setSelectedAvatar(roomId, objectId, true); this._roomObjectEventHandler.setSelectedAvatar(roomId, objectId, true);
} }
public clearSelectedAvatar(roomId: number): void
{
if(!this._roomObjectEventHandler) return;
this._roomObjectEventHandler.clearSelectedAvatar(roomId);
}
public cancelRoomObjectInsert(): void public cancelRoomObjectInsert(): void
{ {
if(!this._roomObjectEventHandler) return; if(!this._roomObjectEventHandler) return;
+1 -6
View File
@@ -737,19 +737,14 @@ export class RoomMessageHandler
this._roomEngine.updateRoomObjectUserLocation(this._currentRoomId, status.id, location, goal, status.canStandUp, height, direction, status.headDirection); this._roomEngine.updateRoomObjectUserLocation(this._currentRoomId, status.id, location, goal, status.canStandUp, height, direction, status.headDirection);
this._roomEngine.updateRoomObjectUserFlatControl(this._currentRoomId, status.id, null); 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) if(status.id === this._ownRoomIndex)
{ {
try try
{
const locked = sessionStorage.getItem('nitro.session.posLocked');
if(!locked)
{ {
sessionStorage.setItem('nitro.session.lastPosX', status.x.toString()); sessionStorage.setItem('nitro.session.lastPosX', status.x.toString());
sessionStorage.setItem('nitro.session.lastPosY', status.y.toString()); sessionStorage.setItem('nitro.session.lastPosY', status.y.toString());
} }
}
catch(e) { /* ignore */ } catch(e) { /* ignore */ }
} }
+45 -5
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 { 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 { 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 { 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'; import { GetRoomSessionManager, GetSessionDataManager } from '@nitrots/session';
@@ -10,6 +10,7 @@ import { SelectedRoomObjectData } from './utils';
export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomObjectEventManager 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 _eventIds: Map<number, Map<string, string>> = new Map();
private _selectedAvatarId: number = -1; private _selectedAvatarId: number = -1;
@@ -17,6 +18,7 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
private _selectedObjectCategory: number = -2; private _selectedObjectCategory: number = -2;
private _whereYouClickIsWhereYouGo: boolean = true; private _whereYouClickIsWhereYouGo: boolean = true;
private _objectPlacementSource: string = null; private _objectPlacementSource: string = null;
private _pendingAvatarLookTimeout: ReturnType<typeof setTimeout> = null;
constructor( constructor(
private readonly _roomEngine: IRoomEngineServices) 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; if(!event || event.altKey || event.ctrlKey || event.shiftKey) return;
@@ -319,20 +321,25 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
return; 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 private handleRoomObjectMouseClickEvent(event: RoomObjectMouseEvent, roomId: number): void
{ {
if(!event) return; if(!event) return;
this.clickRoomObject(event);
let operation = RoomObjectOperationType.OBJECT_UNDEFINED; let operation = RoomObjectOperationType.OBJECT_UNDEFINED;
const selectedData = this.getSelectedRoomObjectData(roomId); const selectedData = this.getSelectedRoomObjectData(roomId);
if(selectedData) operation = selectedData.operation; if(selectedData) operation = selectedData.operation;
this.clickRoomObject(event, operation);
let didWalk = false; let didWalk = false;
let didMove = false; let didMove = false;
@@ -2075,6 +2082,8 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
{ {
if(!this._roomEngine) return; if(!this._roomEngine) return;
this.clearPendingAvatarLook();
const _local_4 = RoomObjectCategory.UNIT; const _local_4 = RoomObjectCategory.UNIT;
const _local_5 = this._roomEngine.getRoomObject(k, this._selectedAvatarId, _local_4); const _local_5 = this._roomEngine.getRoomObject(k, this._selectedAvatarId, _local_4);
@@ -2101,7 +2110,18 @@ export class RoomObjectEventHandler implements IRoomCanvasMouseListener, IRoomOb
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);
}
} }
} }
@@ -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 private resetSelectedObjectData(roomId: number): void
{ {
if(!this._roomEngine) return; if(!this._roomEngine) return;
@@ -25,6 +25,8 @@ export class FloorplanEditor
private _image: HTMLImageElement; private _image: HTMLImageElement;
public onTilemapChange: (() => void) | null = null;
constructor() constructor()
{ {
const width = TILE_SIZE * MAX_NUM_TILE_PER_AXIS + 20; const width = TILE_SIZE * MAX_NUM_TILE_PER_AXIS + 20;
@@ -297,6 +299,8 @@ export class FloorplanEditor
} }
this.renderSquareSelectionPreview(); this.renderSquareSelectionPreview();
if(this.onTilemapChange) this.onTilemapChange();
} }
private renderSquareSelectionPreview(): void private renderSquareSelectionPreview(): void
@@ -473,6 +477,7 @@ export class FloorplanEditor
this._squareSelectStart = null; this._squareSelectStart = null;
this._squareSelectEnd = null; this._squareSelectEnd = null;
this.clearCanvas(); this.clearCanvas();
this.onTilemapChange = null;
} }
+23 -1
View File
@@ -9,6 +9,8 @@ export class RoomSession implements IRoomSession
private _roomId: number = 0; private _roomId: number = 0;
private _password: string = null; private _password: string = null;
private _spawnX: number = -1;
private _spawnY: number = -1;
private _state: string = RoomSessionEvent.CREATED; private _state: string = RoomSessionEvent.CREATED;
private _tradeMode: number = RoomTradingLevelEnum.NO_TRADING; private _tradeMode: number = RoomTradingLevelEnum.NO_TRADING;
private _doorMode: number = 0; private _doorMode: number = 0;
@@ -57,7 +59,7 @@ export class RoomSession implements IRoomSession
{ {
if(!GetCommunication().connection) return false; 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; return true;
} }
@@ -326,6 +328,26 @@ export class RoomSession implements IRoomSession
this._password = password; 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 public get state(): string
{ {
return this._state; 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'); 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: // Re-send room enter request to the server with saved spawn coordinates.
// 1. Session resume (habbo still in room on server): server treats it // The server will place the habbo directly at the saved position
// as a no-op or re-entry to the same room — harmless. // instead of the door tile, providing a seamless reconnection experience.
// 2. Server restart (habbo not in any room): server places the habbo GetCommunication().connection.send(new RoomEnterComposer(roomId, password, this._savedPosX, this._savedPosY));
// in the room so the client view matches server state.
GetCommunication().connection.send(new RoomEnterComposer(roomId, password));
// Keep the guard up briefly to absorb any stray server-side redirects // Keep the guard up briefly to absorb any stray server-side redirects
// (DesktopViewEvent, etc.) from the login packet sequence, then drop it. // (DesktopViewEvent, etc.) from the login packet sequence, then drop it.
@@ -256,9 +254,9 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
this._sessions.clear(); this._sessions.clear();
this._viewerSession = null; this._viewerSession = null;
// Send the room enter request. The guard stays active to block // Send the room enter request with saved spawn coordinates. The server
// DesktopViewEvent / home room redirects from the server's login sequence. // will place the habbo at the saved position instead of the door tile.
this.createSession(roomId, password); this.createSession(roomId, password, this._savedPosX, this._savedPosY);
// Keep the guard up for a generous window to absorb any DesktopViewEvent // Keep the guard up for a generous window to absorb any DesktopViewEvent
// or other server-side redirects that arrive after authentication. // 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; 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 // Set the guard so DesktopViewEvent from the server's login sequence
// doesn't kick us to hotel view before we enter the room // doesn't kick us to hotel view before we enter the room
this._isReconnecting = true; this._isReconnecting = true;
this.createSession(roomId, password); this.createSession(roomId, password, spawnX, spawnY);
// Drop the guard when room entry succeeds or after timeout // Drop the guard when room entry succeeds or after timeout
this.clearGuardTimer(); this.clearGuardTimer();
@@ -364,7 +381,7 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
sessionStorage.removeItem(STORAGE_KEY_ROOM_PASSWORD); sessionStorage.removeItem(STORAGE_KEY_ROOM_PASSWORD);
// Note: position keys (POS_X, POS_Y) are NOT cleared here. // Note: position keys (POS_X, POS_Y) are NOT cleared here.
// They persist across the disconnect→reconnect cycle and are // 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) catch(e)
{ {
@@ -378,7 +395,6 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
{ {
sessionStorage.removeItem(STORAGE_KEY_POS_X); sessionStorage.removeItem(STORAGE_KEY_POS_X);
sessionStorage.removeItem(STORAGE_KEY_POS_Y); sessionStorage.removeItem(STORAGE_KEY_POS_Y);
sessionStorage.removeItem('nitro.session.posLocked');
} }
catch(e) catch(e)
{ {
@@ -398,9 +414,6 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
this._savedPosX = parseInt(posX, 10); this._savedPosX = parseInt(posX, 10);
this._savedPosY = parseInt(posY, 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 + ')'); NitroLogger.log('[RoomSessionManager] Snapshot saved position (' + this._savedPosX + ', ' + this._savedPosY + ')');
} }
catch(e) 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 private setHandlers(session: IRoomSession): void
{ {
if(!this._handlers || !this._handlers.length) return; if(!this._handlers || !this._handlers.length) return;
@@ -458,12 +453,14 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
return existing; 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(); const session = new RoomSession();
session.roomId = roomId; session.roomId = roomId;
session.password = password; session.password = password;
session.spawnX = spawnX;
session.spawnY = spawnY;
return this.addSession(session); return this.addSession(session);
} }
@@ -579,9 +576,16 @@ export class RoomSessionManager implements IRoomSessionManager, IRoomHandlerList
{ {
NitroLogger.log('[RoomSessionManager] Room ready confirmed - dropping guard in 3s'); NitroLogger.log('[RoomSessionManager] Room ready confirmed - dropping guard in 3s');
// Walk to the saved position (where the user was before disconnect). // If we have saved spawn coordinates, send a walk command so the
// Delay briefly so the server finishes placing the avatar in the room. // avatar moves to their previous position. This handles the EMU-restart
setTimeout(() => this.walkToSavedPosition(), 1000); // 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.clearGuardTimer();
this._reconnectGuardTimer = setTimeout(() => this._reconnectGuardTimer = setTimeout(() =>
@@ -54,7 +54,7 @@ export class RoomChatHandler extends BaseHandler
if(!parser) return; 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 private onRespectReceivedEvent(event: RespectReceivedEvent): void
@@ -136,7 +136,7 @@ export class RoomChatHandler extends BaseHandler
break; 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 private onFloodControlEvent(event: FloodControlEvent): void
@@ -168,6 +168,6 @@ export class RoomChatHandler extends BaseHandler
if(!parser) return; 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) { void main(void) {
vec4 currentColor = texture(uTexture, vTextureCoord); vec4 currentColor = texture(uTexture, vTextureCoord);
vec3 colorLine = uLineColor * currentColor.a; vec3 colorLine = uLineColor;
vec3 colorOverlay = uColor * currentColor.a; vec3 colorOverlay = uColor;
if(currentColor.r == 0.0 && currentColor.g == 0.0 && currentColor.b == 0.0 && currentColor.a > 0.0) { 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); finalColor = vec4(colorLine.r, colorLine.g, colorLine.b, currentColor.a);
} else if(currentColor.a > 0.0) { } 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);
} }
} }
`, `,