Skip to content

Commit

Permalink
Support contexts as properties
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianCassar committed Feb 4, 2025
1 parent b5a8c1c commit beb57a3
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/application/commands/AddSessionContextCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export class AddSessionContextCommand {
constructor(
public readonly titleId: TitleId,
public readonly sessionId: SessionId,
public readonly contexts: Map<number, { contextId: number; value: number }>,
public readonly contexts: Array<{ contextId: number; value: number }>,
) {}
}
34 changes: 31 additions & 3 deletions src/domain/aggregates/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ interface CreateMigrationProps {
}

interface ContextProps {
context: Map<number, { contextId: number; value: number }>;
context: Array<{ contextId: number; value: number }>;
}
interface PropertyProps {
properties: Array<Property>;
Expand Down Expand Up @@ -159,7 +159,22 @@ export default class Session {

public addProperties(props: PropertyProps) {
props.properties.forEach((entry) => {
this.props.properties.push(entry);
const propIndex = this.props.properties.findIndex(
(prop) => prop.id == entry.id,
);

// Update property if it already exists during host migration
if (propIndex >= 0) {
const current_prop = this.props.properties[propIndex];

if (!entry.getData().equals(current_prop.getData())) {
this._logger.verbose(`Updated: ${entry.toStringPretty()}`);

this.props.properties[propIndex] = entry;
}
} else {
this.props.properties.push(entry);
}
});
}

Expand Down Expand Up @@ -358,10 +373,23 @@ export default class Session {
}

get propertiesStringArray() {
const properties: Array<string> = this.props.properties.map((prop) => {
let properties: Array<string> = this.props.properties.map((prop) => {
return prop.toString();
});

const contexts: Array<string> = 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;
}

Expand Down
32 changes: 29 additions & 3 deletions src/domain/value-objects/Property.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
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<string>() {
buffer: Buffer<ArrayBuffer>;
data: Buffer<ArrayBuffer>;

id_hex: string = '';
id: number = 0;
size: number = 0;
type: number = 0;
type: X_USER_DATA_TYPE = 0;

public constructor(base64: string) {
super(base64);
Expand All @@ -29,7 +41,7 @@ export default class Property extends TinyTypeOf<string>() {
}

getUTF16() {
if (this.type != 4) {
if (this.type != X_USER_DATA_TYPE.WSTRING) {
return '';
}

Expand Down Expand Up @@ -64,6 +76,20 @@ export default class Property extends TinyTypeOf<string>() {
}

toStringPretty() {
return `ID: ${this.getIDString()} Type: ${this.getType()} Size: ${this.getSize()}`;
return `${this.getType() == X_USER_DATA_TYPE.CONTEXT ? 'Context' : 'Property'} ID:\t0x${this.getIDString().toUpperCase().padStart(8, '0')} 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.writeInt32BE(value, offset);

return buffer.toString('base64');
}
}
14 changes: 14 additions & 0 deletions src/infrastructure/persistance/mappers/SessionDomainMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ export default class SessionDomainMapper {
return new Property(prop);
});

// Serialize contexts back into base64
// const contexts: Array<Property> = 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),
Expand Down
63 changes: 48 additions & 15 deletions src/infrastructure/presentation/controllers/session.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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),
Expand All @@ -561,7 +561,7 @@ export class SessionController {
@Param('titleId') titleId: string,
@Param('sessionId') sessionId: string,
): Promise<SessionContextResponse> {
const session = await this.queryBus.execute(
const session: Session = await this.queryBus.execute(
new GetSessionQuery(new TitleId(titleId), new SessionId(sessionId)),
);

Expand All @@ -582,30 +582,63 @@ export class SessionController {
@Param('sessionId') sessionId: string,
@Body() request: GetSessionPropertyRequest,
) {
const properties: Array<Property> = request.properties.map(
(base64: string) => {
// Extract properties and exclude contexts
const properties: Array<Property> = 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().readUint32LE();

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),
properties,
),
);

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()}`,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { ApiProperty } from '@nestjs/swagger';

export class GetSessionContextRequest {
@ApiProperty()
contexts: Map<number, { contextId: number; value: number }>;
contexts: Array<{ contextId: number; value: number }>;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export interface SessionContextResponse {
context: Map<number, number>;
context: Map<string, number>;
}

0 comments on commit beb57a3

Please sign in to comment.