From ededb340e55115ba39c4151808edfd18d946ff35 Mon Sep 17 00:00:00 2001 From: Adrian <78108584+AdrianCassar@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:52:57 +0000 Subject: [PATCH] Support contexts as properties --- .../commands/AddSessionContextCommand.ts | 2 +- src/domain/aggregates/Session.ts | 17 ++++- src/domain/value-objects/Property.ts | 30 ++++++++- .../mappers/SessionDomainMapper.ts | 14 +++++ .../controllers/session.controller.ts | 63 ++++++++++++++----- .../requests/GetSessionContextRequest.ts | 2 +- .../responses/SessionContextResponse.ts | 2 +- 7 files changed, 108 insertions(+), 22 deletions(-) diff --git a/src/application/commands/AddSessionContextCommand.ts b/src/application/commands/AddSessionContextCommand.ts index 36ec408..dfd65ef 100644 --- a/src/application/commands/AddSessionContextCommand.ts +++ b/src/application/commands/AddSessionContextCommand.ts @@ -5,6 +5,6 @@ export class AddSessionContextCommand { constructor( public readonly titleId: TitleId, public readonly sessionId: SessionId, - public readonly contexts: Map, + public readonly contexts: Array<{ contextId: number; value: number }>, ) {} } diff --git a/src/domain/aggregates/Session.ts b/src/domain/aggregates/Session.ts index 3967511..a487cce 100644 --- a/src/domain/aggregates/Session.ts +++ b/src/domain/aggregates/Session.ts @@ -57,7 +57,7 @@ interface CreateMigrationProps { } interface ContextProps { - context: Map; + context: Array<{ contextId: number; value: number }>; } interface PropertyProps { properties: Array; @@ -358,10 +358,23 @@ export default class Session { } get propertiesStringArray() { - const properties: Array = this.props.properties.map((prop) => { + let properties: Array = this.props.properties.map((prop) => { return prop.toString(); }); + const contexts: Array = Array.from(this.context).map( + ([id, value]) => { + const serialized_context: string = Property.SerializeContextToBase64( + Number(`0x${id}`), + value, + ); + + return serialized_context; + }, + ); + + properties = properties.concat(contexts); + return properties; } diff --git a/src/domain/value-objects/Property.ts b/src/domain/value-objects/Property.ts index 08f7ef1..6b5f6db 100644 --- a/src/domain/value-objects/Property.ts +++ b/src/domain/value-objects/Property.ts @@ -1,5 +1,17 @@ import { TinyTypeOf } from 'tiny-types'; +export enum X_USER_DATA_TYPE { + CONTEXT = 0, + INT32 = 1, + INT64 = 2, + DOUBLE = 3, + WSTRING = 4, + FLOAT = 5, + BINARY = 6, + DATETIME = 7, + UNSET = 0xff, +} + export default class Property extends TinyTypeOf() { buffer: Buffer; data: Buffer; @@ -7,7 +19,7 @@ export default class Property extends TinyTypeOf() { id_hex: string = ''; id: number = 0; size: number = 0; - type: number = 0; + type: X_USER_DATA_TYPE = 0; public constructor(base64: string) { super(base64); @@ -29,7 +41,7 @@ export default class Property extends TinyTypeOf() { } getUTF16() { - if (this.type != 4) { + if (this.type != X_USER_DATA_TYPE.WSTRING) { return ''; } @@ -66,4 +78,18 @@ export default class Property extends TinyTypeOf() { toStringPretty() { return `ID: ${this.getIDString()} Type: ${this.getType()} Size: ${this.getSize()}`; } + + static SerializeContextToBase64(id: number, value: number): string { + const buffer = Buffer.alloc(12); + + let offset: number = 0; + + buffer.writeInt32LE(id, offset); + offset += 4; + buffer.writeInt32LE(4, offset); + offset += 4; + buffer.writeInt32LE(value, offset); + + return buffer.toString('base64'); + } } diff --git a/src/infrastructure/persistance/mappers/SessionDomainMapper.ts b/src/infrastructure/persistance/mappers/SessionDomainMapper.ts index bd970eb..977cbbf 100644 --- a/src/infrastructure/persistance/mappers/SessionDomainMapper.ts +++ b/src/infrastructure/persistance/mappers/SessionDomainMapper.ts @@ -18,6 +18,20 @@ export default class SessionDomainMapper { return new Property(prop); }); + // Serialize contexts back into base64 + // const contexts: Array = Array.from(session.context).map( + // ([id, value]) => { + // const serialized_context: string = Property.SerializeContextToBase64( + // Number(`0x${id}`), + // value, + // ); + + // return new Property(serialized_context); + // }, + // ); + + // properties = properties.concat(contexts); + return new Session({ id: new SessionId(session.id), titleId: new TitleId(session.titleId), diff --git a/src/infrastructure/presentation/controllers/session.controller.ts b/src/infrastructure/presentation/controllers/session.controller.ts index a7c71dc..382d762 100644 --- a/src/infrastructure/presentation/controllers/session.controller.ts +++ b/src/infrastructure/presentation/controllers/session.controller.ts @@ -57,7 +57,7 @@ import { RealIP } from 'nestjs-real-ip'; import { ProcessClientAddressCommand } from 'src/application/commands/ProcessClientAddressCommand'; import Session from 'src/domain/aggregates/Session'; import { UpdatePlayerCommand } from 'src/application/commands/UpdatePlayerCommand'; -import Property from 'src/domain/value-objects/Property'; +import Property, { X_USER_DATA_TYPE } from 'src/domain/value-objects/Property'; @ApiTags('Sessions') @Controller('/title/:titleId/sessions') @@ -541,7 +541,7 @@ export class SessionController { @Param('sessionId') sessionId: string, @Body() request: GetSessionContextRequest, ) { - const session = await this.commandBus.execute( + const session: Session = await this.commandBus.execute( new AddSessionContextCommand( new TitleId(titleId), new SessionId(sessionId), @@ -561,7 +561,7 @@ export class SessionController { @Param('titleId') titleId: string, @Param('sessionId') sessionId: string, ): Promise { - const session = await this.queryBus.execute( + const session: Session = await this.queryBus.execute( new GetSessionQuery(new TitleId(titleId), new SessionId(sessionId)), ); @@ -582,13 +582,45 @@ export class SessionController { @Param('sessionId') sessionId: string, @Body() request: GetSessionPropertyRequest, ) { - const properties: Array = request.properties.map( - (base64: string) => { + // Extract properties and exclude contexts + const properties: Array = request.properties + .filter((base64: string) => { + const prop: Property = new Property(base64); + + return prop.type != X_USER_DATA_TYPE.CONTEXT; + }) + .map((base64: string) => { return new Property(base64); - }, + }); + + // Extract contexts from properties + const contexts: Array<{ contextId: number; value: number }> = + request.properties + .filter((base64: string) => { + const prop: Property = new Property(base64); + + return prop.type == X_USER_DATA_TYPE.CONTEXT; + }) + .map((base64: string) => { + const prop: Property = new Property(base64); + const value = prop.getData().readUInt32BE(); + + return { contextId: prop.id, value: value }; + }); + + const context_session: Session = await this.commandBus.execute( + new AddSessionContextCommand( + new TitleId(titleId), + new SessionId(sessionId), + contexts, + ), ); - const session: Session = await this.commandBus.execute( + if (!context_session) { + throw new NotFoundException(`Session ${sessionId} was not found.`); + } + + const properties_session: Session = await this.commandBus.execute( new AddSessionPropertyCommand( new TitleId(titleId), new SessionId(sessionId), @@ -596,16 +628,17 @@ export class SessionController { ), ); - this.logger.verbose( - `Host Gamer Name: ${session.propertyHostGamerName.getUTF16()}`, - ); + const HostGamerName: Property = properties_session.propertyHostGamerName; + const PUID: Property = properties_session.propertyPUID; - this.logger.verbose( - `Host PUID: ${session.propertyPUID.getData().readBigInt64BE().toString(16).padStart(16, '0').toUpperCase()}`, - ); + if (HostGamerName) { + this.logger.verbose(`Host Gamer Name: ${HostGamerName.getUTF16()}`); + } - if (!session) { - throw new NotFoundException(`Session ${sessionId} was not found.`); + if (PUID) { + this.logger.verbose( + `Host PUID: ${PUID.getData().readBigInt64BE().toString(16).padStart(16, '0').toUpperCase()}`, + ); } } diff --git a/src/infrastructure/presentation/requests/GetSessionContextRequest.ts b/src/infrastructure/presentation/requests/GetSessionContextRequest.ts index 84fffb3..dd22382 100644 --- a/src/infrastructure/presentation/requests/GetSessionContextRequest.ts +++ b/src/infrastructure/presentation/requests/GetSessionContextRequest.ts @@ -2,5 +2,5 @@ import { ApiProperty } from '@nestjs/swagger'; export class GetSessionContextRequest { @ApiProperty() - contexts: Map; + contexts: Array<{ contextId: number; value: number }>; } diff --git a/src/infrastructure/presentation/responses/SessionContextResponse.ts b/src/infrastructure/presentation/responses/SessionContextResponse.ts index 5011365..45dc4e6 100644 --- a/src/infrastructure/presentation/responses/SessionContextResponse.ts +++ b/src/infrastructure/presentation/responses/SessionContextResponse.ts @@ -1,3 +1,3 @@ export interface SessionContextResponse { - context: Map; + context: Map; }