Skip to content

Framework for online games and applications on Node.js

License

Notifications You must be signed in to change notification settings

mopsicus/shardy

Repository files navigation

Static Badge Static Badge

Shardy

Framework for online games and apps

Quick start Β· Docs Β· Service template Β· Unity client Β· Report Bug

πŸ’¬ Overview

Shardy is a framework for online games and applications on Node.js. It provides the basic functionality for building microservices solutions: mobile, social, web, multiplayer games, realtime applications, chats, middleware services, etc.

The main goal of Shardy is to give simple free solution for building almost any kind of online project. πŸ’ͺ

✨ Features

  • Microservices paradigm
  • Simple API: request, response, subscribe, etc
  • Socket and Websocket communication
  • Lightweight and fast: Node.js & TypeScript
  • Support custom serialization
  • Support custom handshake validation
  • Advanced logger: tags, filters, scopes
  • Flexible extension
  • Good reference materials: docs, snippets, examples
  • Almost zero configuration

πŸš€ Usage

Why should I use Shardy?

Start your project or backend for mobile game with Shardy and rest assured:

  • easy to use: work with a user-friendly API and don't worry about how it works under the hood
  • scalable architecture: get exists or create your own microservices, link them together and scale your app
  • fast & lightweight: core network architecture, based on Node.js without any 3rd party libs
  • full docs: Shardy provides good docs with all necessary sections and API references, also all code is coverged by comments

Quick start

It's really easy to start developing your project with Shardy:

  1. Clone a service template or creare a new one
    git clone [email protected]:mopsicus/shardy-template.git
    
  2. Install Shardy and all dependencies:
    npm install
    
  3. Edit .env.dev
  4. Run debug mode
    npm run debug
    

See the documentation section for all API methods and examples.

Service interface

Your (micro)service must implement Service interface to handle general events. This is the main place where you control connected and disconnected users and pass them on to your app.

All your other objects, classes, DBs, etc. should be linked to this class otherwise you won't be able to access them from commands\requests files.

import { TransportType, Service, Client } from 'shardy';

export class MyService implements Service {

    //
    // add your objects, DB connections or something else in this class
    //

    // set name for your service
    name = process.env.SERVICE_NAME; 

    // set transport type
    transport = process.env.SERVICE_TRANSPORT as TransportType; 

    async onConnect(client: Client): Promise<void> {
    // new client connected
    }

    async onDisconnect(client: Client): Promise<void> {
    // client disconnected
    }

    async onReady(client: Client): Promise<void> {
    // client ready to work
    }    

    async onListening(): Promise<void> {
    // service started
    }

    async onError(error: Error): Promise<void> {
    // some error occured
    }

    async onClose(): Promise<void> {
    // service stopped
    }

}

Requests and commands

Shardy API is really user-friendly, it provides an RPC framework for inter-process communications. The snippets below show how each can be used.

The general difference between requests and commands that is other side must respond to requests and doesn't respond to commands. So this means that when you make a request, you have a callback with response data. And when you send a command, you are simply notifying the other side about something.

Request:

client.request('status', (data) => {
// send request and work with response in callback
});
client.request('status', (data) => {
// send request with payload
}, payload);

Command:

client.command('status'); // just send command/event
client.command('status', payload); // send command with payload

Subscribe:

client.on('status', (data) => {
// subscribe on command and process data whenever receive
});

You can also send request from server to client, see the documentation for it.

Command structure

All commands/requests export a named function. Shardy passes all the necessary objects to your commands:

  • commander – current connection controller
  • payload – received data and meta data about the command/request
  • service – reference to your service instance, for communication with your objects, DB, etc

Important

If your command is request, be sure to make a response to it, otherwise caller will timeout.

import { Commander, PayloadData, Service } from 'shardy';

export const status = (commander: Commander, payload: PayloadData, service: Service) => {

    // process received data
    console.log('data', payload.data);

    // to make a response to request
    commander.response(payload); 

    // to make an error on request
    commander.error(payload); 
};

Validation

When a client connects to the server, it must successfully complete the handshake before it can begin. Shardy uses a two-step handshake for connections.

Stages of handshake:

  1. The client sends a handshake to the server
  2. Server receives and verifies it:
    • Sends an acknowledgement to the client
    • Disconnects the client, if the verification fails
  3. The client receives the acknowledgement and verifies it:
    • Sends a reply acknowledgement to the server
    • Disconnects, if the verification fails
  4. After verifying the handshake and acknowledgement, the client and server can communicate with each other

If your implementation does not need to do a two-step handshake, you can set "stubs" on these methods.

Shardy provides an interface for handshake validation. You can implement your own handshake data structure and validation for all these stages. Inherit the Validator class, implement methods and pass it to your service and client.

import { Validator, ValidatorState } from 'shardy';

export class MyHandshake implements Validator {

    verifyHandshake(body: Buffer): ValidatorState {
    // vefify initial handshake
    }

    verifyAcknowledgement(body: Buffer): ValidatorState {
    // vefify acknowledgement data
    }

    acknowledgement(body: Buffer): Buffer {
    // data for acknowledgement after handshake validation passed
    }

    handshake(body?: Buffer): Buffer {
    // data for initial handshake
    }

}

Serialization

Shardy supports custom serialization of transmitted data. You can use JSON, MessagePack, Protobuf, FlatBuffers, etc. or your own serializer.

Just inherit the Serializer class, implement encode/decode methods and pass it to your service and client.

import { PayloadData, Serializer } from 'shardy';

export class MyJsonSerializer implements Serializer {

    encode(body: PayloadData): Buffer {
    // encode PayloadData to Buffer for transporting
    }

    decode(body: Buffer): PayloadData {
    // decode recevied data and serialize to PayloadData
    }

}

πŸ—οΈ Contributing

We invite you to contribute and help improve Shardy. Please see contributing document. πŸ€—

You also can contribute to the Shardy project by:

  • Helping other users
  • Monitoring the issue queue
  • Sharing it to your socials
  • Referring it in your projects

🀝 Support

You can support Shardy by using any of the ways below:

  • Bitcoin (BTC): 1VccPXdHeiUofzEj4hPfvVbdnzoKkX8TJ
  • USDT (TRC20): TMHacMp461jHH2SHJQn8VkzCPNEMrFno7m
  • TON: UQDVp346KxR6XxFeYc3ksZ_jOuYjztg7b4lEs6ulEWYmJb0f
  • Visa, Mastercard via Boosty
  • MIR via CloudTips

βœ‰οΈ Contact

Before you ask a question, it is best to search for existing issues that might help you. Anyway, you can ask any questions and send suggestions by email or Telegram.

πŸ”‘ License

Shardy is licensed under the MIT License. Use it for free and be happy. πŸŽ‰