...

Package server

import "github.com/Diarkis/diarkis/server"
Overview
Index
Subdirectories

Overview ▾

Package server ▷

Server - UDP/RUDP or TCP Server

Sets up a server as either UDP/RUDP or TCP server.

Configurations

When you deploy the Diarkis server cluster on k8s, each pod must acquire public host name.

Force Termination Message

[NOTE] This works for both UDP/RUDP and TCP server.

Environment variable name is DIARKIS_CLOUD_ENV

╭─────────────────╥───────────╮
│ Cloud Service   ║ Env Value │
╞═════════════════╬═══════════╡
│ Google Cloud    ║ GCP       │
├─────────────────╫───────────┤
│ AWS             ║ AWS       │
├─────────────────╫───────────┤
│ Microsoft Azure ║ AZURE     │
├─────────────────╫───────────┤
│ Tencent         ║ TENCENT   │
├─────────────────╫───────────┤
│ Alibaba Cloud   ║ ALIBABA   │
├─────────────────╫───────────┤
│ Linode          ║ LINODE    │
╰─────────────────╨───────────╯

You may also set host name or IP address to DIARKIS_CLOUD_ENV as shown below:

DIARKIS_CLOUD_ENV=$(host_or_ip_address)

Configurations

UDP/RUDP, TCP, and Connector configurations are explained below.

The server sends "force termination" message as long as the client sends message to the server with the connection.

UDP and RUDP Configurations

UDP/RUDP configurations detail

{
  "enableP2P": true,
  "address": "127.0.0.1",
  "nic": "eth0",
  "port": "7000",
  "connectionTTL": 10,
  "sendUDPInterval": 0,
  "handleRUDPInterval": 100,
  "rcvWorkers": 1,
  "retryInterval": 1000,
  "maxRetry": 10,
  "enableEncryption": true
}

▶︎ Configuration Values

enableP2P - Default is true

Enables UDP client to use P2P. Default is true.

address - Default is "127.0.0.1"

Address for UDP server to bind with.

nic - Default is "eth0"

Name of a network interface to retrieve address from.

port - Default is "7000"

Outbound port for the UDP/RUDP server to communicate with the client. The value of port will be used as the starting point if the given port is not available to look for available port automatically.

connectionTTL - Default value / minimum value is 10

RUDP connection TTL in seconds. Default is 10 seconds.

sendUDPInterval - Default is 0

UDP packet send interval in milliseconds. If set to lower than 10, outbound packets will not be buffered - No delay but higher CPU load.

handleRUDPInterval - Default is 100

RUDP packet send/receive interval in milliseconds.

This configuration controls the loop that handles all RUDP delivery and reception.

The lower the value of handleRUDPInterval is more sensitive (fast response) the server becomes, but at the expense of added CPU load.

rcvWorkers - Default is 1

Number of UDP packet receiver Goroutines. Default is the number of CPU cores.

retryInterval - Default is 1000

RUDP packet retry interval in milliseconds.

maxRetry - Default is 10

RUDP packet max retry attempts. When it exceeds this value, RUDP connection is considered timeout and discard.

enableEncryption - Default is true

Enable or disable packet encryption and decryption. This configuration must match the same configuration for HTTP server.

[IMPORTANT] If you are using Docker locally, you need to "use" external IP address of Docker.

[NOTE] You may use either address or nic in your configurations to run the server locally. If none is used, the default is localhost.

[NOTE] rcvWorker cannot be higher than the available CPU cores of the server.

More workers, you have the better in bound throughput, and higher CPU load.

TCP Configurations

When the server receives messages from the client that does not have a proper connection to the server (Server does not have the connection data), The server sends "force termination" message to the client the client.

TCP Configurations

TCP configurations details

{
  "connectionTTL": 10,
  "address": "127.0.0.1",
  "nic": "eth0",
  "port": "7200",
  "maxRcvSize": 8000,
  "noDelay": false,
  "enableEncryption": true
}

▶︎ Configuration Values

connectionTTL - Default is 10

Connection TTL in seconds. Default is 10 seconds.

address - Default is "127.0.0.1"

Address for TCP server to bind with.

nic - Default is "eth0"

Name of a network interface to retrieve address from.

port - Default is "7200"

Outbound port for the UDP/RUDP server to communicate with the client. The value of port will be used as the starting point if the given port is not available to look for available port automatically.

maxRcvSize - Default is 8000

Maximum TCP packet size of each request packet in bytes.

noDelay - Default is false

If true, it disables Nagle's algorithm.

enableEncryption - Default is true

Enable or disable packet encryption and decryption. This configuration must match the same configuration for HTTP server.

[IMPORTANT] If you are using Docker locally, you need to "use" external IP address of Docker.

[NOTE] You may use either address or nic in your configurations to run the server locally. If none is used, the default is localhost.

Server Response Status

A server response comes with a response status code. There are three status codes as follows:

When DIARKIS_USE_STRUCT_ERR=true env is used, the value of Bad and Error response code changes to indicate the use of struct error.

┌────────┬────────────────────────────────────────────────────────────────────────────────────────────────────┬──────┬────────────────────────┐
│ Status │ Description                                                                                        │ Code │ Code With Struct Error │
├────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼────────────────────────┤
│ OK     │ The command was successfully executed.                                                             │ 1    │                        │
├────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼────────────────────────┤
│ Bad    │ The command execution failed because of invalid command invocation such as invalid parameters etc. │ 4    │ 40                     │
├────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼────────────────────────┤
│ Error  │ The command failed to execute due to server failure.                                               │ 5    │ 50                     │
└────────┴────────────────────────────────────────────────────────────────────────────────────────────────────┴──────┴────────────────────────┘

Index ▾

Constants
func Command(ver uint8, cmd uint16, handler func(userData *user.User, next func(error)))
func DeleteNotification(id string)
func EnableNotifications()
func ExposeDebugCommands()
func GetEndPoint() string
func HandleCommand(ver uint8, cmd uint16, handler func(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error))) error
func HookAllCommands(handler func(userData *user.User, next func(error)))
func IsCommandDuplicate(ver uint8, commandID uint16) bool
func IsDebugCommandsEnabled() bool
func IsP2PEnabled() bool
func IsTCP() bool
func IsUDP() bool
func MarkServerAsTakenIf(callback func() bool)
func NotificationService(name string, cb NotificationLoader, interval int64)
func Notify(m *Notification, local bool) error
func Offline()
func OnDisconnect(callback func(string, *user.User))
func OnKeepAlive(handler func(userData *user.User, next func(error)))
func Online()
func SendPM(nodeAddr string, sid string, ver uint8, cmd uint16, message []byte)
func SetDebugCommandCallback(cmd uint16, cb func(*user.User, []byte, func(error, []byte)))
func SetOnNotification(name string, cb NotificationHandler)
func SetPublicEndPoint(addr string)
func SetupAsConnector(path string)
func SetupAsTCPServer(path string)
func SetupAsUDPServer(path string)
func SetupGenericCloudServer() error
func SetupServerForAWS() error
func SetupServerForAlibaba() error
func SetupServerForAzure() error
func SetupServerForGCP() error
func SetupServerForLinode() error
func SetupServerForTencent() error
func Taken()
type Notification
type NotificationHandler
type NotificationLoader
type UserDataDump

Constants

Bad Send status for server response.

const Bad = 4

Err Send status for server response.

const Err = 5

Ok Send status for server response.

const Ok = 1

TCPType a type of TCP server used by mesh module

const TCPType = tcp.Type

UDPType a type of UDP server used by mesh module

const UDPType = udp.Type

func Command

func Command(ver uint8, cmd uint16, handler func(userData *user.User, next func(error)))

Command has been deprecated: Use HandleCommand instead for other servers.

Deprecated

This function has been deprecated and will be removed in the future version without a warning.

ver     uint8  - Command version of the handler.
cmd     uint16 - Command ID of the handler.
handler func(userData *user.User, next func(error))

Returns an error if it fails to assign a handler.

NOTE: If you assign multiple handles with the same ver and cmd, all handlers will be executed in the order of assignment.

func DeleteNotification

func DeleteNotification(id string)

DeleteNotification deletes an existing Notification message struct data.

func EnableNotifications

func EnableNotifications()

EnableNotifications initializes notifier. It must be called before diarkis.Start is called to setup notifier properly. In order for notifications to be sent out to the clients, you must use Notify and/or NotificationService.

func ExposeDebugCommands

func ExposeDebugCommands()

ExposeDebugCommands exposes debug commands to the client. Server package comes with debug-only commands for tests and debugging.

Available Debug Commands:

▷ Set Server State Online

Sets the server to "ONLINE" state.

ONLINE State - The server accepts new client connections execute all diarkis functions.

version:          0
command:          900
Required payload: empty byte array
Response:         none

▷ Set Server State Taken

Sets the server to "TAKEN" state.

TAKEN State - The server does **NOT** accept new client connections, but able to execute all diarkis functions.

version:          0
command:          901
Required payload: empty byte array
Response:         none

▷ Set Server State Offline

Sets the server to "OFFLINE" state.

OFFLINE State - The server does **NOT** accept new client connections, and can **NOT** create new rooms, but able to join rooms and execute all other diarkis functions.

version:          0
command:          902
Required payload: empty byte array
Response:         none

▷ Server Terminate

Stops the server. Diarkis server waits for the connected clients

version:           0
command:          903
Required payload: empty byte array
Response:         none

▷ Room Data Dump

Dumps the room data to the client.

version:          0
command:          904
Required payload: empty byte array
Response:         byte array encoded room data dump as JSON

func GetEndPoint

func GetEndPoint() string

GetEndPoint returns the end point address the server is bound with

func HandleCommand

func HandleCommand(ver uint8, cmd uint16, handler func(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error))) error

HandleCommand registers a command handler function for TCP and UDP/RUDP server.

Returns an error if it fails to assign a handler.

[NOTE] If you assign multiple handles with the same ver and cmd,
       all handlers will be executed in the order of assignment.

Handler callback's next function:

The next function that the handler callback receives must be invoked at the end of all operations within the handler.

This ensures the handling of a command is completed and Diarkis server proceeds to the next handler of the same command ID or another command ID.

By blocking or not invoking the next function, Diarkis server will pause the handling of all commands for the user and eventually the user client will disconnect because it cannot handle echo (keep alive) command that every user client needs to stay connected to the server.

Parameters

ver     - Command version of the handler.
cmd     - Command ID of the handler.
handler - Handler function to be executed when the server receives the command.
          func(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error))

Handler Callback Parameters

ver     - Command ver sent from the client.
cmd     - Command ID sent from the client.
payload - Command payload sent from the client.
userData - User data representing the client that sent the command.
next    - The function to signal the command to move on to the next operation of the same ver and command ID.
          This function must be called at the end of all operations in the callback.
          If you pass an error, it will not proceed to the next operations of the same command ver and ID.

func HookAllCommands

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

HookAllCommands Registers a packet handler as a hook to all commands

handler func(userData *user.User, next func(error)) - Function to be invoked on command hook.

NOTE: The second argument next func(error) must be called at the end of handler to move the operation to next.

func IsCommandDuplicate

func IsCommandDuplicate(ver uint8, commandID uint16) bool

IsCommandDuplicate returns true if ver and command ID are used elsewhere when this function is invoked

ver uint8  - Command version to be checked for duplication.
cmd uint16 - Command ID to be checked for duplication.

func IsDebugCommandsEnabled

func IsDebugCommandsEnabled() bool

IsDebugCommandsEnabled returns true if debug commands are enabled

func IsP2PEnabled

func IsP2PEnabled() bool

IsP2PEnabled returns true if UDP server enabled P2P by its configuration

func IsTCP

func IsTCP() bool

IsTCP Returns true if the server is setup as TCP server

func IsUDP

func IsUDP() bool

IsUDP Returns true if the server is setup as UDP server

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 NotificationService

func NotificationService(name string, cb NotificationLoader, interval int64)

NotificationService starts a goroutine loop that invokes the given callback at the interval that is given. Every iteration of the goroutine loop triggers a notification message being sent.

[NOTE] The returned value of the callback will be sent as a notification.
[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.

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.
interval - Notification delivery cycle interval in milliseconds

Example:

The code example blow demonstrates server.NotificationService fetches a notification data from a database every 10,000ms and sets it up to be delivered to all connected users.

interval := int64(5000)

server.EnableNotifications()

server.NotificationService("Test", func() (notification *server.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 := new(server.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)

	return n, nil
}, interval)

func Notify

func Notify(m *Notification, local bool) error

Notify sends out a notification message for all user clients.

[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.

Parameters

name    - A notification name used to assign NotificationHandler.
m       - Message struct that represents a notification message.
local   - If true, the notification message will be sent to the user clients on the same server ONLY.

Returns an error if it fails to send out the notification.

func Offline

func Offline()

Offline flags the server to be offline (will be shutdown) and will not accept new users

func OnDisconnect

func OnDisconnect(callback func(string, *user.User))

OnDisconnect Registers a callback on connection termination for TCP/UDP

func OnKeepAlive

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

OnKeepAlive assigns a callback function on TCP heartbeat or UDP echo

handler - Function to be invoked on keep alive message from the clients.

[NOTE] next func(error) must be called at the end of handler's operation.

func Online

func Online()

Online flags the server to be online and will accept new users

func SendPM

func SendPM(nodeAddr string, sid string, ver uint8, cmd uint16, message []byte)

SendPM sends a direct message to another user - The sent private message is a reliable delivery.

Deprecated

This function has been deprecated and will be removed in the future version without a warning. Use DM module instead.

nodeAddr string - This is the node (server) internal address of the target user.
sid      string - This is the sid (session ID) of the target user.
ver      uint8  - This is the command version to be used with the message sent.
cmd      uint16 - This is the command version to be used with the message sent.
message  []byte - This is the message data byte array.

func SetDebugCommandCallback

func SetDebugCommandCallback(cmd uint16, cb func(*user.User, []byte, func(error, []byte)))

SetDebugCommandCallback assigns a callback for a debug command with the given debug command ID

func SetOnNotification

func SetOnNotification(name string, cb NotificationHandler)

SetOnNotification assigns a callback function to notifications with the matching name. The callback is invoked on every notification with the same name before it is sent to a client and decides who receives the notification by returning true/false. If the callback returns true, the target user will receive the notification.

[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure
                       in the callback closure will cause those pointers to be not garbage collected leading to memory leak.

func SetPublicEndPoint

func SetPublicEndPoint(addr string)

SetPublicEndPoint sets public end point to be sent to the client to be used to connect

addr string - This is the public endpoint for the server to be registered and used by the clients.

This is usually used internally.

func SetupAsConnector

func SetupAsConnector(path string)

SetupAsConnector Initialize the server as a Connector server for an external real-time server. This is meant to be used with an external real-time server such as Unreal Engine Dedicated server etc. Diarkis itself will not communicate wit the clients, but focuses on auto-scaling and horizontal scaling on K8s.

path string - This is the absolute path of the configuration file to read.

func SetupAsTCPServer

func SetupAsTCPServer(path string)

SetupAsTCPServer Initialize as a TCP server. Load configurations from the file path given. Pass an empty string, if there is no need for configurations.

path string - This is the absolute path of the configuration file to read.

func SetupAsUDPServer

func SetupAsUDPServer(path string)

SetupAsUDPServer Initialize as a UDP server. Load configurations from the file path given. Pass an empty string, if there is no need for configurations.

path string - This is the absolute path of the configuration file to read.

func SetupGenericCloudServer

func SetupGenericCloudServer() error

SetupGenericCloudServer sets public endpoint in a generic way Must be called in diarkis.OnReady() as a callback

func SetupServerForAWS

func SetupServerForAWS() error

SetupServerForAWS sets public endpoint in AWS environment Must be called in diarkis.OnReady() as a callback

func SetupServerForAlibaba

func SetupServerForAlibaba() error

SetupServerForAlibaba sets public endpoint in Alibaba Cloud environment Must be called in diarkis.OnReady() as a callback

func SetupServerForAzure

func SetupServerForAzure() error

SetupServerForAzure sets public endpoint in Microsoft Azure environment Must be called in diarkis.OnReady() as a callback

func SetupServerForGCP

func SetupServerForGCP() error

SetupServerForGCP sets public endpoint in GCP environment Must be called in diarkis.OnReady() as a callback

func SetupServerForLinode

func SetupServerForLinode() error

SetupServerForLinode sets public endpoint in Linode environment Must be called in diarkis.OnReady() as a callback

func SetupServerForTencent

func SetupServerForTencent() error

SetupServerForTencent sets public endpoint in Tencent Cloud environment Must be called in diarkis.OnReady() as a callback

func Taken

func Taken()

Taken flags the server to be taken and will not accept new users.

type Notification

Notification is a data structure that represents a notification message.

ID      - Unique Identifier for the server notification. This ID must not overlap.
Ver     - Command version of the notification. If ver = 0 and cmd = 400, the client raises OnNotification event.
Cmd     - Command ID of the notification. If ver = 0 and cmd = 400, the client raises OnNotification event.
Message - Notification message
TTL     - Notification TTL in milliseconds.
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 NotificationHandler

NotificationHandler is invoked on every Notify. If it returns true, the user client receives the notification, but if it returns false, the client user does not receive the notification.

type NotificationHandler func(userData *user.User, notification *Notification) bool

type NotificationLoader

NotificationLoader is a callback to used by NotificationService to generate a notification message.

type NotificationLoader func() (notification *Notification, err error)

type UserDataDump

UserDataDump implies a payload format for the dump user data command

type UserDataDump struct {
    ID      string                 `json:"ID"`
    SID     string                 `json:"SID"`
    Data    map[string]interface{} `json:"Data"`
    Latency int64                  `json:"Latency"`
}

Subdirectories

Name Synopsis
..
connector
http Package http ▷
tcp
udp