Start setting up the WebSocket reader/writer.

main
Mari 3 years ago
parent 072b410a64
commit b4b5946352
  1. 34
      server/action/client.go
  2. 4
      server/room/clientmessage.go
  3. 93
      server/websocket/client.go
  4. 35
      server/websocket/server.go
  5. 53
      server/websocket/shared.go

@ -1,34 +0,0 @@
package action
import (
"go.uber.org/zap/zapcore"
)
// ClientHello is the action sent by the client when it first establishes the connection.
type ClientHello struct {
// Version is the protocol version the client is running.
Version int `json:"version"`
}
// ClientRefresh is the action sent by the client when it needs the full state re-sent.
type ClientRefresh struct {
}
// IDed contains a pair of ID and Action, as sent by the server.
type IDed struct {
// ID contains the arbitrary ID that was sent by the client, for identifying the action in future messages.
ID int `json:"id"`
// Action contains the action that was actually being sent.
Action Syncable `json:"action"`
}
func (i IDed) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddInt("id", i.ID)
return encoder.AddObject("action", i.Action)
}
// ClientSent is an action sent in order to deliver one or more Syncable actions to the server.
type ClientSent struct {
// Actions contains the actions the client wants to apply, in the order they should be applied.
Actions []IDed `json:"nested"`
}

@ -3,7 +3,7 @@ package room
import (
"github.com/rs/xid"
"go.uber.org/zap/zapcore"
"hexmap-server/action"
"hexmap-server/websocket"
)
// ClientMessage marks messages coming from clients to the room.
@ -62,7 +62,7 @@ func (r RefreshRequest) ClientID() xid.ID {
// websocket.
type ApplyRequest struct {
id xid.ID
action action.IDed
action websocket.IDed
}
func (f ApplyRequest) MarshalLogObject(encoder zapcore.ObjectEncoder) error {

@ -0,0 +1,93 @@
package websocket
import (
"go.uber.org/zap/zapcore"
"hexmap-server/action"
)
// ClientMessageType is an enum type for the client's protocol messages.
type ClientMessageType string
const (
ClientHelloType ClientMessageType = "HELLO"
ClientRefreshType ClientMessageType = "REFRESH"
ClientActType ClientMessageType = "ACT"
ClientGoodbyeType ClientMessageType = GoodbyeType
)
// ClientMessage s are those sent by the client.
type ClientMessage interface {
zapcore.ObjectMarshaler
// ClientType gives the type constant that will be sent on or read from the wire.
ClientType() ClientMessageType
}
// ClientHello is the action sent by the client when it first establishes the connection.
type ClientHello struct {
// Version is the protocol version the client is running.
Version int `json:"version"`
}
func (c ClientHello) ClientType() ClientMessageType {
return ClientHelloType
}
func (c ClientHello) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddString("type", string(ClientHelloType))
encoder.AddInt("version", c.Version)
return nil
}
// ClientRefresh is the action sent by the client when it needs the full state re-sent.
type ClientRefresh struct {
}
func (c ClientRefresh) ClientType() ClientMessageType {
return ClientRefreshType
}
func (c ClientRefresh) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddString("type", string(ClientRefreshType))
return nil
}
// IDed contains a pair of ID and Action, as sent by the client.
type IDed struct {
// ID contains the arbitrary ID that was sent by the client, for identifying the action in future messages.
ID int `json:"id"`
// Action contains the action that was actually being sent.
Action action.Syncable `json:"action"`
}
func (i IDed) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddInt("id", i.ID)
return encoder.AddObject("action", i.Action)
}
type IDPairs []IDed
func (a IDPairs) MarshalLogArray(encoder zapcore.ArrayEncoder) error {
var finalErr error = nil
for _, v := range a {
err := encoder.AppendObject(v)
if err != nil && finalErr == nil {
finalErr = err
}
}
return finalErr
}
// ClientAct is an action sent in order to deliver one or more Syncable actions to the server.
type ClientAct struct {
// Actions contains the actions the client wants to apply, in the order they should be applied.
Actions IDPairs `json:"actions"`
}
func (c ClientAct) ClientType() ClientMessageType {
return ClientActType
}
func (c ClientAct) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddString("type", string(ClientActType))
return encoder.AddArray("actions", c.Actions)
}

@ -1,6 +1,31 @@
package action
package websocket
import "hexmap-server/state"
import (
"go.uber.org/zap/zapcore"
"hexmap-server/action"
"hexmap-server/state"
)
// TODO: Make all the ServerMessages implement ServerMessage.
// ServerMessageType is an enum type for the server's messages.
type ServerMessageType string
const (
ServerHelloType ServerMessageType = "HELLO"
ServerRefreshType ServerMessageType = "REFRESH"
ServerOKType ServerMessageType = "OK"
ServerFailedType ServerMessageType = "FAILED"
ServerActType ServerMessageType = "ACT"
ServerGoodbyeType ServerMessageType = GoodbyeType
)
// ServerMessage s are sent by the server to the client.
type ServerMessage interface {
zapcore.ObjectMarshaler
// ServerType returns the type constant that will be sent on the wire.
ServerType() ServerMessageType
}
// ServerHello is the action sent to establish the current state of the server when a new client connects.
type ServerHello struct {
@ -34,9 +59,9 @@ type ServerFailed struct {
Error string `json:"error"`
}
// ServerSent is the action sent when one or more client actions from other clients have been accepted and applied.
// ServerAct is the action sent when one or more client actions from other clients have been accepted and applied.
// The client's own actions will never be included in this action.
type ServerSent struct {
type ServerAct struct {
// Actions contains the actions that are now being applied.
Actions []Syncable `json:"actions"`
Actions []action.Syncable `json:"actions"`
}

@ -0,0 +1,53 @@
package websocket
import "go.uber.org/zap/zapcore"
// StatusCode is the code used by the WebSocket protocol to signal the other side on close.
type StatusCode int16
const (
StatusNormal StatusCode = 1000
StatusGoingAway StatusCode = 1001
StatusProtocolError StatusCode = 1002
StatusTooBig StatusCode = 1009
StatusProtocolVersionOutOfDate StatusCode = 4000
GoodbyeType = "GOODBYE"
)
// TODO: Noting that there should be three channels in play:
// 1) Reader to client: to receive messages from the connection
// 2) Client to writer: to send messages on the connection
// 3) Writer to reader: indicating that it is about to send a close message, and the reader should wait for one and
// time out the connection if it takes too long.
// SocketClosed is synthesized when a client closes the WebSocket connection, or sent to the write process to write a
// WebSocket close message.
// Sending a SocketClosed on a channel causes that channel to be closed right after.
type SocketClosed struct {
// Code is the StatusCode given (or which should be given) in the close message.
Code StatusCode
// Text is the reason text given (or which should be given) in the close message. Max 123 characters.
Text string
// Error may be an error that resulted in the closure of the socket.
// Will not be written by the writer; only useful when it's returned from the reader.
Error error
}
func (c SocketClosed) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
encoder.AddString("type", GoodbyeType)
encoder.AddInt16("code", int16(c.Code))
encoder.AddString("text", c.Text)
if c.Error != nil {
encoder.AddString("error", c.Error.Error())
}
return nil
}
func (c SocketClosed) ClientType() ClientMessageType {
return ClientGoodbyeType
}
func (c SocketClosed) ServerType() ServerMessageType {
return ServerGoodbyeType
}
Loading…
Cancel
Save