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(data map[string]interface{}) ([]byte, error)
CreateReturnBytes converts the given map[string]interface{} into a byte error for MeshCommandHandler return value.
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(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(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(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(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() 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(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() 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(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() 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(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(key string) (int16, bool)
GetSharedData returns synchronized shared data by its key.
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(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() 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() 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() 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() 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(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()
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()
MarkServerAsOnline flags the server as "ONLINE".
[NOTE] "ONLINE" is the default state of the server.
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(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(name string) *log.Logger
NewLogger creates a new instance of log.Logger.
[IMPORTANT] This function must be invoked BEFORE calling diarkisexec.StartDiarkis()
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(cb func(key string))
RemoveOnSharedDataRemove removes the given callback that has been assigned by SetOnSharedDataRemove.
[IMPORTANT] This function uses mutex lock internally.
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(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() uint8
ResponseStatusBad returns UDP or TCP server response status code for invalid command invocation.
func ResponseStatusErr() uint8
ResponseStatusErr returns UDP or TCP server response status code for server error while handling command.
func ResponseStatusOK() uint8
ResponseStatusOK returns UDP or TCP server response status code for successfully handled command.
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(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(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(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(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.
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(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(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(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(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(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(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(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(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(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()
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.
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))
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)
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)
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 }
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 }
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 }