...

Package diarkisexec

import "github.com/Diarkis/diarkis/diarkisexec"
Overview
Index

Overview ▾

Package diarkisexec ▷

Diarkis Server DiarkisExec

This package helps you to setup Diarkis server with ease.

HTTP Server Example

logConfigPath := "/path/to/config/file/log.json"
meshConfigPath := ""

diarkisexec.SetupDiarkis(logConfigPath, meshConfigPath, &diarkisexec.Modules{
	Dive:       &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dive.json" },
	DM:         &diarkisexec.Options{ ConfigPath: "/path/to/config/file/configs/dm.json" },
	Field:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/configs/field.json" },
	MatchMaker: &diarkisexec.Options{ ConfigPath: "/path/to/config/file/configs/matchmaker.json" },
})

// If we use DIARKIS_HTTP_SERVER_CONFIG_PATH=$(http server config path) env, we do not need to call diarkisexec.SetupDiarkisHTTPServer()
diarkisexec.SetupDiarkisHTTPServer("/path/to/config/file/http.json")
diarkisexec.StartDiarkis()

UDP Server Example

logConfigPath := "/path/to/config/file/log.json"
meshConfigPath := ""

diarkisexec.SetupDiarkis(logConfigPath, meshConfigPath, &diarkisexec.Modules{
	Dive:       &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dive.json" },
	DM:         &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dm.json", ExposeCommands: true },
	Field:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/field.json", ExposeCommands: true },
	Group:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/group.json", ExposeCommands: true },
	MatchMaker: &diarkisexec.Options{ ConfigPath: "/path/to/config/file/matchmaker.json", ExposeCommands: true },
	Room:       &diarkisexec.Options{ ExposeCommands: true },
	Session:    &diarkisexec.Options{ ExposeCommands: true },
	P2P:        &diarkisexec.Options{ ExposeCommands: true },
})

// If we use DIARKIS_UDP_SERVER_CONFIG_PATH=$(udp server config path) env, we do not need to call diarkisexec.SetupDiarkisUDPServer()
diarkisexec.SetupDiarkisUDPServer("/path/to/config/file/udp.json")
diarkisexec.StartDiarkis()

TCP Server Example

logConfigPath := "/path/to/config/file/log.json"
meshConfigPath := ""

diarkisexec.SetupDiarkis(logConfigPath, meshConfigPath, &diarkisexec.Modules{
	Dive:       &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dive.json" },
	DM:         &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dm.json", ExposeCommands: true },
	Field:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/field.json", ExposeCommands: true },
	Group:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/group.json", ExposeCommands: true },
	MatchMaker: &diarkisexec.Options{ ConfigPath: "/path/to/config/file/matchmaker.json", ExposeCommands: true },
	Room:       &diarkisexec.Options{ ExposeCommands: true },
	Session:    &diarkisexec.Options{ ExposeCommands: true },
	P2P:        &diarkisexec.Options{ ExposeCommands: true },
})

// If we use DIARKIS_TCP_SERVER_CONFIG_PATH=$(tcp server config path) env, we do not need to call diarkisexec.SetupDiarkisTCPServer()
diarkisexec.SetupDiarkisTCPServer("/path/to/config/file/tcp.json")
diarkisexec.StartDiarkis()

Setting Up Diarkis Server (HTTP, UDP, or TCP) With Environment Variable

DiarkisExec allows you to setup Diarkis server with a specific network protocol by giving an environment variable instead of calling SetupDiarkisHTTPServer, SetupDiarkisUDPServer, or SetupDiarkisTCPServer.

[IMPORTANT] If you use either DIARKIS_HTTP_SERVER_CONFIG_PATH, DIARKIS_UDP_SERVER_CONFIG_PATH, or DIARKIS_TCP_SERVER_CONFIG_PATH,
            you do NOT need to call SetupDiarkisHTTPServer, SetupDiarkisUDPServer, or SetupDiarkisTCPServer.

Example:

# Start the server as Diarkis HTTP server
DIARKIS_HTTP_SERVER_CONFIG_PATH=/configs/http/main.json ./remote_bin/http

Valid Environment Variables For the Server

┌─────────────────────────────────┬──────────────────────────────────────────┐
│ DIARKIS_HTTP_SERVER_CONFIG_PATH │ Setup the server as Diarkis HTTP server. │
├─────────────────────────────────┼──────────────────────────────────────────┤
│ DIARKIS_UDP_SERVER_CONFIG_PATH  │ Setup the server as Diarkis UDP server.  │
├─────────────────────────────────┼──────────────────────────────────────────┤
│ DIARKIS_TCP_SERVER_CONFIG_PATH  │ Setup the server as Diarkis TCP server.  │
└─────────────────────────────────┴──────────────────────────────────────────┘

Declaring Custom Command Handlers

Diarkis Servers (UDP and TCP) allows you to define your own custom commands and their handlers on the server to be invoked by the client.

You must use SetServerCommandHandler(ver uint8, cmd uint16, handler CommandHandler) BEFORE invoking diarkisexec.StartDiarkis().

Example:

var ver uint8  = 10
var cmd uint16 = 100

func main() {

  logConfigPath := "/path/to/config/file/log.json"
  meshConfigPath := ""
  diarkisexec.SetupDiarkis(logConfigPath, meshConfigPath, &diarkisexec.Modules{
	  Dive:       &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dive.json" },
	  DM:         &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dm.json", ExposeCommands: true },
	  Field:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/field.json", ExposeCommands: true },
	  Group:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/group.json", ExposeCommands: true },
	  MatchMaker: &diarkisexec.Options{ ConfigPath: "/path/to/config/file/matchmaker.json", ExposeCommands: true },
	  Room:       &diarkisexec.Options{ ExposeCommands: true },
	  Session:    &diarkisexec.Options{ ExposeCommands: true },
	  P2P:        &diarkisexec.Options{ ExposeCommands: true },
  })

  // If we use DIARKIS_UDP_SERVER_CONFIG_PATH=$(udp server config path) env, we do not need to call diarkisexec.SetupDiarkisUDPServer()
  diarkisexec.SetupDiarkisUDPServer("/path/to/config/file/udp.json")

  diarkisexec.SetServerCommandHandler(ver, cmd, helloWorld)

  diarkisexec.StartDiarkis()
}

func helloWorld(ver uint8, cmd uint16, next func(error)) {
  userData.ServerRespond([]byte("Hello World"), ver, cmd, diarkisexec.CommandResponseOK, true)
  next(nil)
})

Send Custom Mesh Command (Server-to-Server Command)

Diarkis Server allows you to send your own custom mesh commands (internal server-to-server commands).

// Define data structure to be sent and received using Diarkis' td package (github.com/Diarkis/diarkis/td)
var exampleMeshRequestDef = td.DefineTransportData([]*td.Property{
  td.Property{ Name: "Greeting", Type: td.String },
})

var exampleMeshResponseDef = td.DefineTransportData([]*td.Property{
  td.Property{ Name: "Message", Type: td.String },
  td.Property{ Name: "OriginalMessage", Type: td.String },
})

// This operation must be written in a function.
exampleMeshRequest := exampleMeshRequestDef.New()
exampleMeshRequest.SetAsString("Message", "Hello")

resp, err := diarkisexec.SendMeshRPC(exampleMeshCmdID, meshTargetServerAddress, exampleMeshRequest.Pack())

// Handle the response and error from another server
if err != nil {
  // handle error here
}

exampleMeshResponse := exampleMeshResponseDef.New()
exampleMeshResponse.Unpack(resp)

Declaring Custom Mesh Command Handlers (Server-to-Server Command Handlers)

Diarkis Server allows you to define your own custom mesh commands (internal server-to-server commands).

You must use SetMeshRPCHandler(cmd uint16, handler MeshCommandHandler) BEFORE invoking diarkisexec.StartDiarkis().

Example:

var cmd uint16 = 20000

func main() {

  logConfigPath := "/path/to/config/file/log.json"
  meshConfigPath := ""
  diarkisexec.SetupDiarkis(logConfigPath, meshConfigPath, &diarkisexec.Modules{
	  Dive:       &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dive.json" },
	  DM:         &diarkisexec.Options{ ConfigPath: "/path/to/config/file/dm.json", ExposeCommands: true },
	  Field:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/field.json", ExposeCommands: true },
	  Group:      &diarkisexec.Options{ ConfigPath: "/path/to/config/file/group.json", ExposeCommands: true },
	  MatchMaker: &diarkisexec.Options{ ConfigPath: "/path/to/config/file/matchmaker.json", ExposeCommands: true },
	  Room:       &diarkisexec.Options{ ExposeCommands: true },
	  Session:    &diarkisexec.Options{ ExposeCommands: true },
	  P2P:        &diarkisexec.Options{ ExposeCommands: true },
  })

  // If we use DIARKIS_UDP_SERVER_CONFIG_PATH=$(udp server config path) env, we do not need to call diarkisexec.SetupDiarkisUDPServer()
  diarkisexec.SetupDiarkisUDPServer("/path/to/config/file/udp.json")

  diarkisexec.SetMeshRPCHandler(cmd, handleExampleMeshCommand)

  diarkisexec.StartDiarkis()
}

func handleExampleMeshCommand(payload []byte, senderAddress string) ([]byte, error) {
  exampleMeshRequest := exampleMeshRequestDef.New()
  exampleMeshRequest.Unpack(payload)
  hello, ok := exampleMeshRequest.GetAsString("Greeting")

  if !ok {
    // handle error here
  }

  helloWorld := util.StrConcat(hello, "world")

  // create response using Diarkis' td package (github.com/Diarkis/diarkis/td)
  exampleMeshResponse := exampleMeshResponseDef.New()
  exampleMeshResponse.SetAsString("Message", helloWorld)
  exampleMeshResponse.SetAsString("OriginalMessage", hello)

  return exampleMeshResponse.Pack()
}

Index ▾

Constants
func CreateReturnBytes(data map[string]interface{}) ([]byte, error)
func GetMeshAddress() string
func GetMeshAddressByServerRole(serverRole string) string
func GetMeshAddressByServerType(serverType string) string
func GetMeshAddressesByServerRole(serverRole string) []string
func GetMeshAddressesByServerType(serverType string) []string
func GetPublicEndpoint() string
func GetPublicEndpointByMeshAddress(meshAddress string) string
func GetServerRole() string
func GetServerRoleByMeshAddress(meshAddress string) string
func GetServerType() string
func GetServerTypeByMeshAddress(meshAddress string) string
func GetSharedData(key string) (int16, bool)
func IsOffline() bool
func IsOfflineByMeshAddress(meshAddress string) bool
func IsServerRoleHTTP() bool
func IsServerRoleTCP() bool
func IsServerRoleUDP() bool
func IsTaken() bool
func IsTakenByMeshAddress(meshAddress string) bool
func MarkServerAsOffline()
func MarkServerAsOnline()
func MarkServerAsTaken()
func MarkServerAsTakenIf(callback func() bool)
func NewLogger(name string) *log.Logger
func OnKeepAlive(handler func(userData *user.User, next func(error)))
func RemoveOnSharedDataRemove(cb func(key string))
func RemoveOnSharedDataUpdate(cb func(key string, value int16))
func RemoveSharedData(key string) bool
func ResponseStatusBad() uint8
func ResponseStatusErr() uint8
func ResponseStatusOK() uint8
func SendMeshCommand(cmd uint16, addresses []string, data interface{}, reliable bool) error
func SendMeshRPC(cmd uint16, address string, data []byte) (response []byte, err error)
func SendMeshRequest(cmd uint16, address string, data interface{}, callback func(err error, response map[string]interface{}))
func SetMeshCommandHandler(cmd uint16, handler MeshCommandHandler)
func SetMeshRPCHandler(cmd uint16, handler MeshRPCHandler)
func SetOnSharedDataRemove(cb func(key string))
func SetOnSharedDataUpdate(cb func(key string, value int16))
func SetServerCommandHandler(ver uint8, cmd uint16, handler CommandHandler)
func SetSharedData(key string, value int16) bool
func SetupDiarkis(logConfigPath, meshConfigPath string, m *Modules)
func SetupDiarkisHTTPServer(config string)
func SetupDiarkisTCPServer(config string)
func SetupDiarkisUDPServer(config string)
func SetupNotificationService(name string, interval int64, callback func() (*Notification, error))
func StartDiarkis()
type CommandHandler
type MeshCommandHandler
type MeshRPCHandler
type Modules
type Notification
type Options

Constants

CommandResponseBad represents the server response status code for UDP/TCP server command.

The status indicates the command execution failed due to the client operation: invalid parameters etc..

const CommandResponseBad uint8 = server.Bad

CommandResponseErr represents the server response status code for UDP/TCP server command.

The status indicates the command execution failed due to the server internal error.

const CommandResponseErr uint8 = server.Err

CommandResponseOK represents the server response status code for UDP/TCP server command.

The status indicates the command execution was successful.

const CommandResponseOK uint8 = server.Ok

func CreateReturnBytes

func CreateReturnBytes(data map[string]interface{}) ([]byte, error)

CreateReturnBytes converts the given map[string]interface{} into a byte error for MeshCommandHandler return value.

func GetMeshAddress

func GetMeshAddress() string

GetMeshAddress returns the server's internal mesh address.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.

func GetMeshAddressByServerRole

func GetMeshAddressByServerRole(serverRole string) string

GetMeshAddressByServerRole returns a randomly selected internal mesh address of the given server role.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] Server roles are automatically determined by server's network protocol.
       The valid server roles are: HTTP, UDP, and TCP.

func GetMeshAddressByServerType

func GetMeshAddressByServerType(serverType string) string

GetMeshAddressByServerType returns a randomly selected internal mesh address of the given server type.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] The default server types: HTTP, UDP, and TCP.
[NOTE] Server type can be customized by using DIARKIS_SERVER_TYPE=$(server_type) env.

func GetMeshAddressesByServerRole

func GetMeshAddressesByServerRole(serverRole string) []string

GetMeshAddressesByServerRole returns an array of internal mesh addresses of the given server role.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] Server roles are automatically determined by server's network protocol.
       The valid server roles are: HTTP, UDP, and TCP.

func GetMeshAddressesByServerType

func GetMeshAddressesByServerType(serverType string) []string

GetMeshAddressesByServerType returns an array of internal mesh addresses of the given server type.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] The default server types: HTTP, UDP, and TCP.
[NOTE] Server type can be customized by using DIARKIS_SERVER_TYPE=$(server_type) env.

func GetPublicEndpoint

func GetPublicEndpoint() string

GetPublicEndpoint returns the server's public endpoint.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] This function is available for UDP and TCP servers only.

func GetPublicEndpointByMeshAddress

func GetPublicEndpointByMeshAddress(meshAddress string) string

GetPublicEndpointByMeshAddress returns the remote server's public endpoint by the remote server's internal mesh address.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] This function is available for UDP and TCP servers only.

func GetServerRole

func GetServerRole() string

GetServerRole returns the server role of itself.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] Server roles are automatically determined by server's network protocol.
       The valid server roles are: HTTP, UDP, and TCP.

func GetServerRoleByMeshAddress

func GetServerRoleByMeshAddress(meshAddress string) string

GetServerRoleByMeshAddress returns the server role of a remote server by its internal mesh address.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] Server roles are automatically determined by server's network protocol.
       The valid server roles are: HTTP, UDP, and TCP.

func GetServerType

func GetServerType() string

GetServerType returns the server type of itself.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] The default server types: HTTP, UDP, and TCP.
[NOTE] Server type can be customized by using DIARKIS_SERVER_TYPE=$(server_type) env.

func GetServerTypeByMeshAddress

func GetServerTypeByMeshAddress(meshAddress string) string

GetServerTypeByMeshAddress returns the server type of a remote server by its internal mesh address.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.
[NOTE] The default server types: HTTP, UDP, and TCP.
[NOTE] Server type can be customized by using DIARKIS_SERVER_TYPE=$(server_type) env.

func GetSharedData

func GetSharedData(key string) (int16, bool)

GetSharedData returns synchronized shared data by its key.

func IsOffline

func IsOffline() bool

IsOffline returns true if the server is marked to be shutdown.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.

func IsOfflineByMeshAddress

func IsOfflineByMeshAddress(meshAddress string) bool

IsOfflineByMeshAddress returns true if the remote server of the given mesh address is marked to be shutdown.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.

func IsServerRoleHTTP

func IsServerRoleHTTP() bool

IsServerRoleHTTP returns true if the server role is HTTP.

[IMPORTANT] This function does not work correctly before Diarkis server is ready.

[NOTE]      Server role is decided according to the network protocol the server uses and it does not change unlike server type.

func IsServerRoleTCP

func IsServerRoleTCP() bool

IsServerRoleTCP returns true if the server role is UDP.

[IMPORTANT] This function does not work correctly before Diarkis server is ready.

[NOTE]      Server role is decided according to the network protocol the server uses and it does not change unlike server type.

func IsServerRoleUDP

func IsServerRoleUDP() bool

IsServerRoleUDP returns true if the server role is UDP.

[IMPORTANT] This function does not work correctly before Diarkis server is ready.

[NOTE]      Server role is decided according to the network protocol the server uses and it does not change unlike server type.

func IsTaken

func IsTaken() bool

IsTaken returns true if the server is in taken state.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.

func IsTakenByMeshAddress

func IsTakenByMeshAddress(meshAddress string) bool

IsTakenByMeshAddress returns true if the remote server of the given mesh address is in taken state.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()

[NOTE] Uses mutex lock internally.

func MarkServerAsOffline

func MarkServerAsOffline()

MarkServerAsOffline flags the server as "OFFLINE".

[NOTE] "OFFLINE" server does not allow the following operations on the server:
       - Accept new user creation and connection.
       - Diarkis Room's new room creation.
       - Diarkis Group's new group creation.
       - Diarkis Session's new session creation.
[NOTE] "OFFLINE" server still functions as normal except for the prohibited operations listed above.

func MarkServerAsOnline

func MarkServerAsOnline()

MarkServerAsOnline flags the server as "ONLINE".

[NOTE] "ONLINE" is the default state of the server.

func MarkServerAsTaken

func MarkServerAsTaken()

MarkServerAsTaken flags the server as "TAKEN".

[NOTE] "TAKEN" server does not allow the following operation on the server:
       - Accept new user creation and connection.
[NOTE] "TAKEN" server still functions as normal except for the prohibited operations listed above.

func MarkServerAsTakenIf

func MarkServerAsTakenIf(callback func() bool)

MarkServerAsTakenIf flags the server as "TAKEN" if the callback returns true. The callback is invoked every 2 second and if the callback returns true, the server will be marked as "ONLINE".

[IMPORTANT] If the server is marked as "OFFLINE", this function will be ignored.

[NOTE]      This function is executed every 2 seconds so there will be race condition and it is not precise.

TAKEN - When the server is marked as "TAKEN", the server will NOT accept new user connections.

Example: The example code below uses CCU of the node to control TAKEN <==> ONLINE

server.MarkServerAsTakenIf(func() bool {
	// user package can tell you the CCU of the node
	ccu := user.GetCCU()
	if ccu >= maxAllowedCCU {
		// this will mark the server as TAKEN
		return true
	}
	// this will mark the server as ONLINE
	return false
})

func NewLogger

func NewLogger(name string) *log.Logger

NewLogger creates a new instance of log.Logger.

[IMPORTANT] This function must be invoked BEFORE calling diarkisexec.StartDiarkis()

func OnKeepAlive

func OnKeepAlive(handler func(userData *user.User, next func(error)))

OnKeepAlive assigns a callback to be invoked on every echo (UDP) or heartbeat (TCP).

	[IMPORTANT] Every callback must call next func(error) at the end of the operation
             to allow Diarkis to move on to the next on keep alive operations.

func RemoveOnSharedDataRemove

func RemoveOnSharedDataRemove(cb func(key string))

RemoveOnSharedDataRemove removes the given callback that has been assigned by SetOnSharedDataRemove.

[IMPORTANT] This function uses mutex lock internally.

func RemoveOnSharedDataUpdate

func RemoveOnSharedDataUpdate(cb func(key string, value int16))

RemoveOnSharedDataUpdate removes the given callback that has been assigned by SetOnSharedDataUpdate.

[IMPORTANT] This function uses mutex lock internally.

func RemoveSharedData

func RemoveSharedData(key string) bool

RemoveSharedData removes the given shared key and propagates the removal to all server nodes in the cluster.

The propagation of the shared data may take some time.

[IMPORTANT] Updated value may suffer from race condition.
            If multiple server nodes attempt to update the same key,
            The value of the key maybe overwritten.

func ResponseStatusBad

func ResponseStatusBad() uint8

ResponseStatusBad returns UDP or TCP server response status code for invalid command invocation.

func ResponseStatusErr

func ResponseStatusErr() uint8

ResponseStatusErr returns UDP or TCP server response status code for server error while handling command.

func ResponseStatusOK

func ResponseStatusOK() uint8

ResponseStatusOK returns UDP or TCP server response status code for successfully handled command.

func SendMeshCommand

func SendMeshCommand(cmd uint16, addresses []string, data interface{}, reliable bool) error

SendMeshCommand sends a mesh command of the given cmd to the given internal mesh address.

The server of the given address will invoke the command handler of the given cmd.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()
[IMPORTANT] The command handler must be defined using diarkisexec.SetMeshCommandHandler(cmd uint16, handler MeshCommandHandler).

Parameters

cmd       - Mesh command ID.
addresses - Internal mesh address of the server to send the command to.
data      - Data to be sent to the handler.
            The valid data type of data must be the following two types only:
            1. map[string]interface{}
            2. struct

The diagram below show how command is sent to multiple servers when you give multiple addresses:

                                                           ┌──────────┐
                                                     ┌────▶︎│ Server D │
                                                     │     └──────────┘
                               ┌──────────┐ <2> Send │
                           ┌──▶︎│ Server B │──────────┤     ┌──────────┐
                           │   └──────────┘          └────▶︎│ Server E │
┌──────────┐ <1> Send      │                               └──────────┘
│ Server A │ ──────────────┤                               ┌──────────┐
└──────────┘               │   ┌──────────┐          ┌────▶︎│ Server F │
                           └──▶︎│ Server C │──────────┤     └──────────┘
                               └──────────┘          │
                                                     │     ┌──────────┐
                                                     └────▶︎│ Server G │
                                                           └──────────┘

func SendMeshRPC

func SendMeshRPC(cmd uint16, address string, data []byte) (response []byte, err error)

SendMeshRPC sends a mesh RPC of the given cmd to the given internal mesh address and expects a response back from the server.

The server of the given address will invoke the command handler of the given cmd and sends back a response.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()
[IMPORTANT] The command handler must be defined using diarkisexec.SetMeshRPCHandler(cmd uint16, handler MeshRPCHandler).

Error Cases

┌───────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Error             │ Reason                                                                  │
╞═══════════════════╪═════════════════════════════════════════════════════════════════════════╡
│ Handler error     │ Handler function of the request returned an error.                      │
│ Network error     │ Mesh network error. Failed to send or receive server-to-server message. │
╘═══════════════════╧═════════════════════════════════════════════════════════════════════════╛

The diagram below show how SendMeshRequest works:

┌──────────┐ <1> Send request  ┌──────────┐
│ Server A │ ─────────────────▶︎│ Server B │
│          │ ◀︎──────────────── │          │
└──────────┘ <2> Send Response └──────────┘

Parameters

	cmd       - Mesh command ID.
	addresses - Internal mesh address of the server to send the command to.
	data      - Data to be sent to the handler.
             You may use td.DefineTransportData to create a data structure for RPC.

TransportData Example

// We define the structure of the transport data once
var exampleTransportData = td.DefineTransportData([]td.Property{
  td.Property{ Name: "name",    Type: td.String },
  td.Property{ Name: "counter", Type: td.Int },
  td.Property{ Name: "list",    Type: td.Uint32Array },
  td.Property{ Name: "data",    Type: td.Bytes },
  td.Property{ Name: "users",   Type: td.BytesArray },
})

var userTransportData = td.DefineTransportData([]td.Property{
  td.Property{ Name: "UID", Type: td.String },
  td.Property{ Name: "SID", Type: td.String },
})

// We then create an instance of already defined transport data
// when we need it
td := exampleTransportData.New()

// We set values
td.SetAsString("name", "John")
td.SetAsInt("counter", 0)
td.SetAsUint32Array("list", []uint32{ 10, 20, 2 })
td.SetAsBytes("data", []byte{ 0xff, 0xee, 0xdd, 0xcc })

users := make([]*userTransportData, len(users))

ud := userTransportData.New()

for i, userData := range users {
  ud.SetAsString("UID", userData.ID)
  ud.SetAsString("SID", userData.SID)
  users[i] = ud.Pack()
}

td.SetAsBytes("users", users)

// We then create the byte array for SendRPC
packed := td.Pack()

Returned Values

response  - The RPC handler on a remote server's response to the RPC sent.
err       - If the RPC operation fails, an error is sent.

func SendMeshRequest

func SendMeshRequest(cmd uint16, address string, data interface{}, callback func(err error, response map[string]interface{}))

SendMeshRequest sends a mesh command of the given cmd to the given internal mesh address and expects a response back from the server.

The server of the given address will invoke the command handler of the given cmd and sends back a response.

[IMPORTANT] This function must be used AFTER invoking diarkisexec.StartDiarkis()
[IMPORTANT] The command handler must be defined using diarkisexec.SetMeshCommandHandler(cmd uint16, handler MeshCommandHandler).

Error Cases

┌───────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Error             │ Reason                                                                  │
╞═══════════════════╪═════════════════════════════════════════════════════════════════════════╡
│ Invalid data type │ Input data type must be either a struct or map[string]interface{}.      │
│ Handler error     │ Handler function of the request returned an error.                      │
│ Network error     │ Mesh network error. Failed to send or receive server-to-server message. │
╘═══════════════════╧═════════════════════════════════════════════════════════════════════════╛

The diagram below show how SendMeshRequest works:

┌──────────┐ <1> Send request  ┌──────────┐
│ Server A │ ─────────────────▶︎│ Server B │
│          │ ◀︎──────────────── │          │
└──────────┘ <2> Send Response └──────────┘

Parameters

cmd       - Mesh command ID.
addresses - Internal mesh address of the server to send the command to.
data      - Data to be sent to the handler.
            The valid data type of data must be the following two types only:
            1. map[string]interface{}
            2. struct
callback  - The callback to be invoked when the response is received from the remote server.

func SetMeshCommandHandler

func SetMeshCommandHandler(cmd uint16, handler MeshCommandHandler)

SetMeshCommandHandler assigns a callback to the given mesh command handler to the given cmd.

[IMPORTANT] This function must be used BEFORE invoking diarkisexec.StartDiarkis()
[IMPORTANT] You may NOT assign a callback that is used by built-in internal mesh command handlers.
            Diarkis internally uses cmd from 0 to 10000.
[IMPORTANT] You may NOT assign multiple callbacks to the same cmd.

func SetMeshRPCHandler

func SetMeshRPCHandler(cmd uint16, handler MeshRPCHandler)

SetMeshRPCHandler assigns a callback to the given mesh RPC handler to the given cmd.

[IMPORTANT] This function must be used BEFORE invoking diarkisexec.StartDiarkis()
[IMPORTANT] You may NOT assign a callback that is used by built-in internal mesh RPC handlers.
            Diarkis internally uses cmd from 0 to 10000.
[IMPORTANT] You may NOT assign multiple callbacks to the same cmd.

Parameters

cmd      - RPC command ID.
handler  - Callback associated to the RPC ID.
           The handler callback will be passed two parameters:
           - data     []byte -   The byte array sent from another server node.
                                 The byte array can be encoded by mesh.TransportData.
                                 If mesh.TransPortData is used,
                                 you may decode data using mesh.TransportData.
           - senderAddr string - The private address of the sender server node.

Example of TransportData Decoding

In order to use TransportData, we must define each data structure of TransportData.

// We define the structure of the transport data once
var exampleTransportData = td.DefineTransportData([]td.Property{
  td.Property{ Name: "name",    Type: td.String },
  td.Property{ Name: "counter", Type: td.Int },
  td.Property{ Name: "list",    Type: td.Uint32Array },
  td.Property{ Name: "data",    Type: td.Bytes },
  td.Property{ Name: "users",   Type: td.BytesArray },
})

var userTransportData = td.DefineTransportData([]td.Property{
  td.Property{ Name: "UID", Type: td.String },
  td.Property{ Name: "SID", Type: td.String },
})

// We first create an instance of already defined mesh.TransportData.
// You must create the correct instance that corresponds with what SendRPC used to send the data.
td := exampleTransportData.New()

// We then pass the byte array the callback received.
td.Unpack(receivedBytes)

name,    nameFound    := td.GetAsString("name")
counter, counterFound := td.GetAsInt("counter")
list,    listFound    := td.GetAsUint32Array("list")
data,    dataFound    := td.GetAsBytes("data")

usersBytesList, usersBytesFound := td.GetAsBytesArray("users")

// We can reuse the same userTransportData instance here
ud := userTransportData.New()

for _, userBytes := range userBytesList {
  ud.Unpack(userBytes)

  // We handle each user UID and SID
  uid, uidFound := ud.GetAsString("uid")
  sid, sidFound := ud.GetAsString("sid")
}

func SetOnSharedDataRemove

func SetOnSharedDataRemove(cb func(key string))

SetOnSharedDataRemove assigns a callback to be invoked when a shared data is deleted.

[NOTE] To remove the assigned callback, use RemoveOnSharedDataRemove(cb func(key string)).

func SetOnSharedDataUpdate

func SetOnSharedDataUpdate(cb func(key string, value int16))

SetOnSharedDataUpdate assigns a callback to be invoked when a shared data is updated.

[NOTE] To remove the assigned callback use RemoveOnSharedDataUpdate(cb func(key string, value int16)).

func SetServerCommandHandler

func SetServerCommandHandler(ver uint8, cmd uint16, handler CommandHandler)

SetServerCommandHandler assigns a callback to the given command ver and cmd.

[IMPORTANT] This function must be invoked BEFORE calling diarkisexec.StartDiarkis()
[IMPORTANT] Diarkis' built-in commands use ver ranging from 0 to 1.
            You may NOT assign callbacks with those ver values.
[IMPORTANT] You may NOT assign a callback to the same ver and cmd combination.
[IMPORTANT] Attempting to assign multiple callbacks to the same ver and cmd combination
            as the built-in commands of Diarkis will result in panic.
            Even if you do not use any built-in commands by setting ExposeCommands to false,
            Diarkis start does NOT allow you to use built-in commands' ver and cmd.

func SetSharedData

func SetSharedData(key string, value int16) bool

SetSharedData updates a shared data to be propagated to all server nodes in the cluster.

The propagation of the shared data may take some time.

[IMPORTANT] The number of shared data keys you may store is limited to 10 keys.
[IMPORTANT] Updated value may suffer from race condition.
            If multiple server nodes attempt to update the same key,
            The value of the key maybe overwritten.

func SetupDiarkis

func SetupDiarkis(logConfigPath, meshConfigPath string, m *Modules)

SetupDiarkis initializes core and optional modules.

[NOTE] The configuration paths must be relative to the executable server binary file location.

func SetupDiarkisHTTPServer

func SetupDiarkisHTTPServer(config string)

SetupDiarkisHTTPServer declares the server to be HTTP server.

[IMPORTANT] A server cannot have multiple network protocols (HTTP, TCP, and UDP).

[NOTE] The configuration paths must be relative to the executable server binary file location.

func SetupDiarkisTCPServer

func SetupDiarkisTCPServer(config string)

SetupDiarkisTCPServer declares the server to be TCP server.

[IMPORTANT] A server cannot have multiple network protocols (HTTP, TCP, and UDP).

[NOTE] The configuration paths must be relative to the executable server binary file location.

func SetupDiarkisUDPServer

func SetupDiarkisUDPServer(config string)

SetupDiarkisUDPServer declares the server to be UDP server.

[IMPORTANT] A server cannot have multiple network protocols (HTTP, TCP, and UDP).

[NOTE] The configuration paths must be relative to the executable server binary file location.

func SetupNotificationService

func SetupNotificationService(name string, interval int64, callback func() (*Notification, error))

SetupNotificationService starts a notification service loop on the server.

The notification loop will execute the given callback at the given interval in seconds.

If the callback returns, *diarkisexec.Notification, the server will send the notification message along with ver and cmd to all the clients connected to the server.

[IMPORTANT] The server with HTTP role is not allowed to use Notifier.
[IMPORTANT] In order to deliver notifications, all server process must start the same notification service.
[IMPORTANT] This function does NOT guarantee the delivery of notification message to all user clients.
            The possible failure of notification message delivery may include the following:
              - Duplicate message ID will cause the message to be sent or ignored.
              - User clients that are not connected at the time of this function's execution.
              - User clients that are connected to non-responsive server process.
              - TTL expiration.

[NOTE]      The returned value of the callback will be sent as a notification.

Parameters

name     - A name of the notification service.
cb       - A callback to be invoked on each notification service loop tick. The returned values will be the notification.
           If the callback returns a nil, notification will not be sent to the clients.
interval - Notification delivery cycle interval in milliseconds

Example:

The code example blow demonstrates diarkisexec.SetupNotificationService fetches a notification data from a database every 60s and sets it up to be delivered to all connected users.

// Notification service will poll at every 60 seconds
interval := int64(60) // 60 seconds

diarkisexec.SetupNotificationService("Test", interval, func() (notification *diarkisexec.Notification, err error) {

	// Retrieve notification data from a database by the current time
	notificationData := someDatabase.GetNotificationDataByCurrentTime(year, month, date)

	if notificationData == nil {
		// No notification data to send out
		return nil, nil
	}

	n := &diarkisexec.Notification{}
	n.ID = notificationData.ID
	n.Name = notificationData.Name

	// Ver is used by the client to identify the message when received.
	n.Ver = notificationData.Ver

	// Cmd is used by the client to identify the message when received.
	n.Cmd = notificationData.Cmd

	n.Message = []byte("Notification message says 'Hello from the server'")

	// TTL is in seconds to indicate the availability of the notification data.
	// The notification will be available for the not-connected-clients for the duration of TTL and
	// will be sent to the clients when they connect before TTL expires.
	n.TTL = int64(60 * 60) // one hour

	return n, nil

})

func StartDiarkis

func StartDiarkis()

StartDiarkis starts the Diarkis server.

[IMPORTANT] This function blocks because it starts the process as a server,
            which means that no operations after calling of this function will be executed at all.
[IMPORTANT] SetupDiarkis must be called before calling StartDiarkis to properly setup Diarkis server.

type CommandHandler

CommandHandler callback for a UDP/TCP server command.

[IMPORTANT] At the end of callback operations, you MUST call next func(error).
            Not calling next func(error) will cause the server not to be able to handle the next incoming commands from the client.
type CommandHandler func(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error))

type MeshCommandHandler

MeshCommandHandler callback for a mesh command.

[IMPORTANT] In order to return a response value for SendMeshRequest, the handler function must return the response value as a byte array.
type MeshCommandHandler func(req map[string]interface{}) (response []byte, err error)

type MeshRPCHandler

MeshRPCHandler callback for a mesh command.

[IMPORTANT] In order to return a response value for SendMeshRPC, the handler function must return the response value as a byte array.
type MeshRPCHandler func(req []byte, senderAddr string) (response []byte, err error)

type Modules

Modules represents module declarations.

type Modules struct {
    Dive       *Options
    DM         *Options
    Field      *Options
    Group      *Options
    Room       *Options
    MatchMaker *Options
    Session    *Options
    Metrics    *Options
    Notifier   *Options
    // In order to use P2P module properly, you must provide enableP2P:true config to Diarkis UDP server.
    // P2P module is only compatible with Diarkis UDP server.
    P2P *Options
}

type Notification

Notification represents the notification data to be propagated to all connected Diarkis clients.

type Notification struct {
    // Message ID
    ID string
    // Name is used to associate the notification with a handler callback
    Name string
    // Message command ver to be sent to the user clients
    Ver uint8
    // Message command ID to be sent to the user clients
    Cmd uint16
    // Message to be sent to the user clients
    Message []byte
    // TTL of the message
    TTL int64
}

type Options

Options represents module configurations.

type Options struct {
    // Configuration file path for the module that needs configuration JSON file provided.
    // The path to configuration JSON file must be a relative to the server executable binary file location.
    ConfigPath string
    // If true, Diarkis server will expose built-in commands of the modules to the client.
    // There are modules that do not have built-in commands.
    ExposeCommands bool
}