You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-19 15:06:20 +00:00
refactor(session): fold permission map into UserPermissionsEvent
Drop the separate UserPermissionsMapEvent / UserPermissionsMapParser and the IncomingHeader.USER_PERMISSIONS_MAP = 10070 registration — the resolved permission map now rides on the existing UserPermissionsEvent as a third optional trailing block, after the rank metadata one. Same wire data, one fewer packet, one fewer event registration, one fewer handler. Wire layout (UserPermissionsEvent / header 411): int clubLevel int securityLevel bool isAmbassador --- rank metadata (Arcturus ≥ 4.2.10) --- int rankId string rankName string rankBadge string rankPrefix string rankPrefixColor --- resolved permission map (Arcturus ≥ 4.2.10) --- int count loop: string permission_key + int value (1=ALLOWED, 2=ROOM_OWNER) Both trailing blocks are guarded by `bytesAvailable` in UserPermissionsParser so older emulators that don't append them still parse cleanly. SessionDataManager.onUserPermissionsEvent is now the single handler: - updates clubLevel/securityLevel/isAmbassador/rank* AND _permissions; - invalidates BOTH the user-data snapshot and the permissions snapshot (dispatching the two distinct NitroEventType.SESSION_DATA_UPDATED / USER_PERMISSIONS_UPDATED events). The two distinct invalidation events stay so React consumers can subscribe granularly — useHasPermission(key) only triggers on a real permission map flip, not on every session-data bump. Companion Arcturus change (feat/react19-emu-update) folds UserPermissionsMapComposer into UserPermissionsComposer and removes the second sendResponse in HabboManager.setRank + SecureLoginEvent. Verification: yarn compile:fast clean, vitest 138/138.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -255,8 +255,6 @@ export class IncomingHeader
|
||||
public static USER_OUTFITS = 3315;
|
||||
public static USER_PERKS = 2586;
|
||||
public static USER_PERMISSIONS = 411;
|
||||
// Resolved permission map (Arcturus extension, Outgoing.UserPermissionsMapComposer = 10070).
|
||||
public static USER_PERMISSIONS_MAP = 10070;
|
||||
public static USER_PET_ADD = 2101;
|
||||
public static USER_PET_REMOVE = 3253;
|
||||
public static USER_PETS = 3522;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { IMessageEvent } from '@nitrots/api';
|
||||
import { MessageEvent } from '@nitrots/events';
|
||||
import { UserPermissionsMapParser } from '../../../parser';
|
||||
|
||||
export class UserPermissionsMapEvent extends MessageEvent implements IMessageEvent
|
||||
{
|
||||
constructor(callBack: Function)
|
||||
{
|
||||
super(callBack, UserPermissionsMapParser);
|
||||
}
|
||||
|
||||
public getParser(): UserPermissionsMapParser
|
||||
{
|
||||
return this.parser as UserPermissionsMapParser;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
export * from './UserPermissionsEvent';
|
||||
export * from './UserPermissionsMapEvent';
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { IMessageDataWrapper, IMessageParser } from '@nitrots/api';
|
||||
|
||||
/**
|
||||
* Parses the resolved permission map for the current user
|
||||
* (Arcturus-Morningstar-Extended ≥ 4.2.10, header
|
||||
* `Outgoing.UserPermissionsMapComposer = 10070`).
|
||||
*
|
||||
* Wire layout:
|
||||
* int count
|
||||
* loop: string permission_key + int value (1 = ALLOWED, 2 = ROOM_OWNER)
|
||||
*
|
||||
* Only permissions whose `PermissionSetting != DISALLOWED` are sent —
|
||||
* absence means "no". The renderer-side `SessionDataManager` consumes
|
||||
* this and exposes it via a snapshot getter; React-side
|
||||
* `useHasPermission(key)` drives UI gates against the real
|
||||
* `permission_definitions.permission_key` strings instead of
|
||||
* deployment-specific rank IDs.
|
||||
*/
|
||||
export class UserPermissionsMapParser implements IMessageParser
|
||||
{
|
||||
private _permissions: Map<string, number> = new Map();
|
||||
|
||||
public flush(): boolean
|
||||
{
|
||||
this._permissions = new Map();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public parse(wrapper: IMessageDataWrapper): boolean
|
||||
{
|
||||
if(!wrapper) return false;
|
||||
|
||||
const count = wrapper.readInt();
|
||||
const fresh = new Map<string, number>();
|
||||
|
||||
for(let i = 0; i < count; i++)
|
||||
{
|
||||
const key = wrapper.readString();
|
||||
const value = wrapper.readInt();
|
||||
|
||||
fresh.set(key, value);
|
||||
}
|
||||
|
||||
this._permissions = fresh;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get permissions(): ReadonlyMap<string, number>
|
||||
{
|
||||
return this._permissions;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export class UserPermissionsParser implements IMessageParser
|
||||
private _rankBadge: string;
|
||||
private _rankPrefix: string;
|
||||
private _rankPrefixColor: string;
|
||||
private _permissions: Map<string, number> = new Map();
|
||||
|
||||
public flush(): boolean
|
||||
{
|
||||
@@ -21,6 +22,7 @@ export class UserPermissionsParser implements IMessageParser
|
||||
this._rankBadge = '';
|
||||
this._rankPrefix = '';
|
||||
this._rankPrefixColor = '';
|
||||
this._permissions = new Map();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -34,9 +36,9 @@ export class UserPermissionsParser implements IMessageParser
|
||||
this._isAmbassador = wrapper.readBoolean();
|
||||
|
||||
// Optional trailing block (Arcturus-Morningstar-Extended ≥ 4.2.10):
|
||||
// rank metadata appended in a backward-compatible way. Older
|
||||
// emulators don't write these bytes so we keep the defaults
|
||||
// from flush().
|
||||
// rank metadata + resolved permission map appended in a
|
||||
// backward-compatible way. Older emulators don't write these
|
||||
// bytes so we keep the defaults from flush().
|
||||
if(!wrapper.bytesAvailable) return true;
|
||||
|
||||
this._rankId = wrapper.readInt();
|
||||
@@ -45,6 +47,25 @@ export class UserPermissionsParser implements IMessageParser
|
||||
this._rankPrefix = wrapper.readString();
|
||||
this._rankPrefixColor = wrapper.readString();
|
||||
|
||||
if(!wrapper.bytesAvailable) return true;
|
||||
|
||||
// Resolved permission map: int count + (string key, int value)*.
|
||||
// value 1 = ALLOWED, 2 = ROOM_OWNER. Only entries with
|
||||
// PermissionSetting != DISALLOWED are sent; absence on the client
|
||||
// means "no" (useHasPermission(key) returns false).
|
||||
const count = wrapper.readInt();
|
||||
const permissions = new Map<string, number>();
|
||||
|
||||
for(let i = 0; i < count; i++)
|
||||
{
|
||||
const key = wrapper.readString();
|
||||
const value = wrapper.readInt();
|
||||
|
||||
permissions.set(key, value);
|
||||
}
|
||||
|
||||
this._permissions = permissions;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -87,4 +108,9 @@ export class UserPermissionsParser implements IMessageParser
|
||||
{
|
||||
return this._rankPrefixColor;
|
||||
}
|
||||
|
||||
public get permissions(): ReadonlyMap<string, number>
|
||||
{
|
||||
return this._permissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './UserPermissionsMapParser';
|
||||
export * from './UserPermissionsParser';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IFurnitureData, IGroupInformationManager, IMessageComposer, IMessageEvent, IProductData, ISessionDataManager, IUserDataSnapshot, NoobnessLevelEnum, SecurityLevel } from '@nitrots/api';
|
||||
import { AccountSafetyLockStatusChangeMessageEvent, AccountSafetyLockStatusChangeParser, AvailabilityStatusMessageEvent, ChangeUserNameResultMessageEvent, EmailStatusResultEvent, FigureUpdateEvent, GetCommunication, GetUserTagsComposer, InClientLinkEvent, MysteryBoxKeysEvent, NoobnessLevelMessageEvent, PetRespectComposer, PetScratchFailedMessageEvent, RoomReadyMessageEvent, RoomUnitChatComposer, UserInfoEvent, UserNameChangeMessageEvent, UserPermissionsEvent, UserPermissionsMapEvent, UserRespectComposer, UserTagsMessageEvent } from '@nitrots/communication';
|
||||
import { AccountSafetyLockStatusChangeMessageEvent, AccountSafetyLockStatusChangeParser, AvailabilityStatusMessageEvent, ChangeUserNameResultMessageEvent, EmailStatusResultEvent, FigureUpdateEvent, GetCommunication, GetUserTagsComposer, InClientLinkEvent, MysteryBoxKeysEvent, NoobnessLevelMessageEvent, PetRespectComposer, PetScratchFailedMessageEvent, RoomReadyMessageEvent, RoomUnitChatComposer, UserInfoEvent, UserNameChangeMessageEvent, UserPermissionsEvent, UserRespectComposer, UserTagsMessageEvent } from '@nitrots/communication';
|
||||
import { GetConfiguration } from '@nitrots/configuration';
|
||||
import { GetLocalizationManager } from '@nitrots/localization';
|
||||
import { GetEventDispatcher, MysteryBoxKeysUpdateEvent, NitroEvent, NitroEventType, NitroSettingsEvent, SessionDataPreferencesEvent, UserNameUpdateEvent } from '@nitrots/events';
|
||||
@@ -161,7 +161,6 @@ export class SessionDataManager implements ISessionDataManager
|
||||
})),
|
||||
GetCommunication().registerMessageEvent(new UserInfoEvent(this.onUserInfoEvent.bind(this))),
|
||||
GetCommunication().registerMessageEvent(new UserPermissionsEvent(this.onUserPermissionsEvent.bind(this))),
|
||||
GetCommunication().registerMessageEvent(new UserPermissionsMapEvent(this.onUserPermissionsMapEvent.bind(this))),
|
||||
GetCommunication().registerMessageEvent(new AvailabilityStatusMessageEvent(this.onAvailabilityStatusMessageEvent.bind(this))),
|
||||
GetCommunication().registerMessageEvent(new PetScratchFailedMessageEvent(this.onPetRespectFailed.bind(this))),
|
||||
GetCommunication().registerMessageEvent(new ChangeUserNameResultMessageEvent(this.onChangeNameUpdateEvent.bind(this))),
|
||||
@@ -293,18 +292,19 @@ export class SessionDataManager implements ISessionDataManager
|
||||
this._rankBadge = parser.rankBadge;
|
||||
this._rankPrefix = parser.rankPrefix;
|
||||
this._rankPrefixColor = parser.rankPrefixColor;
|
||||
// Copy into our local mutable Map so the parser's reference
|
||||
// (which is overwritten on every parse() call) can't leak back
|
||||
// to consumers.
|
||||
this._permissions = new Map(parser.permissions);
|
||||
|
||||
// Invalidate BOTH snapshots: a UserPermissionsComposer push from
|
||||
// the emulator refreshes user-data fields (clubLevel/securityLevel
|
||||
// /rank metadata) AND the resolved permission map. Keep the two
|
||||
// invalidation events distinct so React consumers can subscribe
|
||||
// to just one (e.g. a widget that only cares about
|
||||
// useHasPermission re-renders only when the map actually
|
||||
// changes, not on every snapshot bump).
|
||||
this.invalidateUserDataSnapshot();
|
||||
}
|
||||
|
||||
private onUserPermissionsMapEvent(event: UserPermissionsMapEvent): void
|
||||
{
|
||||
if(!event || !event.connection) return;
|
||||
|
||||
// Copy into our local mutable Map so the parser's reference (which
|
||||
// is overwritten on every parse() call) can't leak back to consumers.
|
||||
this._permissions = new Map(event.getParser().permissions);
|
||||
|
||||
this.invalidatePermissionsSnapshot();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user