Skip to content

3mcd/javelin

Repository files navigation

CD codecov discord

Javelin

Join us on Discord!

Javelin is a collection of JavaScript packages that provide the means to create multiplayer games for Node and web browsers. It's comprised of an Entity-Component System (ECS), an efficient networking protocol, and other helpful utilities like a high-precision game loop for Node.

Javelin's lack of opinions makes it a good candidate for building many kinds of multiplayer games, but you'll need to bring your own network transport (WebSockets, WebRTC, etc.), database, auth strategy, controller support, matchmaking, etc.

API Sample

Below is an example game server that broadcasts entities with both Position and Velocity components to connected clients.

import { createWorld, createQuery, component, useMonitor } from "@javelin/ecs"
import { createMessageProducer, encode, float64 } from "@javelin/net"
import { createHrtimeLoop } from "@javelin/hrtime-loop"
import { broadcast } from "./net"

const Position = {
  x: float64,
  y: float64,
}
const Velocity = {
  x: float64,
  y: float64,
}
const world = createWorld()
const points = createQuery(Position, Velocity)
const messages = createMessageProducer()

// create an entity
world.create(component(Position), component(Velocity, { x: 0, y: -9.81 }))
// create/delete points on client
world.addSystem(() => useMonitor(points, messages.attach, messages.destroy))
// update point positions
world.addSystem(() =>
  points((e, [p, v]) => {
    p.x += v.x
    p.y += v.y
  }),
)
// broadcast messages to clients
world.addSystem(() => {
  if (useInterval((1 / 20) * 1000)) {
    points(messages.update)
    broadcast(encode(messages.take()))
  }
})
// start a high-precision game loop
createHrtimeLoop(world.step, (1 / 60) * 1000).start()

There's a lot more that goes into building a game, but hopefully this example demonstrates the unobtrusive and consise nature of Javelin's APIs.

Docs

Visit https://javelin.games

Packages

Name Description
@javelin/core Core types, utilities, and data model
@javelin/ecs Build games using the ECS pattern
@javelin/net Synchronize ECS worlds
@javelin/pack Convert objects to and from binary arrays
@javelin/hrtime-loop Create smooth, high-precision game loops in NodeJS
@javelin/isomorphic-utils Universal utils requiring separate browser/node builds

Examples

Name Description NPM Script
spin Entity/component synchronization over WebRTC datachannels example:spin
interop Interop with Cannon and Three.js objects example:interop

Development

This project is a monorepository (monorepo for short), which is a collection of independent NPM packages that are located in the same repository for improved development, publishing, and visibility.

You can "bootstrap" all monorepo and package dependencies using the following command:

pnpm i

Build times are quick thanks to TypeScript project references. You can build (or re-build) all packages like so:

pnpm build --recursive

The structure of the monorepo looks something like this:

packages/
├── core/
│   ├── dist/
│   │   ├── esm/
│   │   │   ├── index.js
│   │   │   ├── index.d.ts
│   │   │   └── ...
│   │   └── cjs/index.js
│   ├── src/
│   │   ├── index.js
│   │   └── ...
│   ├── README.md
│   ├── package.json
│   └── tsconfig.json
├── ecs/
└── ...

Where each package has its own README file, TypeScript configuration file, source and dist directories, and scripts which describe how the package is cleaned, build, and prepared for publishing.

Each package has the following specified in its package.json:

"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",

Where "main" is a CommonJS entrypoint, "module" is an ESM entrypoint, and "types" is the root .d.ts file generated by the TypeScript compiler.

Packages reference eachother using pnpm workspace aliases and TypeScript project references. For example, the ecs package depends on core, so in the package.json file of @javelin/ecs:

{
  "dependencies": {
    "@javelin/core": "workspace:*"
  }
}

And in the same package's TypeScript config file, we make sure to include a project reference for faster builds and other benefits:

{
  "references": [{ "path": "../../core" }]
}