Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
## OpenCore Framework v1.0.8
## OpenCore Framework v1.0.10

### Added
- Added framework event bridging from `CORE` to `RESOURCE` for `@OnFrameworkEvent` listeners.

### Changed
- Updated `@OnRuntimeEvent` and `@OnFrameworkEvent` documentation to clarify handler arguments and cross-context behavior.
- Added authoritative CORE exports to link and unlink player accounts from remote resources.
- Added debug logs for remote player session mutations delegated from `RESOURCE` to `CORE`.

### Fixed
- Fixed `@OnFrameworkEvent` delivery so built-in framework lifecycle events can reach `RESOURCE` listeners with hydrated payloads.
- Fixed `RESOURCE` player session mutations so `player.linkAccount()`, `player.unlinkAccount()`, `player.setMeta()` and state changes propagate to CORE.
- Fixed secure `@OnNet` and `@OnRPC` handlers being blocked in sibling resources after successful authentication performed from a `RESOURCE` auth module.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@open-core/framework",
"version": "1.0.9",
"version": "1.0.10",
"description": "Secure, event-driven TypeScript Framework & Runtime engine for CitizenFX (Cfx).",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
26 changes: 26 additions & 0 deletions src/runtime/server/controllers/player-export.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { inject } from 'tsyringe'
import { loggers } from '../../../kernel/logger'
import { Controller } from '../decorators/controller'
import { Export } from '../decorators/export'
import { serializeServerPlayerData } from '../adapter/serialization'
Expand Down Expand Up @@ -77,6 +78,31 @@ export class PlayerExportController implements InternalPlayerExports {
this.playerService.setMeta(clientID, key, value)
}

@Export()
linkPlayerAccount(clientID: number, accountID: string): void {
const player = this.playerService.getByClient(clientID)
if (!player) return

player.linkAccount(accountID)
loggers.session.debug('Remote player account linked in CORE', {
clientID,
accountID,
})
}

@Export()
unlinkPlayerAccount(clientID: number): void {
const player = this.playerService.getByClient(clientID)
if (!player) return

const previousAccountID = player.accountID
player.unlinkAccount()
loggers.session.debug('Remote player account unlinked in CORE', {
clientID,
accountID: previousAccountID,
})
}

// ═══════════════════════════════════════════════════════════════
// State Management
// ═══════════════════════════════════════════════════════════════
Expand Down
75 changes: 74 additions & 1 deletion src/runtime/server/implementations/remote/player.remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,80 @@ export class RemotePlayerImplementation extends Players {
}

private createPlayerFromData(data: SerializedPlayerData): Player {
return createRemoteServerPlayer(data, this.playerAdapters)
const player = createRemoteServerPlayer(data, this.playerAdapters)
this.attachAuthoritativeMutators(player)
return player
}

/**
* Proxies remote session mutations to CORE so security-critical data remains authoritative.
*/
private attachAuthoritativeMutators(player: Player): void {
const core = this.core
const originalSetMeta = player.setMeta.bind(player)
const originalLinkAccount = player.linkAccount.bind(player)
const originalUnlinkAccount = player.unlinkAccount.bind(player)
const originalAddState = player.addState.bind(player)
const originalRemoveState = player.removeState.bind(player)
const originalToggleState = player.toggleState.bind(player)

player.setMeta = <T = unknown>(key: string, value: T): void => {
core.setPlayerMeta(player.clientID, key, value)
originalSetMeta(key, value)
loggers.session.debug('Remote player meta delegated to CORE', {
clientID: player.clientID,
key,
})
}

player.linkAccount = (accountID): void => {
core.linkPlayerAccount(player.clientID, accountID.toString())
originalLinkAccount(accountID)
loggers.session.debug('Remote player linkAccount delegated to CORE', {
clientID: player.clientID,
accountID: accountID.toString(),
})
}

player.unlinkAccount = (): void => {
const previousAccountID = player.accountID
core.unlinkPlayerAccount(player.clientID)
originalUnlinkAccount()
loggers.session.debug('Remote player unlinkAccount delegated to CORE', {
clientID: player.clientID,
accountID: previousAccountID,
})
}

player.addState = (state: string): void => {
core.addPlayerState(player.clientID, state)
originalAddState(state)
loggers.session.debug('Remote player state added in CORE', {
clientID: player.clientID,
state,
})
}

player.removeState = (state: string): void => {
core.removePlayerState(player.clientID, state)
originalRemoveState(state)
loggers.session.debug('Remote player state removed in CORE', {
clientID: player.clientID,
state,
})
}

player.toggleState = (state: string, force?: boolean): boolean => {
const next = force ?? !player.hasState(state)
if (next) {
player.addState(state)
} else {
player.removeState(state)
}

originalToggleState(state, next)
return next
}
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/runtime/server/types/core-exports.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,21 @@ export interface InternalPlayerExports {
*/
setPlayerMeta(clientID: number, key: string, value: unknown): void

/**
* Links a persistent account to the authoritative CORE player session.
*
* @param clientID - FiveM client/server ID
* @param accountID - Persistent account identifier
*/
linkPlayerAccount(clientID: number, accountID: string): void

/**
* Removes any linked account from the authoritative CORE player session.
*
* @param clientID - FiveM client/server ID
*/
unlinkPlayerAccount(clientID: number): void

/**
* Gets complete serialized player data.
*
Expand Down
Loading