Appearance
Client Engine Hooks
Client engine hooks allow you to listen to client-side events and customize client behavior. These hooks are defined in the engine property of your client module.
Usage
ts
import { RpgClientEngine, RpgClientEngineHooks, defineModule } from '@rpgjs/client'
const engine: RpgClientEngineHooks = {
onStart(engine: RpgClientEngine) {
console.log('Client engine started')
return true // Allow connection to server
},
onStep(engine: RpgClientEngine, t: number, dt: number) {
// Called each frame
}
}
export default defineModule({
engine
})Available Hooks
onStart
Description: Called when the client engine starts. Return false to prevent connection to the server.
Parameters:
engine: RpgClientEngine- The client engine instance
Returns:
boolean | void- Returnfalseto prevent server connection
Example:
ts
const engine: RpgClientEngineHooks = {
async onStart(engine: RpgClientEngine) {
console.log('🎮 Client starting...')
// Load global configuration
try {
const response = await fetch('/api/game-config')
const config = await response.json()
engine.globalConfig = config
console.log('✅ Configuration loaded')
return true // Allow connection
} catch (error) {
console.error('❌ Failed to load configuration:', error)
return false // Prevent connection
}
}
}onStep
Description: Called at each frame (typically 60 FPS)
Parameters:
engine: RpgClientEngine- The client engine instancet: number- Current timestampdt: number- Delta time since last frame
Example:
ts
const engine: RpgClientEngineHooks = {
onStep(engine: RpgClientEngine, t: number, dt: number) {
// Update custom animations
if (engine.customAnimations) {
engine.customAnimations.forEach(animation => {
animation.update(dt)
})
}
// Update UI elements
if (engine.ui) {
engine.ui.updateTimers(dt)
}
// Performance monitoring
if (t % 1000 < dt) { // Every second
const fps = Math.round(1000 / dt)
if (fps < 30) {
console.warn(`Low FPS detected: ${fps}`)
}
}
}
}onInput
Description: Called when keyboard input is detected
Parameters:
engine: RpgClientEngine- The client engine instanceobj: { input: string, playerId: number }- Input data
Example:
ts
const engine: RpgClientEngineHooks = {
onInput(engine: RpgClientEngine, { input, playerId }) {
console.log(`Input received: ${input} from player ${playerId}`)
// Handle custom key bindings
switch (input) {
case 'F1':
engine.gui('help').toggle()
break
case 'F2':
engine.gui('debug').toggle()
break
case 'F11':
engine.toggleFullscreen()
break
case 'screenshot':
engine.takeScreenshot()
break
case 'chat':
engine.gui('chat').focus()
break
}
// Log input for analytics
if (engine.analytics) {
engine.analytics.logInput(input, playerId)
}
}
}onConnected
Description: Called when the client successfully connects to the server
Parameters:
engine: RpgClientEngine- The client engine instancesocket: any- The socket connection instance
Example:
ts
const engine: RpgClientEngineHooks = {
onConnected(engine: RpgClientEngine, socket: any) {
console.log('🔗 Connected to server')
// Show connection status
engine.gui('notification').show({
message: 'Connected to server',
type: 'success',
duration: 3000
})
// Initialize client-side systems
engine.initializeChat()
engine.initializeAudio()
engine.initializeParticles()
// Send client information
socket.emit('client-info', {
version: engine.version,
platform: navigator.platform,
userAgent: navigator.userAgent,
screenResolution: {
width: screen.width,
height: screen.height
}
})
// Start heartbeat
engine.startHeartbeat(socket)
}
}onDisconnect
Description: Called when the client disconnects from the server
Parameters:
engine: RpgClientEngine- The client engine instancereason: any- Disconnection reasonsocket: any- The socket connection instance
Example:
ts
const engine: RpgClientEngineHooks = {
onDisconnect(engine: RpgClientEngine, reason: any, socket: any) {
console.log('🔌 Disconnected from server:', reason)
// Show disconnection message
engine.gui('notification').show({
message: `Disconnected: ${reason}`,
type: 'error',
duration: 5000
})
// Stop client-side systems
engine.stopHeartbeat()
engine.pauseAudio()
engine.clearParticles()
// Show reconnection dialog
engine.gui('reconnect-dialog').open({
reason: reason,
onReconnect: () => {
engine.reconnect()
}
})
// Save offline data
engine.saveOfflineData()
}
}onConnectError
Description: Called when there's an error connecting to the server
Parameters:
engine: RpgClientEngine- The client engine instanceerr: any- The connection errorsocket: any- The socket connection instance
Example:
ts
const engine: RpgClientEngineHooks = {
onConnectError(engine: RpgClientEngine, err: any, socket: any) {
console.error('❌ Connection error:', err)
// Show error message
engine.gui('error-dialog').open({
title: 'Connection Failed',
message: 'Unable to connect to the game server. Please check your internet connection and try again.',
error: err.message,
actions: [
{
label: 'Retry',
action: () => engine.reconnect()
},
{
label: 'Offline Mode',
action: () => engine.startOfflineMode()
}
]
})
// Log error for debugging
if (engine.errorLogger) {
engine.errorLogger.log({
type: 'connection_error',
error: err,
timestamp: Date.now(),
userAgent: navigator.userAgent
})
}
}
}onWindowResize
Description: Called when the browser window is resized
Example:
ts
const engine: RpgClientEngineHooks = {
onWindowResize() {
console.log('🖥️ Window resized')
// Update viewport
const newWidth = window.innerWidth
const newHeight = window.innerHeight
// Adjust game canvas
if (engine.renderer) {
engine.renderer.resize(newWidth, newHeight)
}
// Update UI layout
if (engine.ui) {
engine.ui.updateLayout(newWidth, newHeight)
}
// Adjust camera
if (engine.camera) {
engine.camera.updateViewport(newWidth, newHeight)
}
// Save new dimensions
localStorage.setItem('gameWindowSize', JSON.stringify({
width: newWidth,
height: newHeight
}))
}
}Complete Example
ts
import { RpgClientEngine, RpgClientEngineHooks, defineModule } from '@rpgjs/client'
const engine: RpgClientEngineHooks = {
async onStart(engine: RpgClientEngine) {
console.log('🎮 Starting RPG Client...')
// Load game configuration
try {
const slug = 'my-game-project'
const response = await fetch(`/api/game/project/${slug}`)
const config = await response.json()
engine.globalConfig = config
engine.gameVersion = config.version
// Initialize client systems
engine.analytics = new GameAnalytics(config.analyticsKey)
engine.errorLogger = new ErrorLogger(config.errorReportingUrl)
console.log('✅ Client initialized successfully')
return true
} catch (error) {
console.error('❌ Failed to initialize client:', error)
return false
}
},
onStep(engine: RpgClientEngine, t: number, dt: number) {
// Update performance metrics
engine.performanceMonitor?.update(dt)
// Update custom systems
engine.particleSystem?.update(dt)
engine.audioManager?.update(dt)
// Auto-save progress every 30 seconds
if (t % 30000 < dt) {
engine.autoSave()
}
},
onInput(engine: RpgClientEngine, { input, playerId }) {
// Handle debug commands
if (engine.debugMode) {
switch (input) {
case 'F3':
engine.gui('debug-info').toggle()
break
case 'F4':
engine.toggleWireframe()
break
}
}
// Handle screenshot
if (input === 'F12') {
engine.takeScreenshot()
}
},
onConnected(engine: RpgClientEngine, socket: any) {
console.log('🌐 Connected to game server')
// Initialize multiplayer features
engine.chat.enable()
engine.voiceChat.initialize()
// Send client capabilities
socket.emit('client-capabilities', {
webGL: engine.renderer.isWebGL,
audioContext: !!window.AudioContext,
gamepadSupport: !!navigator.getGamepads,
touchSupport: 'ontouchstart' in window
})
engine.gui('loading').hide()
engine.gui('hud').show()
},
onDisconnect(engine: RpgClientEngine, reason: any, socket: any) {
console.log('🔌 Disconnected:', reason)
// Disable multiplayer features
engine.chat.disable()
engine.voiceChat.disconnect()
// Show appropriate UI
engine.gui('hud').hide()
engine.gui('disconnected').show({ reason })
},
onConnectError(engine: RpgClientEngine, err: any, socket: any) {
console.error('Connection failed:', err)
engine.gui('connection-error').show({
error: err.message,
onRetry: () => engine.reconnect(),
onOffline: () => engine.startOfflineMode()
})
},
onWindowResize() {
// Responsive design adjustments
const width = window.innerWidth
const height = window.innerHeight
engine.renderer.resize(width, height)
engine.ui.adjustLayout(width, height)
// Mobile optimizations
if (width < 768) {
engine.ui.enableMobileMode()
} else {
engine.ui.disableMobileMode()
}
}
}
export default defineModule({
engine
})