parent
fe5fc0657f
commit
a83c9688bb
@ -1,18 +1,109 @@ |
|||||||
package client |
package client |
||||||
|
|
||||||
import ( |
import ( |
||||||
|
"context" |
||||||
|
"git.reya.zone/reya/hexmap/build" |
||||||
|
"git.reya.zone/reya/hexmap/proto" |
||||||
"github.com/magefile/mage/mg" |
"github.com/magefile/mage/mg" |
||||||
|
"github.com/magefile/mage/target" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path/filepath" |
||||||
) |
) |
||||||
|
|
||||||
const ProtocPluginPath = "client/node_modules/.bin/protoc-gen-ts_proto" |
func NPMInstall(ctx context.Context) error { |
||||||
|
var packageLockOutOfDate, nodeModulesOutOfDate bool |
||||||
|
var err error |
||||||
|
if packageLockOutOfDate, err = target.Path("client/package-lock.json", "client/package-lock.json"); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if nodeModulesOutOfDate, err = target.Dir("client/node_modules", "client/package.json"); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if !(packageLockOutOfDate || nodeModulesOutOfDate) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
cmd := exec.CommandContext(ctx, "npm", "install") |
||||||
|
cmd.Dir, err = filepath.Abs("client") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
cmd.Stderr = os.Stderr |
||||||
|
if mg.Verbose() { |
||||||
|
cmd.Stdout = os.Stdout |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func ProtocFlags() ([]string, error) { |
||||||
|
buildPath, err := filepath.Abs(filepath.Join(build.ToolsDir, "protoc-gen-ts_proto")) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return []string{ |
||||||
|
"--plugin=" + buildPath, |
||||||
|
"--ts_proto_out=client/src/proto/", |
||||||
|
"--ts_proto_opt=env=browser", |
||||||
|
"--ts_proto_opt=esModuleInterop=true", |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
type Protobuf mg.Namespace |
type Protobuf mg.Namespace |
||||||
|
|
||||||
// TODO: NPMInstall
|
const TSPluginNPMLocation = "client/node_modules/.bin/protoc-gen-ts_proto" |
||||||
// TODO: Protobuf:InstallTSPlugin
|
|
||||||
// TODO: Protobuf:InstallPlugins
|
func (Protobuf) InstallTSPlugin(ctx context.Context) error { |
||||||
// TODO: Protobuf:Build
|
mg.CtxDeps(ctx, NPMInstall) |
||||||
// TODO: Protobuf:Clean
|
buildPath, err := filepath.Abs(filepath.Join(build.ToolsDir, "protoc-gen-ts_proto")) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
sourcePath, err := filepath.Abs(TSPluginNPMLocation) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
// Errors here just mean we move on to the next step - this could not exist or not be a link, we don't care.
|
||||||
|
// The important part is the later parts.
|
||||||
|
if linkPath, err := os.Readlink(buildPath); err == nil && linkPath == sourcePath { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Remove whatever's in the way, if necessary.
|
||||||
|
err = os.Remove(buildPath) |
||||||
|
if err != nil && !os.IsNotExist(err) { |
||||||
|
return err |
||||||
|
} |
||||||
|
err = os.Symlink(sourcePath, buildPath) |
||||||
|
if err != nil && !os.IsExist(err) { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (Protobuf) MakeProtoDir() error { |
||||||
|
if err := os.Mkdir("client/src/proto", 0755); err != nil && !os.IsExist(err) { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (Protobuf) InstallPlugins(ctx context.Context) error { |
||||||
|
mg.CtxDeps(ctx, Protobuf.InstallTSPlugin, Protobuf.MakeProtoDir) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (Protobuf) Build(ctx context.Context) error { |
||||||
|
mg.SerialCtxDeps(ctx, Protobuf.Clean, Protobuf.InstallPlugins) |
||||||
|
return proto.Compile(ctx, []proto.ProtocFlagsFunc{ProtocFlags}) |
||||||
|
} |
||||||
|
|
||||||
|
func (Protobuf) Clean() error { |
||||||
|
err := os.RemoveAll("client/src/proto") |
||||||
|
if err != nil && !os.IsNotExist(err) { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
// TODO: Build
|
// TODO: Build
|
||||||
// TODO: Clean
|
// TODO: Clean
|
||||||
// TODO: Serve
|
// TODO: Serve
|
||||||
|
@ -1,176 +0,0 @@ |
|||||||
import { |
|
||||||
CLIENT_ACT, |
|
||||||
ClientAction, |
|
||||||
ClientActAction, |
|
||||||
isClientActAction, |
|
||||||
SendableAction, |
|
||||||
SentAction, |
|
||||||
SERVER_FAILED, |
|
||||||
SERVER_GOODBYE, |
|
||||||
SERVER_HELLO, |
|
||||||
SERVER_OK, |
|
||||||
SERVER_ACT, |
|
||||||
SERVER_SOCKET_STARTUP, |
|
||||||
ServerAction, |
|
||||||
SocketState |
|
||||||
} from "../../actions/NetworkAction"; |
|
||||||
import {HexagonOrientation, HexMapRepresentation, initializeMap, LineParity} from "../../state/HexMap"; |
|
||||||
import {ReactElement, useContext, useEffect, useRef, useState} from "react"; |
|
||||||
import {DispatchContext} from "../context/DispatchContext"; |
|
||||||
import {USER_ACTIVE_COLOR} from "../../actions/UserAction"; |
|
||||||
import {CELL_COLOR} from "../../actions/CellAction"; |
|
||||||
|
|
||||||
export enum OrientationConstants { |
|
||||||
ROWS = "ROWS", |
|
||||||
COLUMNS = "COLUMNS", |
|
||||||
EVEN_ROWS = "EVEN_ROWS", |
|
||||||
EVEN_COLUMNS = "EVEN_COLUMNS" |
|
||||||
} |
|
||||||
export function orientationFromString(string: string): HexMapRepresentation { |
|
||||||
const normalized = string.toUpperCase().trim() |
|
||||||
switch (normalized) { |
|
||||||
case OrientationConstants.ROWS: |
|
||||||
return { orientation: HexagonOrientation.POINTY_TOP, indentedLines: LineParity.ODD } |
|
||||||
case OrientationConstants.COLUMNS: |
|
||||||
return { orientation: HexagonOrientation.FLAT_TOP, indentedLines: LineParity.ODD } |
|
||||||
case OrientationConstants.EVEN_ROWS: |
|
||||||
return { orientation: HexagonOrientation.POINTY_TOP, indentedLines: LineParity.EVEN } |
|
||||||
case OrientationConstants.EVEN_COLUMNS: |
|
||||||
return { orientation: HexagonOrientation.FLAT_TOP, indentedLines: LineParity.EVEN } |
|
||||||
default: |
|
||||||
return { orientation: HexagonOrientation.POINTY_TOP, indentedLines: LineParity.ODD } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** Fake "connection" to a "server" that actually just goes back and forth with the console. */ |
|
||||||
export class ConsoleConnection { |
|
||||||
public receivedMessages: ClientAction[] = [] |
|
||||||
private readonly dispatch: (action: ServerAction) => void |
|
||||||
|
|
||||||
constructor(dispatch: (action: ServerAction) => void) { |
|
||||||
this.dispatch = dispatch |
|
||||||
} |
|
||||||
|
|
||||||
receive(action: ClientAction): void { |
|
||||||
this.receivedMessages.push(action) |
|
||||||
if (isClientActAction(action)) { |
|
||||||
console.log(`Received Sent action containing: ${action.actions.map((value) => `${value.id}/${value.action.type}`).join(", ")}`) |
|
||||||
} else { |
|
||||||
console.log(`Received: ${action.type}`) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public sendSocketConnecting(): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_SOCKET_STARTUP, |
|
||||||
state: SocketState.CONNECTING |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendSocketConnected(): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_SOCKET_STARTUP, |
|
||||||
state: SocketState.OPEN |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendHello({color = "#0000FF", displayMode = "ROWS", xid = "TotallyCoolXID", lines = 10, cells = 10}: { |
|
||||||
color?: string, |
|
||||||
displayMode?: string, |
|
||||||
xid?: string, |
|
||||||
lines?: number, |
|
||||||
cells?: number |
|
||||||
} = {}): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_HELLO, |
|
||||||
version: 1, |
|
||||||
state: { |
|
||||||
map: initializeMap({ |
|
||||||
lines, |
|
||||||
cellsPerLine: cells, |
|
||||||
displayMode: orientationFromString(displayMode), |
|
||||||
xid |
|
||||||
}), |
|
||||||
user: {activeColor: color} |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendOK(ids: readonly number[]): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_OK, |
|
||||||
ids |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendFailed(ids: readonly number[], error: string = "No thanks."): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_FAILED, |
|
||||||
ids, |
|
||||||
error |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendGoodbye({code = 1000, reason = "Okay, bye then!"}: { code?: number, reason?: string } = {}): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_GOODBYE, |
|
||||||
code, |
|
||||||
reason, |
|
||||||
currentTime: new Date() |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendColorChange(color: string = "#FF0000FF"): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_ACT, |
|
||||||
actions: [{ |
|
||||||
type: USER_ACTIVE_COLOR, |
|
||||||
color |
|
||||||
}] |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
public sendColorAtTile(color: string = "#FFFF00FF", line: number = 0, cell: number = 0): void { |
|
||||||
this.dispatch({ |
|
||||||
type: SERVER_ACT, |
|
||||||
actions: [{ |
|
||||||
type: CELL_COLOR, |
|
||||||
at: { line, cell }, |
|
||||||
color |
|
||||||
}] |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
export function ConsoleConnector({specialMessage, pendingMessages, nextID}: {specialMessage: ClientAction|null, pendingMessages: readonly SendableAction[], nextID: number}): ReactElement { |
|
||||||
const dispatch = useContext(DispatchContext) |
|
||||||
const connector = useRef(new ConsoleConnection(dispatch || (() => null))) |
|
||||||
const [lastSpecialMessage, setLastSpecialMessage] = useState<ClientAction|null>(null) |
|
||||||
useEffect(() => { |
|
||||||
// @ts-ignore
|
|
||||||
window.fakedServerConnection = connector.current |
|
||||||
}, []); |
|
||||||
useEffect(() => { |
|
||||||
if (dispatch !== null) { |
|
||||||
if (pendingMessages.length > 0) { |
|
||||||
const sentMessages: SentAction[] = pendingMessages.map((action, index) => { |
|
||||||
return { id: index + nextID, action } |
|
||||||
}); |
|
||||||
const sentMessage: ClientActAction = { |
|
||||||
type: CLIENT_ACT, |
|
||||||
actions: sentMessages |
|
||||||
}; |
|
||||||
connector.current.receive(sentMessage) |
|
||||||
dispatch(sentMessage) |
|
||||||
} |
|
||||||
} |
|
||||||
}, [nextID, dispatch, pendingMessages]) |
|
||||||
useEffect(() => { |
|
||||||
if (specialMessage !== null && specialMessage !== lastSpecialMessage) { |
|
||||||
connector.current.receive(specialMessage); |
|
||||||
setLastSpecialMessage(specialMessage); |
|
||||||
} |
|
||||||
}, [specialMessage, lastSpecialMessage, setLastSpecialMessage]) |
|
||||||
return <div className="consoleConnector">Console connection active</div> |
|
||||||
} |
|
@ -1,7 +1,3 @@ |
|||||||
export function arrayShallowEqual<T>(left: readonly T[], right: readonly T[]): boolean { |
|
||||||
return left.length === right.length && arrayShallowStartsWith(left, right) |
|
||||||
} |
|
||||||
|
|
||||||
export function arrayShallowStartsWith<T>(target: readonly T[], prefix: readonly T[]): boolean { |
export function arrayShallowStartsWith<T>(target: readonly T[], prefix: readonly T[]): boolean { |
||||||
return target.length >= prefix.length && prefix.every((value, index) => target[index] === value) |
return target.length >= prefix.length && prefix.every((value, index) => target[index] === value) |
||||||
} |
} |
@ -1,12 +1,12 @@ |
|||||||
#!/bin/bash |
#!/bin/bash |
||||||
|
|
||||||
MAINPATH=$(readlink -e ${BASH_SOURCE%/*}) |
SCRIPTPATH=$(readlink -e ${BASH_SOURCE}) |
||||||
|
MAINPATH=${SCRIPTPATH%/*} |
||||||
BUILDTOOLSPATH=${MAINPATH}/buildtools |
BUILDTOOLSPATH=${MAINPATH}/buildtools |
||||||
MAGEPATH=${BUILDTOOLSPATH}/mage |
MAGEPATH=${BUILDTOOLSPATH}/mage |
||||||
|
|
||||||
if [[ ! -x "$MAGEPATH" ]]; then |
if [[ ! -x "$MAGEPATH" ]]; then |
||||||
echo "go install-ing mage..." |
echo "go install-ing mage..." |
||||||
GOBIN="$BUILDTOOLSPATH" go install github.com/magefile/mage@latest |
GOBIN="$BUILDTOOLSPATH" go install github.com/magefile/mage@latest |
||||||
fi |
fi |
||||||
|
|
||||||
exec "$MAGEPATH" -d "$MAINPATH" -w "$MAINPATH" "$@" |
exec "$MAGEPATH" -d "$MAINPATH" -w "$MAINPATH" "$@" |
||||||
|
@ -0,0 +1,56 @@ |
|||||||
|
package proto |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"github.com/magefile/mage/mg" |
||||||
|
"io/fs" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path/filepath" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
func BaseProtocFlags() ([]string, error) { |
||||||
|
return []string{"-I=proto"}, nil |
||||||
|
} |
||||||
|
|
||||||
|
type ProtocFlagsFunc func() ([]string, error) |
||||||
|
|
||||||
|
func Sources() ([]string, error) { |
||||||
|
result := []string(nil) |
||||||
|
err := filepath.WalkDir("proto", func(path string, d fs.DirEntry, err error) error { |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if !d.IsDir() && strings.HasSuffix(path, ".proto") { |
||||||
|
result = append(result, path) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}) |
||||||
|
return result, err |
||||||
|
} |
||||||
|
|
||||||
|
func Compile(ctx context.Context, withPlugins []ProtocFlagsFunc) error { |
||||||
|
flags, err := BaseProtocFlags() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
for _, flagsFunc := range withPlugins { |
||||||
|
pluginFlags, err := flagsFunc() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
flags = append(flags, pluginFlags...) |
||||||
|
} |
||||||
|
protoFiles, err := Sources() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
args := append(flags, protoFiles...) |
||||||
|
cmd := exec.CommandContext(ctx, "protoc", args...) |
||||||
|
cmd.Stderr = os.Stderr |
||||||
|
if mg.Verbose() { |
||||||
|
cmd.Stdout = os.Stdout |
||||||
|
} |
||||||
|
return cmd.Run() |
||||||
|
} |
@ -1,41 +1,38 @@ |
|||||||
syntax = "proto3"; |
syntax = "proto3"; |
||||||
|
|
||||||
import "action.proto"; |
import "action.proto"; |
||||||
import "map.proto"; |
import "state.proto"; |
||||||
import "user.proto"; |
|
||||||
|
|
||||||
option go_package = "git.reya.zone/reya/hexmap/server/websocket"; |
option go_package = "git.reya.zone/reya/hexmap/server/websocket"; |
||||||
|
|
||||||
message ServerState { |
message ServerHelloPB { |
||||||
HexMap map = 1; |
|
||||||
UserState user = 2; |
|
||||||
} |
|
||||||
|
|
||||||
message ServerHello { |
|
||||||
uint32 version = 1; |
uint32 version = 1; |
||||||
ServerState state = 2; |
SyncableStatePB state = 2; |
||||||
} |
} |
||||||
|
|
||||||
message ServerRefresh { |
message ServerRefreshPB { |
||||||
ServerState state = 1; |
SyncableStatePB state = 1; |
||||||
} |
} |
||||||
|
|
||||||
message ServerOK { |
message ServerOKPB { |
||||||
repeated uint32 ids = 1; |
repeated uint32 ids = 1; |
||||||
} |
} |
||||||
|
|
||||||
message ServerFailed { |
message ServerFailedPB { |
||||||
repeated uint32 ids = 1; |
repeated uint32 ids = 1; |
||||||
string error = 2; |
string error = 2; |
||||||
} |
} |
||||||
|
|
||||||
message ServerAction { |
message ServerActPB { |
||||||
oneof action { |
repeated ServerActionPB actions = 1; |
||||||
CellSetColor cell_set_color = 1; |
|
||||||
UserSetActiveColor user_set_active_color = 2; |
|
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
message ServerAct { |
message ServerCommandPB { |
||||||
repeated ServerAction actions = 1; |
oneof command { |
||||||
|
ServerHelloPB hello = 1; |
||||||
|
ServerRefreshPB refresh = 2; |
||||||
|
ServerOKPB ok = 3; |
||||||
|
ServerFailedPB failed = 4; |
||||||
|
ServerActPB act = 5; |
||||||
|
} |
||||||
} |
} |
@ -0,0 +1,11 @@ |
|||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
option go_package = "git.reya.zone/reya/hexmap/server/state"; |
||||||
|
|
||||||
|
import "map.proto"; |
||||||
|
import "user.proto"; |
||||||
|
|
||||||
|
message SyncableStatePB { |
||||||
|
HexMapPB map = 1; |
||||||
|
UserStatePB user = 2; |
||||||
|
} |
@ -1,2 +1,2 @@ |
|||||||
*.pb.go |
*.pb.go |
||||||
*.zap.go |
proto/ |
@ -0,0 +1,75 @@ |
|||||||
|
package action |
||||||
|
|
||||||
|
import "git.reya.zone/reya/hexmap/server/state" |
||||||
|
|
||||||
|
func (x *ServerActionPB) ToGo() (Server, error) { |
||||||
|
if x == nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
switch action := x.Action.(type) { |
||||||
|
case *ServerActionPB_Client: |
||||||
|
return action.Client.ToGo() |
||||||
|
default: |
||||||
|
panic("A case was missed in ServerActionPB.ToGo!") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ClientActionPB) ToGo() (Client, error) { |
||||||
|
if x == nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
switch action := x.Action.(type) { |
||||||
|
case *ClientActionPB_CellSetColor: |
||||||
|
return action.CellSetColor.ToGo() |
||||||
|
case *ClientActionPB_UserSetActiveColor: |
||||||
|
return action.UserSetActiveColor.ToGo(), nil |
||||||
|
default: |
||||||
|
panic("A case was missed in ClientActionPB.ToGo!") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *CellSetColorPB) ToGo() (*CellColor, error) { |
||||||
|
at, err := x.At.ToGo() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &CellColor{ |
||||||
|
At: at, |
||||||
|
Color: state.ColorFromInt(x.Color), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c CellColor) ToServerPB() *ServerActionPB { |
||||||
|
return serverPBFromClient(c) |
||||||
|
} |
||||||
|
|
||||||
|
func (c CellColor) ToClientPB() *ClientActionPB { |
||||||
|
return &ClientActionPB{ |
||||||
|
Action: &ClientActionPB_CellSetColor{ |
||||||
|
CellSetColor: &CellSetColorPB{ |
||||||
|
Color: c.Color.ToInt(), |
||||||
|
At: c.At.ToPB(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *UserSetActiveColorPB) ToGo() *UserActiveColor { |
||||||
|
return &UserActiveColor{ |
||||||
|
Color: state.ColorFromInt(x.Color), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (c UserActiveColor) ToServerPB() *ServerActionPB { |
||||||
|
return serverPBFromClient(c) |
||||||
|
} |
||||||
|
|
||||||
|
func (c UserActiveColor) ToClientPB() *ClientActionPB { |
||||||
|
return &ClientActionPB{ |
||||||
|
Action: &ClientActionPB_UserSetActiveColor{ |
||||||
|
UserSetActiveColor: &UserSetActiveColorPB{ |
||||||
|
Color: c.Color.ToInt(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
@ -1,45 +0,0 @@ |
|||||||
package action |
|
||||||
|
|
||||||
import ( |
|
||||||
"git.reya.zone/reya/hexmap/server/state" |
|
||||||
"go.uber.org/zap/zapcore" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
CellColorType Type = "CELL_COLOR" |
|
||||||
) |
|
||||||
|
|
||||||
// CellColor is the action sent when a cell of the map has been colored a different color.
|
|
||||||
type CellColor struct { |
|
||||||
// At is the location of the cell in storage coordinates.
|
|
||||||
At state.StorageCoordinates `json:"at"` |
|
||||||
// Color is the color the cell has been changed to.
|
|
||||||
Color state.HexColor `json:"color"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c CellColor) MarshalLogObject(encoder zapcore.ObjectEncoder) error { |
|
||||||
encoder.AddString("type", string(CellColorType)) |
|
||||||
err := encoder.AddObject("at", c.At) |
|
||||||
encoder.AddString("color", c.Color.String()) |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
func (c CellColor) Type() Type { |
|
||||||
return CellColorType |
|
||||||
} |
|
||||||
|
|
||||||
// Apply sets the target cell's color, or returns ErrNoOp if it can't.
|
|
||||||
func (c CellColor) Apply(s *state.Synced) error { |
|
||||||
if c.Color.A < 0xF { |
|
||||||
return ErrNoTransparentColors |
|
||||||
} |
|
||||||
cell, err := s.Map.LineCells.GetCellAt(c.At) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
if cell.Color == c.Color { |
|
||||||
return ErrNoOp |
|
||||||
} |
|
||||||
cell.Color = c.Color |
|
||||||
return nil |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
package action |
|
||||||
|
|
||||||
import ( |
|
||||||
"git.reya.zone/reya/hexmap/server/state" |
|
||||||
"go.uber.org/zap/zapcore" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
UserActiveColorType = "USER_ACTIVE_COLOR" |
|
||||||
) |
|
||||||
|
|
||||||
// UserActiveColor is the action sent when the user's current color, the one being painted with, changes.
|
|
||||||
type UserActiveColor struct { |
|
||||||
// Color is the color that is now active.
|
|
||||||
Color state.HexColor `json:"color"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c UserActiveColor) MarshalLogObject(encoder zapcore.ObjectEncoder) error { |
|
||||||
encoder.AddString("color", c.Color.String()) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Apply sets the user's active color, or returns ErrNoOp if it can't.
|
|
||||||
func (c UserActiveColor) Apply(s *state.Synced) error { |
|
||||||
if c.Color.A < 0xF { |
|
||||||
return ErrNoTransparentColors |
|
||||||
} |
|
||||||
if s.User.ActiveColor == c.Color { |
|
||||||
return ErrNoOp |
|
||||||
} |
|
||||||
s.User.ActiveColor = c.Color |
|
||||||
return nil |
|
||||||
} |
|
@ -0,0 +1,46 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
import "fmt" |
||||||
|
|
||||||
|
// Color is an internal representation of a hexadecimal color string.
|
||||||
|
// It can take one of these formats:
|
||||||
|
// #RRGGBBAA
|
||||||
|
// #RRGGBB
|
||||||
|
// #RGBA
|
||||||
|
// #RGB
|
||||||
|
// When marshaling, it will always choose the most efficient format, but any format can be used when unmarshaling.
|
||||||
|
type Color struct { |
||||||
|
// R is the red component of the color.
|
||||||
|
R uint8 |
||||||
|
// G is the green component of the color.
|
||||||
|
G uint8 |
||||||
|
// B is the blue component of the color.
|
||||||
|
B uint8 |
||||||
|
// A is the alpha component of the color.
|
||||||
|
A uint8 |
||||||
|
} |
||||||
|
|
||||||
|
// String prints the Color as an abbreviated notation.
|
||||||
|
// Specifically, short form is used when possible (i.e., when all components are evenly divisible by 0x11).
|
||||||
|
// The alpha component is left out if it's 0xFF.
|
||||||
|
func (c Color) String() string { |
||||||
|
if c.R%0x11 == 0 && c.G%0x11 == 0 && c.B%0x11 == 0 && c.A%0x11 == 0 { |
||||||
|
// Short form works.
|
||||||
|
if c.A == 0xFF { |
||||||
|
// It's great when it's easy!
|
||||||
|
return fmt.Sprintf("#%01X%01X%01X", c.R/0x11, c.G/0x11, c.B/0x11) |
||||||
|
} else { |
||||||
|
// Just need to add the alpha.
|
||||||
|
return fmt.Sprintf("#%01X%01X%01X%01X", c.R/0x11, c.G/0x11, c.B/0x11, c.A/0x11) |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Gotta use long form.
|
||||||
|
if c.A == 0xFF { |
||||||
|
// Can skip the alpha channel, though.
|
||||||
|
return fmt.Sprintf("#%02X%02X%02X", c.R, c.G, c.B) |
||||||
|
} else { |
||||||
|
// Doing things the hard way.
|
||||||
|
return fmt.Sprintf("#%02X%02X%02X%02X", c.R, c.G, c.B, c.A) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
// ColorFromInt decodes a packed uint32 into a hex color.
|
||||||
|
func ColorFromInt(value uint32) Color { |
||||||
|
return Color{ |
||||||
|
R: uint8((value >> 24) & 0xFF), |
||||||
|
G: uint8((value >> 16) & 0xFF), |
||||||
|
B: uint8((value >> 8) & 0xFF), |
||||||
|
A: uint8((value >> 0) & 0xFF), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ToInt packs a hex color into a uint32.
|
||||||
|
func (c Color) ToInt() uint32 { |
||||||
|
return uint32(c.R)<<24 | uint32(c.G)<<16 | uint32(c.B)<<8 | uint32(c.A) |
||||||
|
} |
@ -1,6 +1,8 @@ |
|||||||
package state |
package state |
||||||
|
|
||||||
import "go.uber.org/zap/zapcore" |
import ( |
||||||
|
"go.uber.org/zap/zapcore" |
||||||
|
) |
||||||
|
|
||||||
// StorageCoordinates gives the coordinates of a cell in a form optimized for storage.
|
// StorageCoordinates gives the coordinates of a cell in a form optimized for storage.
|
||||||
type StorageCoordinates struct { |
type StorageCoordinates struct { |
@ -0,0 +1,25 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"math" |
||||||
|
) |
||||||
|
|
||||||
|
var ErrOutOfBounds = errors.New("coordinate was out of bounds") |
||||||
|
|
||||||
|
func (x *StorageCoordinatesPB) ToGo() (StorageCoordinates, error) { |
||||||
|
if x.Line > math.MaxUint8 || x.Cell > math.MaxUint8 { |
||||||
|
return StorageCoordinates{}, ErrOutOfBounds |
||||||
|
} |
||||||
|
return StorageCoordinates{ |
||||||
|
Line: uint8(x.Line), |
||||||
|
Cell: uint8(x.Cell), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s StorageCoordinates) ToPB() *StorageCoordinatesPB { |
||||||
|
return &StorageCoordinatesPB{ |
||||||
|
Line: uint32(s.Line), |
||||||
|
Cell: uint32(s.Cell), |
||||||
|
} |
||||||
|
} |
@ -1,104 +0,0 @@ |
|||||||
package state |
|
||||||
|
|
||||||
import "fmt" |
|
||||||
|
|
||||||
// HexColor is an internal representation of a hexadecimal color string.
|
|
||||||
// It can take one of these formats:
|
|
||||||
// #RRGGBBAA
|
|
||||||
// #RRGGBB
|
|
||||||
// #RGBA
|
|
||||||
// #RGB
|
|
||||||
// When marshaling, it will always choose the most efficient format, but any format can be used when unmarshaling.
|
|
||||||
type HexColor struct { |
|
||||||
// R is the red component of the color.
|
|
||||||
R uint8 |
|
||||||
// G is the green component of the color.
|
|
||||||
G uint8 |
|
||||||
// B is the blue component of the color.
|
|
||||||
B uint8 |
|
||||||
// A is the alpha component of the color.
|
|
||||||
A uint8 |
|
||||||
} |
|
||||||
|
|
||||||
// HexColorFromString decodes a hexadecimal string into a hex color.
|
|
||||||
func HexColorFromString(text string) (HexColor, error) { |
|
||||||
var hex HexColor |
|
||||||
if err := (&hex).UnmarshalText([]byte(text)); err != nil { |
|
||||||
return hex, err |
|
||||||
} |
|
||||||
return hex, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (h *HexColor) UnmarshalText(text []byte) error { |
|
||||||
var count, expected int |
|
||||||
var short bool |
|
||||||
var scanErr error |
|
||||||
switch len(text) { |
|
||||||
case 9: |
|
||||||
// Long form with alpha: #RRGGBBAA
|
|
||||||
expected = 4 |
|
||||||
short = false |
|
||||||
count, scanErr = fmt.Sscanf(string(text), "#%02X%02X%02X%02X", &h.R, &h.G, &h.B, &h.A) |
|
||||||
case 7: |
|
||||||
// Long form: #RRGGBB
|
|
||||||
expected = 3 |
|
||||||
h.A = 0xFF |
|
||||||
count, scanErr = fmt.Sscanf(string(text), "#%02X%02X%02X", &h.R, &h.G, &h.B) |
|
||||||
case 5: |
|
||||||
// Short form with alpha: #RGBA
|
|
||||||
expected = 4 |
|
||||||
short = true |
|
||||||
count, scanErr = fmt.Sscanf(string(text), "#%01X%01X%01X%01X", &h.R, &h.G, &h.B, &h.A) |
|
||||||
case 4: |
|
||||||
// Short form: #RGB
|
|
||||||
expected = 3 |
|
||||||
h.A = 0xF |
|
||||||
short = true |
|
||||||
count, scanErr = fmt.Sscanf(string(text), "#%01X%01X%01X", &h.R, &h.G, &h.B) |
|
||||||
default: |
|
||||||
return fmt.Errorf("can't decode %s as HexColor: wrong length", text) |
|
||||||
} |
|
||||||
if scanErr != nil { |
|
||||||
return scanErr |
|
||||||
} |
|
||||||
if count != expected { |
|
||||||
return fmt.Errorf("can't decode %s as HexColor: missing components", text) |
|
||||||
} |
|
||||||
if short { |
|
||||||
h.R *= 0x11 |
|
||||||
h.G *= 0x11 |
|
||||||
h.B *= 0x11 |
|
||||||
h.A *= 0x11 |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// MarshalText marshals the HexColor into a small string.
|
|
||||||
func (h HexColor) MarshalText() (text []byte, err error) { |
|
||||||
return []byte(h.String()), nil |
|
||||||
} |
|
||||||
|
|
||||||
// String prints the HexColor as an abbreviated notation.
|
|
||||||
// Specifically, short form is used when possible (i.e., when all components are evenly divisible by 0x11).
|
|
||||||
// The alpha component is left out if it's 0xFF.
|
|
||||||
func (h HexColor) String() string { |
|
||||||
if h.R%0x11 == 0 && h.G%0x11 == 0 && h.B%0x11 == 0 && h.A%0x11 == 0 { |
|
||||||
// Short form works.
|
|
||||||
if h.A == 0xFF { |
|
||||||
// It's great when it's easy!
|
|
||||||
return fmt.Sprintf("#%01X%01X%01X", h.R/0x11, h.G/0x11, h.B/0x11) |
|
||||||
} else { |
|
||||||
// Just need to add the alpha.
|
|
||||||
return fmt.Sprintf("#%01X%01X%01X%01X", h.R/0x11, h.G/0x11, h.B/0x11, h.A/0x11) |
|
||||||
} |
|
||||||
} else { |
|
||||||
// Gotta use long form.
|
|
||||||
if h.A == 0xFF { |
|
||||||
// Can skip the alpha channel, though.
|
|
||||||
return fmt.Sprintf("#%02X%02X%02X", h.R, h.G, h.B) |
|
||||||
} else { |
|
||||||
// Doing things the hard way.
|
|
||||||
return fmt.Sprintf("#%02X%02X%02X%02X", h.R, h.G, h.B, h.A) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
package state |
|
||||||
|
|
||||||
import "fmt" |
|
||||||
|
|
||||||
// HexOrientation is the enum for the direction hexes are facing.
|
|
||||||
type HexOrientation uint8 |
|
||||||
|
|
||||||
const ( |
|
||||||
// PointyTop indicates hexes that have a pair of sides on either side in the horizontal direction,
|
|
||||||
// and points on the top and bottom in the vertical direction.
|
|
||||||
PointyTop HexOrientation = 1 |
|
||||||
// FlatTop indicates hexes that have a pair of points on either side in the horizontal direction,
|
|
||||||
// and sides on the top and bottom in the vertical direction.
|
|
||||||
FlatTop HexOrientation = 2 |
|
||||||
) |
|
||||||
|
|
||||||
// UnmarshalText unmarshals from the equivalent Javascript constant name.
|
|
||||||
func (o *HexOrientation) UnmarshalText(text []byte) error { |
|
||||||
switch string(text) { |
|
||||||
case "POINTY_TOP": |
|
||||||
*o = PointyTop |
|
||||||
return nil |
|
||||||
case "FLAT_TOP": |
|
||||||
*o = FlatTop |
|
||||||
return nil |
|
||||||
default: |
|
||||||
return fmt.Errorf("can't unmarshal unknown HexOrientation %s", text) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// MarshalText marshals into the equivalent JavaScript constant name.
|
|
||||||
func (o HexOrientation) MarshalText() (text []byte, err error) { |
|
||||||
switch o { |
|
||||||
case PointyTop, FlatTop: |
|
||||||
return []byte(o.String()), nil |
|
||||||
default: |
|
||||||
return nil, fmt.Errorf("can't marshal unknown HexOrientation %d", o) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// String returns the equivalent JavaScript constant name.
|
|
||||||
func (o HexOrientation) String() string { |
|
||||||
switch o { |
|
||||||
case PointyTop: |
|
||||||
return "POINTY_TOP" |
|
||||||
case FlatTop: |
|
||||||
return "FLAT_TOP" |
|
||||||
default: |
|
||||||
return fmt.Sprintf("[unknown HexOrientation %d]", o) |
|
||||||
} |
|
||||||
} |
|
@ -1,49 +0,0 @@ |
|||||||
package state |
|
||||||
|
|
||||||
import "fmt" |
|
||||||
|
|
||||||
// LineParity indicates whether odd or even lines are indented.
|
|
||||||
type LineParity uint8 |
|
||||||
|
|
||||||
const ( |
|
||||||
// OddLines indicates that odd lines - 1, 3, 5... - are indented by 1/2 cell.
|
|
||||||
OddLines LineParity = 1 |
|
||||||
// EvenLines indicates that even lines - 0, 2, 4... - are indented by 1/2 cell.
|
|
||||||
EvenLines LineParity = 2 |
|
||||||
) |
|
||||||
|
|
||||||
// UnmarshalText unmarshals from the equivalent Javascript constant name.
|
|
||||||
func (o *LineParity) UnmarshalText(text []byte) error { |
|
||||||
switch string(text) { |
|
||||||
case "ODD": |
|
||||||
*o = OddLines |
|
||||||
return nil |
|
||||||
case "EVEN": |
|
||||||
*o = EvenLines |
|
||||||
return nil |
|
||||||
default: |
|
||||||
return fmt.Errorf("can't unmarshal unknown LineParity %s", text) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// MarshalText marshals into the equivalent JavaScript constant name.
|
|
||||||
func (o LineParity) MarshalText() (text []byte, err error) { |
|
||||||
switch o { |
|
||||||
case OddLines, EvenLines: |
|
||||||
return []byte(o.String()), nil |
|
||||||
default: |
|
||||||
return nil, fmt.Errorf("can't marshal unknown LineParity %d", o) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// String returns the equivalent JavaScript constant name.
|
|
||||||
func (o LineParity) String() string { |
|
||||||
switch o { |
|
||||||
case OddLines: |
|
||||||
return "ODD_LINES" |
|
||||||
case EvenLines: |
|
||||||
return "EVEN_LINES" |
|
||||||
default: |
|
||||||
return fmt.Sprintf("[unknown LineParity %d]", o) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,142 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/rs/xid" |
||||||
|
"math" |
||||||
|
) |
||||||
|
|
||||||
|
func (x HexMapPB_Layout_Orientation) ToGo() HexOrientation { |
||||||
|
switch x { |
||||||
|
case HexMapPB_Layout_POINTY_TOP: |
||||||
|
return PointyTop |
||||||
|
case HexMapPB_Layout_FLAT_TOP: |
||||||
|
return FlatTop |
||||||
|
default: |
||||||
|
return UnknownOrientation |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (o HexOrientation) ToPB() HexMapPB_Layout_Orientation { |
||||||
|
switch o { |
||||||
|
case PointyTop: |
||||||
|
return HexMapPB_Layout_POINTY_TOP |
||||||
|
case FlatTop: |
||||||
|
return HexMapPB_Layout_FLAT_TOP |
||||||
|
default: |
||||||
|
return HexMapPB_Layout_UNKNOWN_ORIENTATION |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x HexMapPB_Layout_LineParity) ToGo() LineParity { |
||||||
|
switch x { |
||||||
|
case HexMapPB_Layout_EVEN: |
||||||
|
return EvenLines |
||||||
|
case HexMapPB_Layout_ODD: |
||||||
|
return OddLines |
||||||
|
default: |
||||||
|
return UnknownParity |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (o LineParity) ToPB() HexMapPB_Layout_LineParity { |
||||||
|
switch o { |
||||||
|
case OddLines: |
||||||
|
return HexMapPB_Layout_ODD |
||||||
|
case EvenLines: |
||||||
|
return HexMapPB_Layout_EVEN |
||||||
|
default: |
||||||
|
return HexMapPB_Layout_UNKNOWN_LINE |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *HexMapPB_Layout) ToGo() Layout { |
||||||
|
return Layout{ |
||||||
|
Orientation: x.Orientation.ToGo(), |
||||||
|
IndentedLines: x.IndentedLines.ToGo(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (l Layout) ToPB() *HexMapPB_Layout { |
||||||
|
return &HexMapPB_Layout{ |
||||||
|
Orientation: l.Orientation.ToPB(), |
||||||
|
IndentedLines: l.IndentedLines.ToPB(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *HexCellPB) ToGo() HexCell { |
||||||
|
return HexCell{ |
||||||
|
Color: ColorFromInt(x.Color), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (h HexCell) ToPB() *HexCellPB { |
||||||
|
return &HexCellPB{ |
||||||
|
Color: h.Color.ToInt(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *HexLinePB) ToGo() HexLine { |
||||||
|
r := make(HexLine, len(x.Cells)) |
||||||
|
for index, cell := range x.Cells { |
||||||
|
r[index] = cell.ToGo() |
||||||
|
} |
||||||
|
return r |
||||||
|
} |
||||||
|
|
||||||
|
func (l HexLine) ToPB() *HexLinePB { |
||||||
|
cells := make([]*HexCellPB, len(l)) |
||||||
|
for index, cell := range l { |
||||||
|
cells[index] = cell.ToPB() |
||||||
|
} |
||||||
|
return &HexLinePB{ |
||||||
|
Cells: cells, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *HexLayerPB) ToGo() HexLayer { |
||||||
|
r := make(HexLayer, len(x.Lines)) |
||||||
|
for index, line := range x.Lines { |
||||||
|
r[index] = line.ToGo() |
||||||
|
} |
||||||
|
return r |
||||||
|
} |
||||||
|
|
||||||
|
func (l HexLayer) ToPB() *HexLayerPB { |
||||||
|
lines := make([]*HexLinePB, len(l)) |
||||||
|
for index, line := range l { |
||||||
|
lines[index] = line.ToPB() |
||||||
|
} |
||||||
|
return &HexLayerPB{ |
||||||
|
Lines: lines, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *HexMapPB) ToGo() (HexMap, error) { |
||||||
|
pbId, err := xid.FromBytes(x.Xid) |
||||||
|
if err != nil { |
||||||
|
return HexMap{}, err |
||||||
|
} |
||||||
|
if x.Lines > math.MaxUint8 { |
||||||
|
return HexMap{}, ErrOutOfBounds |
||||||
|
} |
||||||
|
if x.CellsPerLine > math.MaxUint8 { |
||||||
|
return HexMap{}, ErrOutOfBounds |
||||||
|
} |
||||||
|
return HexMap{ |
||||||
|
XID: pbId, |
||||||
|
Lines: uint8(x.Lines), |
||||||
|
CellsPerLine: uint8(x.CellsPerLine), |
||||||
|
Layout: x.Layout.ToGo(), |
||||||
|
Layer: x.Layer.ToGo(), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (m HexMap) ToPB() *HexMapPB { |
||||||
|
return &HexMapPB{ |
||||||
|
Xid: m.XID.Bytes(), |
||||||
|
Lines: uint32(m.Lines), |
||||||
|
CellsPerLine: uint32(m.CellsPerLine), |
||||||
|
Layout: m.Layout.ToPB(), |
||||||
|
Layer: m.Layer.ToPB(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
func (x *SyncableStatePB) ToGo() (Synced, error) { |
||||||
|
pbMap, err := x.Map.ToGo() |
||||||
|
if err != nil { |
||||||
|
return Synced{}, err |
||||||
|
} |
||||||
|
user := x.User.ToGo() |
||||||
|
return Synced{ |
||||||
|
Map: pbMap, |
||||||
|
User: user, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s Synced) ToPB() *SyncableStatePB { |
||||||
|
return &SyncableStatePB{ |
||||||
|
Map: s.Map.ToPB(), |
||||||
|
User: s.User.ToPB(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package state |
||||||
|
|
||||||
|
func (x *UserStatePB) ToGo() UserState { |
||||||
|
return UserState{ |
||||||
|
ActiveColor: ColorFromInt(x.Color), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (u UserState) ToPB() *UserStatePB { |
||||||
|
return &UserStatePB{ |
||||||
|
Color: u.ActiveColor.ToInt(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package websocket |
||||||
|
|
||||||
|
func (x *ClientCommandPB) ToGo() (ClientCommand, error) { |
||||||
|
switch msg := x.Command.(type) { |
||||||
|
case *ClientCommandPB_Hello: |
||||||
|
return msg.Hello.ToGo(), nil |
||||||
|
case *ClientCommandPB_Refresh: |
||||||
|
return msg.Refresh.ToGo(), nil |
||||||
|
case *ClientCommandPB_Act: |
||||||
|
return msg.Act.ToGo() |
||||||
|
default: |
||||||
|
panic("A case was missed in ClientCommandPB.ToGo!") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ClientHelloPB) ToGo() ClientHello { |
||||||
|
return ClientHello{ |
||||||
|
Version: x.Version, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (c ClientHello) ToClientPB() *ClientCommandPB { |
||||||
|
return &ClientCommandPB{ |
||||||
|
Command: &ClientCommandPB_Hello{ |
||||||
|
Hello: &ClientHelloPB{ |
||||||
|
Version: c.Version, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (*ClientRefreshPB) ToGo() ClientRefresh { |
||||||
|
return ClientRefresh{} |
||||||
|
} |
||||||
|
|
||||||
|
func (c ClientRefresh) ToClientPB() *ClientCommandPB { |
||||||
|
return &ClientCommandPB{ |
||||||
|
Command: &ClientCommandPB_Refresh{ |
||||||
|
Refresh: &ClientRefreshPB{}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ClientActPB_IDed) ToGo() (IDed, error) { |
||||||
|
action, err := x.Action.ToGo() |
||||||
|
if err != nil { |
||||||
|
return IDed{}, nil |
||||||
|
} |
||||||
|
return IDed{ |
||||||
|
ID: x.Id, |
||||||
|
Action: action, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (i IDed) ToPB() *ClientActPB_IDed { |
||||||
|
return &ClientActPB_IDed{ |
||||||
|
Id: i.ID, |
||||||
|
Action: i.Action.ToClientPB(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ClientActPB) ToGo() (ClientAct, error) { |
||||||
|
actions := make(IDPairs, len(x.Actions)) |
||||||
|
for index, ided := range x.Actions { |
||||||
|
action, err := ided.ToGo() |
||||||
|
if err != nil { |
||||||
|
return ClientAct{}, err |
||||||
|
} |
||||||
|
actions[index] = action |
||||||
|
} |
||||||
|
return ClientAct{ |
||||||
|
Actions: actions, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c ClientAct) ToClientPB() *ClientCommandPB { |
||||||
|
actions := make([]*ClientActPB_IDed, len(c.Actions)) |
||||||
|
for index, ided := range c.Actions { |
||||||
|
actions[index] = ided.ToPB() |
||||||
|
} |
||||||
|
return &ClientCommandPB{ |
||||||
|
Command: &ClientCommandPB_Act{ |
||||||
|
Act: &ClientActPB{ |
||||||
|
Actions: actions, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (ClientMalformed) ToClientPB() *ClientCommandPB { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (SocketClosed) ToClientPB() *ClientCommandPB { |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,136 @@ |
|||||||
|
package websocket |
||||||
|
|
||||||
|
import "git.reya.zone/reya/hexmap/server/action" |
||||||
|
|
||||||
|
func (x *ServerCommandPB) ToGo() (ServerCommand, error) { |
||||||
|
switch msg := x.Command.(type) { |
||||||
|
case *ServerCommandPB_Hello: |
||||||
|
return msg.Hello.ToGo() |
||||||
|
case *ServerCommandPB_Refresh: |
||||||
|
return msg.Refresh.ToGo() |
||||||
|
case *ServerCommandPB_Ok: |
||||||
|
return msg.Ok.ToGo(), nil |
||||||
|
case *ServerCommandPB_Failed: |
||||||
|
return msg.Failed.ToGo(), nil |
||||||
|
case *ServerCommandPB_Act: |
||||||
|
return msg.Act.ToGo() |
||||||
|
default: |
||||||
|
panic("A case was missed in ServerCommandPB.ToGo!") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ServerHelloPB) ToGo() (ServerHello, error) { |
||||||
|
state, err := x.State.ToGo() |
||||||
|
if err != nil { |
||||||
|
return ServerHello{}, err |
||||||
|
} |
||||||
|
return ServerHello{ |
||||||
|
Version: x.Version, |
||||||
|
State: &state, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s ServerHello) ToServerPB() *ServerCommandPB { |
||||||
|
return &ServerCommandPB{ |
||||||
|
Command: &ServerCommandPB_Hello{ |
||||||
|
Hello: &ServerHelloPB{ |
||||||
|
Version: s.Version, |
||||||
|
State: s.State.ToPB(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ServerRefreshPB) ToGo() (ServerRefresh, error) { |
||||||
|
state, err := x.State.ToGo() |
||||||
|
if err != nil { |
||||||
|
return ServerRefresh{}, err |
||||||
|
} |
||||||
|
return ServerRefresh{ |
||||||
|
State: &state, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s ServerRefresh) ToServerPB() *ServerCommandPB { |
||||||
|
return &ServerCommandPB{ |
||||||
|
Command: &ServerCommandPB_Refresh{ |
||||||
|
Refresh: &ServerRefreshPB{ |
||||||
|
State: s.State.ToPB(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ServerOKPB) ToGo() ServerOK { |
||||||
|
ids := make(IDSlice, len(x.Ids)) |
||||||
|
copy(ids, x.Ids) |
||||||
|
return ServerOK{ |
||||||
|
IDs: ids, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s ServerOK) ToServerPB() *ServerCommandPB { |
||||||
|
ids := make([]uint32, len(s.IDs)) |
||||||
|
copy(ids, s.IDs) |
||||||
|
return &ServerCommandPB{ |
||||||
|
Command: &ServerCommandPB_Ok{ |
||||||
|
Ok: &ServerOKPB{ |
||||||
|
Ids: ids, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ServerFailedPB) ToGo() ServerFailed { |
||||||
|
ids := make(IDSlice, len(x.Ids)) |
||||||
|
copy(ids, x.Ids) |
||||||
|
return ServerFailed{ |
||||||
|
IDs: ids, |
||||||
|
Error: x.Error, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s ServerFailed) ToServerPB() *ServerCommandPB { |
||||||
|
ids := make([]uint32, len(s.IDs)) |
||||||
|
copy(ids, s.IDs) |
||||||
|
return &ServerCommandPB{ |
||||||
|
Command: &ServerCommandPB_Failed{ |
||||||
|
Failed: &ServerFailedPB{ |
||||||
|
Ids: ids, |
||||||
|
Error: s.Error, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *ServerActPB) ToGo() (ServerAct, error) { |
||||||
|
actions := make(action.ServerSlice, len(x.Actions)) |
||||||
|
for index, act := range x.Actions { |
||||||
|
convertedAct, err := act.ToGo() |
||||||
|
if err != nil { |
||||||
|
return ServerAct{}, err |
||||||
|
} |
||||||
|
actions[index] = convertedAct |
||||||
|
} |
||||||
|
return ServerAct{ |
||||||
|
Actions: actions, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s ServerAct) ToServerPB() *ServerCommandPB { |
||||||
|
actions := make([]*action.ServerActionPB, len(s.Actions)) |
||||||
|
for index, act := range s.Actions { |
||||||
|
actions[index] = act.ToServerPB() |
||||||
|
} |
||||||
|
return &ServerCommandPB{ |
||||||
|
Command: &ServerCommandPB_Act{ |
||||||
|
Act: &ServerActPB{ |
||||||
|
Actions: actions, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (SocketClosed) ToServerPB() *ServerCommandPB { |
||||||
|
return nil |
||||||
|
} |
Loading…
Reference in new issue