### Install and Run netplayjs-server using NPM Source: https://github.com/rameshvarun/netplayjs/blob/master/netplayjs-server/README.md These commands demonstrate how to install the netplayjs-server globally using NPM and then start the server. This method is suitable if you have Node.js and NPM installed. ```bash npm install -g netplayjs-server netplayjs-server ``` -------------------------------- ### Install NetplayJS via npm Source: https://github.com/rameshvarun/netplayjs/blob/master/README.md Install NetplayJS from npm for use in larger projects with module bundlers like Webpack. This is the recommended approach for projects beyond simple script tag inclusion. ```bash npm install --save netplayjs ``` -------------------------------- ### NetplayJS Server - Dockerfile Source: https://context7.com/rameshvarun/netplayjs/llms.txt A Dockerfile to containerize the NetplayJS matchmaking server. It sets up a Node.js environment, installs dependencies, builds the application, and exposes the necessary port. Assumes Node.js 18 and Alpine Linux. ```dockerfile # Dockerfile for hosting NetplayJS server FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npm run build EXPOSE 3000 USER node CMD ["node", "dist/index.js"] ``` -------------------------------- ### Run netplayjs-server using Docker Source: https://github.com/rameshvarun/netplayjs/blob/master/netplayjs-server/README.md This command allows you to run the netplayjs-server as a Docker container. It publishes the server to port 80 externally, forwarding to port 3000 internally. Ensure you have Docker installed. ```bash docker run --publish 80:3000 varunramesh/netplayjs-server:0.0.9 ``` -------------------------------- ### Start Game with Lockstep Netcode using LockstepWrapper Source: https://context7.com/rameshvarun/netplayjs/llms.txt Initiates a game using LockstepWrapper, suitable for games with non-serializable or rewind-prohibitive state that require frame-perfect synchronization. It defines a game with physics, handles player inputs to update physics bodies, and renders the game state. Dependencies include 'netplayjs' and a physics engine. ```typescript import { LockstepWrapper, Game, NetplayPlayer, DefaultInput } from 'netplayjs'; class PhysicsGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 800, height: 600 }; static deterministic = true; physicsWorld: any; playerObjects: Map = new Map(); constructor(canvas: HTMLCanvasElement, players: Array) { super(); this.physicsWorld = this.createPhysicsWorld(); for (const player of players) { this.playerObjects.set(player.getID(), this.createPlayerBody()); } } tick(playerInputs: Map): void { for (const [player, input] of playerInputs.entries()) { const body = this.playerObjects.get(player.getID()); const vel = input.wasd(); this.applyForce(body, vel.x * 100, vel.y * 100); } this.physicsWorld.step(PhysicsGame.timestep / 1000); } draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); for (const [id, body] of this.playerObjects) { const pos = this.getBodyPosition(body); ctx.fillStyle = id === 0 ? "red" : "blue"; ctx.fillRect(pos.x - 10, pos.y - 10, 20, 20); } } createPhysicsWorld(): any { /* ... */ } createPlayerBody(): any { /* ... */ } applyForce(body: any, x: number, y: number): void { /* ... */ } getBodyPosition(body: any): {x: number, y: number} { /* ... */ } } new LockstepWrapper(PhysicsGame).start(); ``` -------------------------------- ### Local Testing with Multiple Players using LocalWrapper Source: https://context7.com/rameshvarun/netplayjs/llms.txt Enables local multiplayer testing by running multiple game instances within the same browser window using LocalWrapper. This example demonstrates a simple game where players move objects using arrow keys. It initializes player positions and updates them based on input, then renders the objects. Dependencies include 'netplayjs'. ```typescript import { LocalWrapper, Game, NetplayPlayer, DefaultInput } from 'netplayjs'; class TestGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 400, height: 300 }; static numPlayers = 2; positions: Map = new Map(); constructor(canvas: HTMLCanvasElement, players: Array) { super(); players.forEach((player, i) => { this.positions.set(player.getID(), { x: 100 + i * 200, y: 150 }); }); } tick(playerInputs: Map): void { for (const [player, input] of playerInputs.entries()) { const pos = this.positions.get(player.getID())!; const vel = input.arrowKeys(); pos.x += vel.x * 3; pos.y -= vel.y * 3; } } draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); const colors = ["red", "blue", "green", "yellow"]; for (const [id, pos] of this.positions) { ctx.fillStyle = colors[id]; ctx.fillRect(pos.x - 8, pos.y - 8, 16, 16); } } } new LocalWrapper(TestGame).start(); ``` -------------------------------- ### Handle Diverse Player Inputs with NetplayJS DefaultInput Source: https://context7.com/rameshvarun/netplayjs/llms.txt This snippet demonstrates how to utilize the DefaultInput class in NetplayJS to process inputs from keyboards, mice, touchscreens, and gamepads. It covers individual key presses, mouse coordinates, touch points, and gamepad axes/buttons, providing flexibility for game development. Ensure NetplayJS is installed as a dependency. ```typescript import { DefaultInput, NetplayPlayer, Game } from 'netplayjs'; class InputDemoGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 800, height: 600 }; // Enable pointer lock for FPS-style mouse control static pointerLock = true; // Prevent right-click context menu static preventContextMenu = true; playerStates: Map = new Map(); tick(playerInputs: Map): void { for (const [player, input] of playerInputs.entries()) { const state = this.playerStates.get(player.getID()) || {}; // Keyboard: arrow keys as vector const arrowVel = input.arrowKeys(); // Returns Vec2 with x/y values // Keyboard: WASD keys as vector const wasdVel = input.wasd(); // Returns Vec2 with x/y values // Keyboard: individual key states if (input.keysPressed['Space']) { state.jumping = true; // Key pressed this frame } if (input.keysHeld['Shift']) { state.sprinting = true; // Key held down } if (input.keysReleased['e']) { state.interact = false; // Key released this frame } // Mouse: position and movement if (input.mousePosition) { state.cursorX = input.mousePosition.x; state.cursorY = input.mousePosition.y; } // Touch: multiple touch points if (input.touches.length > 0) { state.touchX = input.touches[0].x; state.touchY = input.touches[0].y; } // Gamepad: axes and buttons if (input.gamepads.length > 0) { const gamepad = input.gamepads[0]; // Analog stick axes const leftStickX = gamepad.axes[0]; const leftStickY = gamepad.axes[1]; // Button states if (gamepad.buttons[0].pressed) { state.attackStarted = true; } if (gamepad.buttons[0].held) { state.attacking = true; } if (gamepad.buttons[0].released) { state.attackEnded = true; } } this.playerStates.set(player.getID(), state); } } draw(canvas: HTMLCanvasElement) { // Render game state } } new RollbackWrapper(InputDemoGame).start(); ``` -------------------------------- ### Game Class Implementation in TypeScript Source: https://context7.com/rameshvarun/netplayjs/llms.txt Define a multiplayer game by extending the `Game` class, configuring timestep, canvas size, and implementing `tick()` for game logic updates and `draw()` for rendering. This example demonstrates basic player movement and rendering. ```typescript import { Game, NetplayPlayer, DefaultInput, Vec2 } from 'netplayjs'; class SimpleGame extends Game { // Configure fixed timestep at 60 FPS static timestep = 1000 / 60; // Configure fixed canvas dimensions static canvasSize = { width: 600, height: 300 }; // Optional: mark game as deterministic for efficiency static deterministic = true; // Game state variables aPos: Vec2 = new Vec2(100, 150); bPos: Vec2 = new Vec2(500, 150); // Update game state given player inputs tick(playerInputs: Map): void { for (const [player, input] of playerInputs.entries()) { // Read arrow keys as velocity vector const vel = input.arrowKeys().multiplyScalar(5); // Update position based on player ID if (player.getID() == 0) { this.aPos.x += vel.x; this.aPos.y -= vel.y; } else if (player.getID() == 1) { this.bPos.x += vel.x; this.bPos.y -= vel.y; } } } // Render current game state to canvas draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; // Clear background ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw player squares ctx.fillStyle = "red"; ctx.fillRect(this.aPos.x - 5, this.aPos.y - 5, 10, 10); ctx.fillStyle = "blue"; ctx.fillRect(this.bPos.x - 5, this.bPos.y - 5, 10, 10); } } ``` -------------------------------- ### Rollback Wrapper for Low-Latency Multiplayer in TypeScript Source: https://context7.com/rameshvarun/netplayjs/llms.txt Utilize `RollbackWrapper` to implement client-side prediction and state rewind for low-latency multiplayer games. The `PongGame` example demonstrates state serialization and basic physics. Games using rollback must have serializable state. ```typescript import { RollbackWrapper } from 'netplayjs'; // Rollback netcode requires serializable game state class PongGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 600, height: 300 }; static highDPI = true; leftPaddle: number = 150; rightPaddle: number = 150; ballPosition: [number, number] = [295, 145]; ballVelocity: [number, number] = [300, 0]; leftScore: number = 0; rightScore: number = 0; tick(playerInputs: Map): void { const dt = PongGame.timestep / 1000; // Update paddles from player inputs for (const [player, input] of playerInputs.entries()) { const direction = (input.keysHeld["ArrowDown"] ? 1 : 0) + (input.keysHeld["ArrowUp"] ? -1 : 0); if (player.getID() == 0) { this.leftPaddle += direction * 300 * dt; } else if (player.getID() == 1) { this.rightPaddle += direction * 300 * dt; } } // Update ball physics and collision detection this.ballPosition[0] += this.ballVelocity[0] * dt; this.ballPosition[1] += this.ballVelocity[1] * dt; } draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; ctx.resetTransform(); ctx.scale(window.devicePixelRatio, window.devicePixelRatio); ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw paddles and ball ctx.fillStyle = "white"; ctx.fillRect(100, this.leftPaddle, 10, 100); ctx.fillRect(490, this.rightPaddle, 10, 100); ctx.fillRect(this.ballPosition[0], this.ballPosition[1], 10, 10); } } // Start game with rollback netcode new RollbackWrapper(PongGame).start(); ``` -------------------------------- ### Implement Custom Serialization for Complex Game States in NetplayJS Source: https://context7.com/rameshvarun/netplayjs/llms.txt This example shows how to define custom serialize and deserialize methods for complex game states in NetplayJS, particularly when the default autoserializer cannot handle nested structures like Maps. It ensures that complex data is correctly saved and restored across game sessions. Requires 'type-fest' for JsonValue. ```typescript import { Game, NetplayPlayer, DefaultInput } from 'netplayjs'; import { JsonValue } from 'type-fest'; class ComplexGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 600, height: 400 }; // Complex nested state entities: Array<{ id: number; type: string; position: { x: number; y: number }; velocity: { x: number; y: number }; data: Map; }> = []; frame: number = 0; tick(playerInputs: Map): void { this.frame++; // Update entities based on inputs for (const entity of this.entities) { entity.position.x += entity.velocity.x; entity.position.y += entity.velocity.y; } } // Custom serialization to handle Maps and complex structures serialize(): JsonValue { return { frame: this.frame, entities: this.entities.map(e => ({ id: e.id, type: e.type, position: e.position, velocity: e.velocity, data: Array.from(e.data.entries()) // Convert Map to array })) }; } // Custom deserialization to reconstruct state deserialize(value: JsonValue): void { const state = value as any; this.frame = state.frame; this.entities = state.entities.map(e => ({ id: e.id, type: e.type, position: e.position, velocity: e.velocity, data: new Map(e.data) // Reconstruct Map from array })); } draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); for (const entity of this.entities) { ctx.fillStyle = "white"; ctx.fillRect(entity.position.x, entity.position.y, 10, 10); } } } new RollbackWrapper(ComplexGame).start(); ``` -------------------------------- ### NetplayJS Server - Deployment Scripts Source: https://context7.com/rameshvarun/netplayjs/llms.txt Commands for deploying a NetplayJS matchmaking server using Docker or npm. Includes building the Docker image, running a container, and installing/running the server globally via npm. ```bash # Deploy with Docker docker build -t netplayjs-server . docker run -p 3000:3000 -e PORT=3000 netplayjs-server # Or install globally and run npm install -g netplayjs-server netplayjs-server ``` -------------------------------- ### NetplayJS Server - Host Matchmaking Server Source: https://context7.com/rameshvarun/netplayjs/llms.txt Sets up and runs a NetplayJS matchmaking server. This code is intended for Node.js environments and requires the 'netplayjs-server' package. It listens for incoming connections and manages game requests. ```typescript import { Server } from 'netplayjs-server'; // Create and start server const server = new Server(); server.start().then(() => { console.log('NetplayJS matchmaking server started'); console.log('Listening on port:', process.env.PORT || 3000); }); // Graceful shutdown process.on('SIGTERM', async () => { console.log('Shutting down server...'); await server.close(); process.exit(0); }); ``` -------------------------------- ### NetplayJS Client - Connect to Multiplayer Servers Source: https://context7.com/rameshvarun/netplayjs/llms.txt Connects a NetplayJS client to a matchmaking server to find opponents and establish peer-to-peer connections. It handles registration, match requests, and managing host/client roles. Dependencies include the 'netplayjs' library. ```typescript import { MatchmakingClient, DEFAULT_SERVER_URL } from 'netplayjs'; // Connect to default NetplayJS server const client = new MatchmakingClient(DEFAULT_SERVER_URL); // Or connect to custom server const customClient = new MatchmakingClient('https://your-server.com'); // Wait for successful registration client.onRegistered.once((clientID) => { console.log(`Registered with ID: ${clientID}`); console.log(`ICE Servers:`, client.iceServers); // Request matchmaking for a specific game client.sendMatchRequest( 'my-game-id', // Unique game identifier 2, // Minimum players 2 // Maximum players ); }); // Handle being assigned as host client.onHostMatch.once(({ clientIDs }) => { console.log(`Hosting match for clients:`, clientIDs); // Establish peer connections to each client for (const clientID of clientIDs) { const conn = client.connectPeer(clientID); conn.onOpen(() => { console.log(`Connected to client ${clientID}`); // Start game as host // wrapper.startHost(players, conn); }); conn.on('data', (data) => { console.log(`Received from ${clientID}:`, data); }); } }); // Handle being assigned as client client.onJoinMatch.once(({ hostID }) => { console.log(`Joining match hosted by ${hostID}`); const conn = client.connectPeer(hostID); conn.onOpen(() => { console.log(`Connected to host ${hostID}`); // Start game as client // wrapper.startClient(players, conn); }); conn.on('data', (data) => { console.log(`Received from host:`, data); }); }); // Handle incoming peer connections client.onConnection.once((conn) => { console.log(`Peer attempting connection from ${conn.peerID}`); conn.onOpen(() => { console.log(`Peer connection opened`); conn.send({ type: 'hello', message: 'Connected!' }); }); conn.on('data', (data) => { console.log('Received peer data:', data); }); }); ``` -------------------------------- ### Simple Game Implementation with NetplayJS Source: https://github.com/rameshvarun/netplayjs/blob/master/README.md This JavaScript code defines a simple game using NetplayJS. It initializes player positions, implements a tick function for game logic based on player input, and a draw function to render the game state on a canvas. It utilizes NetplayJS's RollbackWrapper for netcode. ```javascript class SimpleGame extends netplayjs.Game { // In the constructor, we initialize the state of our game. constructor() { super(); // Initialize our player positions. this.aPos = { x: 100, y: 150 }; this.bPos = { x: 500, y: 150 }; } // The tick function takes a map of Player -> Input and // simulates the game forward. Think of it like making // a local multiplayer game with multiple controllers. tick(playerInputs) { for (const [player, input] of playerInputs.entries()) { // Generate player velocity from input keys. const vel = input.arrowKeys(); // Apply the velocity to the appropriate player. if (player.getID() == 0) { this.aPos.x += vel.x * 5; this.aPos.y -= vel.y * 5; } else if (player.getID() == 1) { this.bPos.x += vel.x * 5; this.bPos.y -= vel.y * 5; } } } // Normally, we have to implement a serialize / deserialize function // for our state. However, there is an autoserializer that can handle // simple states for us. We don't need to do anything here! // serialize() {} // deserialize(value) {} // Draw the state of our game onto a canvas. draw(canvas) { const ctx = canvas.getContext("2d"); // Fill with black. ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw squares for the players. ctx.fillStyle = "red"; ctx.fillRect(this.aPos.x - 5, this.aPos.y - 5, 10, 10); ctx.fillStyle = "blue"; ctx.fillRect(this.bPos.x - 5, this.bPos.y - 5, 10, 10); } } SimpleGame.timestep = 1000 / 60; // Our game runs at 60 FPS SimpleGame.canvasSize = { width: 600, height: 300 }; // Because our game can be easily rewound, we will use Rollback netcode // If your game cannot be rewound, you should use LockstepWrapper instead. new netplayjs.RollbackWrapper(SimpleGame).start(); ``` -------------------------------- ### JavaScript: Panel and Viewer Interaction Source: https://github.com/rameshvarun/netplayjs/blob/master/netplayjs-demo/src/index.html This snippet handles user interactions with a collapsible panel and a viewer element. It includes event listeners for an expand button and a panel scrim to toggle the panel's 'open' class. It also manages the selection state of links that target the viewer, ensuring only one link is selected at a time and updating the viewer's focus. ```javascript const panel = document.getElementById("panel"); const viewer = document.getElementById("viewer"); const expandButton = document.getElementById("expandButton"); const panelScrim = document.getElementById("panelScrim"); expandButton.addEventListener("click", function (event) { event.preventDefault(); panel.classList.toggle("open"); }); panelScrim.onclick = (event) => { event.preventDefault(); panel.classList.toggle("open"); }; document.querySelectorAll('a[target="viewer"]').forEach((link) => { console.log(link); link.addEventListener("click", function (event) { if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return; document .querySelectorAll('a[target="viewer"]') .forEach((l) => l.parentElement.classList.remove("selected")); link.parentElement.classList.add("selected"); viewer.focus(); panel.classList.remove("open"); }); }); ``` -------------------------------- ### Basic NetplayJS Game Structure in TypeScript Source: https://github.com/rameshvarun/netplayjs/blob/master/README.md Defines the fundamental structure of a game using NetplayJS by extending the `netplayjs.Game` class. This includes implementing initialization, update, drawing, and serialization methods. ```typescript class MyGame extends netplayjs.Game { // NetplayJS games use a fixed timestep. static timestep = 1000 / 60; // NetplayJS games use a fixed canvas size. static canvasSize = { width: 600, height: 300 }; // Initialize the game state. constructor(canvas: HTMLCanvasElement, players: Array) {} // Tick the game state forward given the inputs for each player. tick(playerInputs: Map): void {} // Draw the current state of the game to a canvas. draw(canvas: HTMLCanvasElement) {} // Serialize the state of a game to JSON-compatible value. serialize(): JsonValue {} // Load the state of a game from a serialized JSON value. deserialize(value: JsonValue) {} } ``` -------------------------------- ### Include NetplayJS Library Source: https://github.com/rameshvarun/netplayjs/blob/master/README.md This script tag includes the NetplayJS library from a CDN. Ensure you use a valid version and integrity hash for production environments. It provides the necessary functionalities for building multiplayer games. ```html ``` -------------------------------- ### NetplayJS: Query Player Role and Identity (TypeScript) Source: https://context7.com/rameshvarun/netplayjs/llms.txt This TypeScript code snippet demonstrates how to use the NetplayPlayer class to determine a player's unique ID, whether they are the local player, a remote opponent, the server/host, or a client. It iterates through players, logs their roles, and shows conditional logic for host-authoritative game updates and local player input processing. ```typescript import { NetplayPlayer, Game, DefaultInput } from 'netplayjs'; class PlayerAwareGame extends Game { static timestep = 1000 / 60; static canvasSize = { width: 600, height: 400 }; players: Array; constructor(canvas: HTMLCanvasElement, players: Array) { super(); this.players = players; for (const player of players) { // Unique player ID (0, 1, 2, etc.) const id = player.getID(); // Check if this is the local player if (player.isLocalPlayer()) { console.log(`Player ${id} is local (you)`); } // Check if this is a remote player if (player.isRemotePlayer()) { console.log(`Player ${id} is remote (opponent)`); } // Check if this is the host/server if (player.isServer() || player.isHost) { console.log(`Player ${id} is the host`); } // Check if this is a client if (player.isClient()) { console.log(`Player ${id} is a client`); } } } tick(playerInputs: Map): void { for (const [player, input] of playerInputs.entries()) { // Only the host should run server-side logic if (player.isLocalPlayer() && player.isServer()) { // Host-authoritative game logic this.spawnEnemies(); this.checkGameOver(); } // All players process their own inputs if (player.isLocalPlayer()) { this.updateLocalPlayer(input); } } } draw(canvas: HTMLCanvasElement) { const ctx = canvas.getContext("2d")!; ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Highlight local player differently for (const player of this.players) { ctx.fillStyle = player.isLocalPlayer() ? "green" : "red"; // Draw player representation } } spawnEnemies(): void { /* ... */ } checkGameOver(): void { /* ... */ } updateLocalPlayer(input: DefaultInput): void { /* ... */ } } new RollbackWrapper(PlayerAwareGame).start(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.