npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ecmaservegames/host

v0.0.10

Published

A host server for a game.

Downloads

5

Readme

EcmaserveGames/host ⬛

EcmaserveGames is intended to be an expressive ecosystem for building web-based board(-like) games.

This is a host server for building your game application on.

Installation

npm install <TBD>

Running the Host

You will need to create your own entry file for Node.JS.

/// index.js
const { GameServer } = require('<TBD>')
const game = new GameServer().run()

Core Concepts

  • Game State

    This core concept is inspired by uni-direction data flow such as React or Flux. Any particular game instance has a structured game state that can be serialized and transmitted from the host to any connected participant (either as a player or observer).

    There is a corresponding socket address to receive updates on the game state. i.e. - https://<hostname>/games/<game_instance_id>/state

    As noted, game-state is not intended to be computed by the client so it relies on other mechanisms to update and mutate as the game is played.

    EcmaserveGames does not prescribe a game state definition and allows you to build it, as needed. The supported format for describing your state definition is Protocol Buffers or protobuf and requires a singular State message. 🔗 Read More about Protocol Buffers

    /// state.proto
    
    package testgame;
    syntax = "proto3";
    message State { /// <-- Required name
      string current_player_id = 2;
      int32 current_player_roll_count = 3;
    }

    Loading these State messages is done as part of the host's builder pattern. You will need to supply a filename, packageName and optionally an initial game state for any created game instances.

    /// index.js
    const { GameServer } = require('<TBD>')
    const path = require('path')
    
    const game = new GameServer()
      .useState(
        // A filename
        path.resolve(__dirname, './state.proto'),
        // Your protobuf package name
        'testgame',
        // [OPTIONAL] An initial game state
        {
          currentPlayerId: "player-1",
          currentPlayerRolCount: 0
        }
      )
      .run()
  • Authentication

    Authentication is handled via middleware. It is not recommended to write a custom authentication method. EcmaserveGames/host expects the middleware context to supply the identified user as an object at <middleware_context>.state.user.

    If you are using Bearer tokens, it is recommended to use koa-jwt since the host is built using koa's middleware server.

    /// index.js
    const { GameServer } = require('<TBD>')
    const jwt = require('koa-jwt)
    
    const game = new GameServer()
    game
      .useAuthentication(jwt({ secret: '<JWT_SECRET_KEY>' }))
      .run()
  • Actions

    Actions are the trigger for updating game state. There are not pre-defined actions so you are free to create your own Actions defintion.

    Like Game State, Protocol Buffers are the supported means of defining your game's actions.

    However, to properly route rules and game mechanics, Actions definition must be in the following well-known structure.

    /// actions.proto
    
    package testgame;
    syntax = "proto3";
    
    message Actions {
      oneof Action {
        ... /// any keyed messages that become your action set
      }
    }

    Loading the Actions message is done as part of the host's builder pattern. You will need to supply a filename and packageName.

    /// index.js
    const { GameServer } = require('<TBD>')
    const path = require('path')
    
    const game = new GameServer()
      .useActions(
        // A filename
        path.resolve(__dirname, './actions.proto'),
        // Your protobuf package name
        'testgame',
      )
      .run()
  • Mechanics

    Mechanics are methods that manipulate game state when an action is triggered. They are not evaluated if the rules cannot be validated. They also do not require a return and can perform async operations.

    // CustomGameMechanic.js
    const { Mechanic } = require('<TBD>')
    
    const DoFoo = Mechanic
      .create('do_foo')
      .forActions('foo')
      .use(
        ({ 
          actions, // 📩 The Actions Message received
          gameState // 🎲 The GameState
        }) => { 
          /// 🦠 Have fun mutating your gameState freely!
        })
    
    modules.exports = {
      DoFoo
    }

    They are added to the game host like such:

    /// index.js
    const { GameServer } = require('<TBD>')
    const { DoFoo } = require('./CustomGameMechanic')
    
    const game = new GameServer()
      .useMechanics(DoFoo)
      .run()
  • Rules

    Rules are methods that can validate if an action should be allowed and may manipulate the game state if that action is allowed.

    These are the real guts of the application and offer a great deal of flexibility.

    // GoldenRule.js
    const { Rule } = require('<TBD>')
    
    const GoldenRule = Rule
      .create('golden')
      .forActions('doOntoOther')
      .use(
        ({ 
          actions, // 📩 The Actions Message received
          gameState, // 🎲 The current/unmutated GameState
          ruleResults, // 🧪 The results of previousl evaluated rules
          mutate, // 🦠 Register a mutation if the rules pass
          exeptionTo, // 🔀 A callback to remove another rule validation
                      // result. Keep in mind that the order of 
                      // adding rules is the order of rule evaluation.
          user // 👤 The user provided from authentication middleware
        }) => { 
            
        })
    
    modules.exports = {
      GoldenRule
    }

    Rules are added to the game host like such:

    /// index.js
    const { GameServer } = require('<TBD>')
    const { GoldenRule } = require('./GoldenRule')
    
    const game = new GameServer()
      .useRules(GoldenRule)
      .run()
  • Game State Masks

    Game state masks allow you to selectively hide parts of the game state to a connected participant. They are applied to each individual participant after a game state update has been triggered by an action and are not persisted in any way.

    // PlayerHand.js
    
    const hideOtherPlayerHands = 
      ({ 
        user, // 👤 The participant provided from authentication middleware
        mutate // 🦠 A callback to reveal the game state being sent to a participant
      }) => {
        mutate(state => {
          state.playerHands = state.playerHands.filter(
            hand => {
              return hand.playerId === user.playerId
            })
        })
      }
    
    modules.exports = {
      hideOtherPlayerHands
    }

    Game State masks are added to the game host like such:

    /// index.js
    const { GameServer } = require('<TBD>')
    const { hideOtherPlayerHands } = require('./PlayerHand')
    
    const game = new GameServer()
      .useStateMask(hideOtherPlayerHands)
      .run()