Coder Social home page Coder Social logo

rameshvarun / netplayjs Goto Github PK

View Code? Open in Web Editor NEW
451.0 17.0 32.0 5.97 MB

Make P2P multiplayer browser games, no server hosting or synchronization code required. Powered by rollback netcode + WebRTC.

Home Page: https://rameshvarun.github.io/netplayjs/

License: ISC License

TypeScript 86.74% JavaScript 2.55% HTML 10.61% Dockerfile 0.10%
rollback-netcode multiplayer-browser-game typescript p2p threejs game-development webrtc gamedev multiplayer multiplayer-game game-networking

netplayjs's Introduction

NetplayJS

Node.js CI npm

Make peer-to-peer WebRTC-based multiplayer games in JavaScript, no server hosting or network synchronization code required!

[CHECK OUT THE DEMOS]

Quick Start

Remix on Glitch

Here's how NetplayJS works:

  • You create your game within static HTML files.
  • You can use a variety of HTML5 game frameworks, including Three.js.
  • You can host your game anywhere (GitHub Pages, Itch.io, Glitch, and many more).

NetplayJS handles most of the complicated aspects of multiplayer game development, letting you create games almost as if they were local multiplayer games. Synchronization and matchmaking are handled automatically under the hood - and best of all you don't have to host any servers!

Let's make a very simple game. Create an HTML file and add the following script tag.

<script src="https://unpkg.com/[email protected]/dist/netplay.js"></script>

Now add this javascript code to the same HTML somewhere within the <body>.

<script>
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();
</script>

And voila - we've made a real-time networked game with rollback netcode and client-side prediction.

Overview

NetplayJS is a framework designed to make the process of creating multiplayer browser games simple and fun. It consists of several different components.

  • netplayjs-server - The matchmaking and signaling server. You can host your own or use the public instance.
  • (WIP) netplayjs-netcode - Implementations of rollback netcode and lockstep netcode.
  • (WIP) netplayjs-connection - The client side code that communicates with the matchmaking server to establish connections.
  • netplayjs - A prototyping framework that lets you rapidly create multiplayer games.
  • netplayjs-demos - A collection of demos built in netplayjs to show off how to use it.

Installation

For simple usage, you can include NetplayJS directly from a script tag in an HTML file.

<script src="https://unpkg.com/[email protected]/dist/netplay.js"></script>

For larger projects, you should install NetplayJS from npm and bundle it with your application using Webpack or a similar module bundler.

npm install --save netplayjs

I also highly recommend that you use it with TypeScript, though this is not required. The examples following will be in TypeScript.

Usage

To create a game using NetplayJS, you create a new class that extends netplayjs.Game.

  • This class should implement functions for initializing, updating, and drawing the game.
  • It should implement functions for serializing / deserializing the state (more info in the next section).
  • It should contain static properties used to configure the netcode (see here).
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<NetplayPlayer>) {}

  // Tick the game state forward given the inputs for each player.
  tick(playerInputs: Map<NetplayPlayer, DefaultInput>): 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) {}
}

You can now start the game by passing your game class to one of several wrappers.

  • new LocalWrapper(MyGame).start(); - Runs mutiple instances of the game in the same browser page. Use for local testing and rapid iteration.
  • new RollbackWrapper(MyGame).start(); - Runs the game using rollback netcode. Use for game states that can be rewound and replayed.
  • new LockstepWrapper(MyGame).start(); - Runs the game using lockstep netcode. Use for game states that can't be rewound.

Game State Serialization

The client-side prediction and rewind capabilities of netplayjs are based off of the ability to serialize and deserialize the state of the game. In the quickstart example above, we let the autoserializer take care of this. For most games, however, you will need to implement your own logic. You can do this by overriding Game.serialize and Game.deserialize in your subclass.

If you cannot serialize the game state, you can still use NetplayJS, but you will need to use Lockstep netcode, rather than predictive netcodes like Rollback, and you need to mark your game as deterministic.

NetplayPlayer

A NetplayPlayer represents one player in a game. NetplayPlayer.getID() returns an ID that is stable across each network replication of the game.

DefaultInput

NetplayJS games are synchronized by sending inputs across a network. DefaultInput automatically captures and replicates keyboard events, mouse events, and touch events.

FAQ

Does NetplayJS require game code to be deterministic?

NetplayJS does not require game code to be deterministic, but is more efficient if it is. By default, NetplayJS corrects for drift by having one player (the host) send authoritative state updates to the others. NetplayJS will skip these updates if you explicitly mark your game as being deterministic.

Whether or not JavaScript operations are cross-platform deterministic is a difficult question. Here's what I know:

  • Integer arithmatic can be assumed to be deterministic.
  • In WASM code, floating point operations are cross-platform deterministic, with the exception of the bit pattern of NaN values.
    • This means that WASM physics engines like Ammo.js can be assumed to be deterministic.
  • Anything else is potentially up in the air.

Can NetplayJS be used with Unity, Godot, PlayCanvas, etc?

NetplayJS works best with lightweight game frameworks. The reason is we need game state to be kept in one place, so that it's easy to replicate across the network

Other engines tend to have complicated entity systems with their own state management, and wont fit nicely into the NetplayJS state model.

One way to get around this is to essentially create an invisible NetplayJS game that runs in the background and describes the actual game logic. Then, on draw(), instead of drawing directly, use the current game state to update the entities in your game engine's scene.

Assets Used from Other Projects

This repo contains code and assets from other open source projects.

netplayjs's People

Contributors

dungeonkim avatar rameshvarun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

netplayjs's Issues

Support Public Matchmaking

NetplayJS should have a public matchmaking system so players can play with strangers. Games shouldn't need to do anything to support this and it should come for free with the prototyping framework. A matchmaking server will need to be hosted, but since no game data flows through the server it should be quite cheap to host for an extended duration.

Add Deterministic Mode for Rollback

NetplayJS, by default, does not assume that game code is deterministic. Instead, it corrects for drift by having the host send authoritative state updates to the other players. In rollback netcode, because the player predicts ahead, state updates trigger a rollback / replay in addition to the rollbacks from player inputs.

Lockstep has an option to disable state synchronization, but this hasn't been implemented in Rollback netcode yet.

The fix is pretty simple - don't send state updates, and don't wait for state updates to clean up old history.

3+ players?

I just found this library and I'm struggling to figure out whether it's only suitable for two-player games. If 3+ player games are supported, it would be nice to showcase it on the demo page, e.g. with the "FPS" game, which, based on my tests, does not appear to function with three browser windows open.

Add Delay-based Netcode

Lockstep netcode allows you to synchronize a deterministic game over the network even if you cannot serialize / restore it. This is used in the physics demo. However lockstep is not really practical, since a stall-free experience requires latency < timestep.

Delay-based netcode adds input delay to allow additional time for inputs to resolve.

I think the best move is to add this as a new class. Trying to implement delay as a parameter to LockstepNetcode could make the lockstep code too complicated.

Demos are abysmally slow even on local

When testing the demos im getting like 1-2 fps on most of the demos even whem im running it on localhost, also the resolution is low and blurred, why is that?
Tested on both firefox and edge.

Dissable network debug info?

I'm trying to make a game using this library and it seems like there is an always-on debug info box. Is there any way to disable it?

Add a Chat Window

If players are able to play with strangers, we probably want some kind of chat system that is available even before the game actually starts. We already have data channels open so we might as well allow chat messages to be sent over these. Also, because it's WebRTC we could eventually support audio chat.

For now just give players random usernames. Give the host the ability to disable chat, and allow the chat window to be hidden / shown at any time.

One sided rollbacks

Any ideas why one-sided rollbacks could occur?
I made a small testing game and tried it with a friend, I could see his actions jaggy, but on his end it was smooth.
This also happened when he was hosting. I was still seeing lots of rollbacks, but it was smooth on his end.

I'm the blue box in the below vids.

This is my POV:
https://user-images.githubusercontent.com/8401769/147406805-a8b15b0e-137a-4dc5-af68-0c099426c7a2.mp4

This is his:
https://user-images.githubusercontent.com/8401769/147406872-82831a48-8843-425e-a286-c8a7db1d2f1b.mp4

In my POV the yellow box (my friend) has a jaggy movement, due to lots of rollbacks i assume.
In his POV, my box (blue) has smooth movement.

I've noticed that the history size on my friends POV is 1-3, but on my end it was 5-7. Not sure if this has anything to do with it.
How would one go about fixing this?

Game Pauses When One Player Backgrounds a Tab

Right now, for both rollback and lockstep netcode, if one user backgrounds their tab, the game will totally freeze. Currently it’s not that big of an issue since we only support two-player games anyway, but as we move on to >2 players this problem must be solved.

Right now the main loops of both netcode implementations look something like this.

start() {
  setInterval(() => {
    this.tick();
  }, this.timestep);
}

The problem is that the browser will stop calling setInterval (and other callbacks like requestAnimationFrame) when the tab is backgrounded meaning that the game wont tick anymore.

The good news is that this is solvable. Even when backgrounded, the pages are responding to messages on the RTCDataChannel, meaning that event listeners are being run. We should be able to tick during these event listeners, though this will require some changes to the main loops to make this possible.

LockstepNetcode: Handle case where state sync arrives early

Right now there's an edge case in LockstepNetcode where the host's authoritative state sync can arrive before a client actually reaches that frame (and thus the client can't apply the state sync). This is mostly likely to happen when communicating over localhost.

The fix is to queue the state syncs if they arrive early.

It should be impossible for a state sync to arrive late since the sync is sent before the host's inputs. Without the host's inputs, we can never advance past that frame.

Easier demos

I'm confused to get started. Is it possible to make an example on glitch.com so it becomes clear how things are set up?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.