🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@playertwo/devtools

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@playertwo/devtools

Development tools for playertwo - state inspector, action history, network monitor

latest
Source
npmnpm
Version
0.2.0
Version published
Weekly downloads
2
Maintainers
1
Weekly downloads
 
Created
Source

@playertwo/devtools

Development tools for playertwo multiplayer SDK - debug your multiplayer games with ease.

Features

  • Real-time State Inspection - Diff-based snapshots with automatic throttling
  • Action History Tracking - Aggregated timelines that avoid spammy actions
  • Statistics & Metrics - Track action frequency and state changes
  • Event Listeners - React to state changes and actions programmatically
  • Memory-Efficient - Configurable limits for history size

Installation

pnpm add -D @playertwo/devtools

Quick Start

import { StateInspector } from '@playertwo/devtools';
import { GameRuntime } from '@playertwo/core';

// Create your game runtime
const runtime = new GameRuntime(myGame, transport, config);

// Attach the inspector
const inspector = new StateInspector();
inspector.attach(runtime);

// View state snapshots
console.log(inspector.getSnapshots());

// View action history
console.log(inspector.getActionHistory());

// View statistics
console.log(inspector.getStats());

// Listen for changes
inspector.onStateChange((snapshot) => {
  console.log('State changed:', snapshot.state);
});

inspector.onAction((action) => {
  console.log('Action submitted:', action.actionName, action.input);
});

// Clean up when done
inspector.detach();

API Reference

Constructor

new StateInspector(options?: StateInspectorOptions)

Options:

  • maxSnapshots (default: 100) - Maximum number of state snapshots to keep
  • maxActions (default: 1000) - Maximum number of actions to keep in history
  • snapshotIntervalMs (default: 250) - Minimum time between automatic snapshots
  • actionAggregationWindowMs (default: 200) - Time window for grouping identical actions
  • ignoreActions (default: []) - Array of action names to drop entirely (e.g. ['tick'])

Methods

attach(runtime: GameRuntime): void

Attach the inspector to a GameRuntime instance. Only one runtime can be attached at a time.

inspector.attach(runtime);

detach(): void

Detach the inspector from the current runtime and stop tracking.

inspector.detach();

isAttached(): boolean

Check if the inspector is currently attached to a runtime.

if (inspector.isAttached()) {
  console.log('Inspector is active');
}

getRuntime(): GameRuntime | null

Get the currently attached runtime, or null if not attached.

const runtime = inspector.getRuntime();

getSnapshots(): StateSnapshot[]

Get all captured state snapshots.

const snapshots = inspector.getSnapshots();
snapshots.forEach(snapshot => {
  console.log(snapshot.timestamp, snapshot.state);
});

Returns an array of:

interface StateSnapshot {
  id: number;           // Incrementing identifier
  timestamp: number;    // Unix timestamp in milliseconds
  state?: any;          // Deep clone of game state (only stored for baseline snapshots)
  diff?: Patch[];       // Diffs relative to previous snapshot
  lastActionId?: number;// Action id responsible for this snapshot
}

getActionHistory(): ActionRecord[]

Get all tracked actions.

const history = inspector.getActionHistory();
history.forEach(action => {
  console.log(action.actionName, action.input);
});

Returns an array of:

interface ActionRecord {
  id: number;              // Incrementing identifier
  timestamp: number;      // Unix timestamp in milliseconds
  actionName: string;     // Name of the action
  input: any;            // Action payload
  playerId?: string;     // Who submitted the action
  targetId?: string;     // Who was targeted
  count?: number;         // Number of aggregated occurrences
  duration?: number;      // Duration of aggregated burst in ms
  snapshotId?: number;    // Linked snapshot id
  excludedActionsTotal?: number; // Total ignored actions so far
}

getStats(): InspectorStats

Get statistics about tracked actions and state changes.

const stats = inspector.getStats();
console.log(`Total actions: ${stats.totalActions}`);
console.log(`Total state changes: ${stats.totalStateChanges}`);
console.log(`Increment action called ${stats.actionsByName.increment} times`);

Returns:

interface InspectorStats {
  totalActions: number;                   // Total actions submitted
  totalStateChanges: number;             // Total state updates
  actionsByName: Record<string, number>; // Action frequency map
  excludedActions: number;               // Actions filtered via ignoreActions
}

onStateChange(listener: (snapshot: StateSnapshot) => void): () => void

Listen for state changes. Returns an unsubscribe function.

const unsubscribe = inspector.onStateChange((snapshot) => {
  console.log('New state:', snapshot.state);
});

// Later: stop listening
unsubscribe();

onAction(listener: (action: ActionRecord) => void): () => void

Listen for actions. Returns an unsubscribe function.

const unsubscribe = inspector.onAction((action) => {
  console.log(`${action.actionName} submitted by ${action.playerId}`);
});

// Later: stop listening
unsubscribe();

clear(): void

Clear all snapshots, action history, and statistics.

inspector.clear();

Use Cases

Debug State Issues

const inspector = new StateInspector();
inspector.attach(runtime);

// Submit some actions
runtime.submitAction('move', { x: 100, y: 200 });
runtime.submitAction('attack', { targetId: 'enemy1' });

// View state evolution
const snapshots = inspector.getSnapshots();
console.log('Initial state:', snapshots[0].state);
console.log('Final state:', snapshots[snapshots.length - 1].state);

Track Performance

const inspector = new StateInspector();
inspector.attach(runtime);

let actionCount = 0;
inspector.onAction(() => {
  actionCount++;
  if (actionCount % 100 === 0) {
    const stats = inspector.getStats();
    console.log(`Actions per second: ${actionCount / ((Date.now() - startTime) / 1000)}`);
  }
});

Build Custom DevTools UI

const inspector = new StateInspector({ maxSnapshots: 50 });
inspector.attach(runtime);

// React component example
function DevToolsPanel() {
  const [snapshots, setSnapshots] = useState([]);
  const [actions, setActions] = useState([]);

  useEffect(() => {
    const unsubState = inspector.onStateChange((snapshot) => {
      setSnapshots(prev => [...prev, snapshot]);
    });

    const unsubAction = inspector.onAction((action) => {
      setActions(prev => [...prev, action]);
    });

    return () => {
      unsubState();
      unsubAction();
    };
  }, []);

  return (
    <div>
      <StateViewer snapshots={snapshots} />
      <ActionHistory actions={actions} />
      <Statistics stats={inspector.getStats()} />
    </div>
  );
}

Monitor Multiplayer Sync

const inspector = new StateInspector();
inspector.attach(runtime);

inspector.onAction((action) => {
  console.log(`[${action.timestamp}] ${action.playerId} -> ${action.actionName}`);
});

inspector.onStateChange((snapshot) => {
  // Check for state anomalies
  if (snapshot.state.players[myId]?.health < 0) {
    console.warn('Invalid state detected:', snapshot);
  }
});

Testing with StateInspector

import { describe, it, expect } from 'vitest';
import { StateInspector } from '@playertwo/devtools';

describe('MyGame', () => {
  it('should increment counter on action', () => {
    const inspector = new StateInspector();
    inspector.attach(runtime);

    const initialState = inspector.getSnapshots()[0].state;
    expect(initialState.count).toBe(0);

    runtime.submitAction('increment', {});

    const finalSnapshots = inspector.getSnapshots();
    const finalState = finalSnapshots[finalSnapshots.length - 1].state;
    expect(finalState.count).toBe(1);

    inspector.detach();
  });
});

Best Practices

  • Use in Development Only - Don't ship StateInspector to production (it's a devDependency)
  • Set Limits - Configure maxSnapshots and maxActions to prevent memory issues
  • Detach When Done - Always call detach() to clean up listeners
  • Deep Clone Awareness - Snapshots are deep clones, so large states may impact performance

Next Steps

  • See playertwo SDK Documentation
  • Check out the demos for working examples
  • Read about Best Practices

License

MIT

Keywords

devtools

FAQs

Package last updated on 31 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts