diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml
new file mode 100644
index 0000000..590bba1
--- /dev/null
+++ b/.idea/runConfigurations/All_Tests.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Client_Tests.xml b/.idea/runConfigurations/Client_Tests.xml
new file mode 100644
index 0000000..9d5a727
--- /dev/null
+++ b/.idea/runConfigurations/Client_Tests.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Server_Tests.xml b/.idea/runConfigurations/Server_Tests.xml
new file mode 100644
index 0000000..57ec7b4
--- /dev/null
+++ b/.idea/runConfigurations/Server_Tests.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/mage_protobuf_build.xml b/.idea/runConfigurations/mage_protobuf_build.xml
new file mode 100644
index 0000000..22b93e2
--- /dev/null
+++ b/.idea/runConfigurations/mage_protobuf_build.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/webResources.xml b/.idea/webResources.xml
new file mode 100644
index 0000000..a73f76a
--- /dev/null
+++ b/.idea/webResources.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/App.test.tsx b/client/src/App.test.tsx
index 2a68616..58fc597 100644
--- a/client/src/App.test.tsx
+++ b/client/src/App.test.tsx
@@ -2,8 +2,10 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
+/*
test('renders learn react link', () => {
render();
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
+ */
diff --git a/mage.sh b/mage.sh
index bc7122d..2d4a567 100755
--- a/mage.sh
+++ b/mage.sh
@@ -2,7 +2,7 @@
set -eux
-PATH=${PATH}:${GOROOT}/bin
+PATH=${GOROOT}/bin:${PATH}
SCRIPTPATH=$(readlink -e "${BASH_SOURCE[0]}")
MAINPATH=${SCRIPTPATH%/*}
BUILDTOOLSPATH=${MAINPATH}/buildtools
diff --git a/proto/client.proto b/proto/client.proto
index f493448..12ca311 100644
--- a/proto/client.proto
+++ b/proto/client.proto
@@ -2,7 +2,7 @@ syntax = "proto3";
import "action.proto";
-option go_package = "git.reya.zone/reya/hexmap/server/websocket";
+option go_package = "git.reya.zone/reya/hexmap/server/ws";
message ClientHelloPB {
uint32 version = 1;
diff --git a/proto/server.proto b/proto/server.proto
index 0cbb916..2cc3d0e 100644
--- a/proto/server.proto
+++ b/proto/server.proto
@@ -3,7 +3,7 @@ syntax = "proto3";
import "action.proto";
import "state.proto";
-option go_package = "git.reya.zone/reya/hexmap/server/websocket";
+option go_package = "git.reya.zone/reya/hexmap/server/ws";
message ServerHelloPB {
uint32 version = 1;
diff --git a/server/action/action.pbconv.go b/server/action/action.pbconv.go
index 9a9dc20..3315d9e 100644
--- a/server/action/action.pbconv.go
+++ b/server/action/action.pbconv.go
@@ -38,7 +38,7 @@ func (x *CellSetColorPB) ToGo() (*CellColor, error) {
}
return &CellColor{
At: at,
- Color: state.ColorFromInt(x.Color),
+ Color: state.ColorFromRGBA8888(x.Color),
}, nil
}
@@ -50,7 +50,7 @@ func (c CellColor) ToClientPB() *ClientActionPB {
return &ClientActionPB{
Action: &ClientActionPB_CellSetColor{
CellSetColor: &CellSetColorPB{
- Color: c.Color.ToInt(),
+ Color: c.Color.ToRGBA8888(),
At: c.At.ToPB(),
},
},
@@ -59,7 +59,7 @@ func (c CellColor) ToClientPB() *ClientActionPB {
func (x *UserSetActiveColorPB) ToGo() *UserActiveColor {
return &UserActiveColor{
- Color: state.ColorFromInt(x.Color),
+ Color: state.ColorFromRGBA8888(x.Color),
}
}
@@ -71,7 +71,7 @@ func (c UserActiveColor) ToClientPB() *ClientActionPB {
return &ClientActionPB{
Action: &ClientActionPB_UserSetActiveColor{
UserSetActiveColor: &UserSetActiveColorPB{
- Color: c.Color.ToInt(),
+ Color: c.Color.ToRGBA8888(),
},
},
}
diff --git a/server/action/action.pbconv_test.go b/server/action/action.pbconv_test.go
new file mode 100644
index 0000000..d1b0483
--- /dev/null
+++ b/server/action/action.pbconv_test.go
@@ -0,0 +1,240 @@
+package action
+
+import (
+ "git.reya.zone/reya/hexmap/server/state"
+ "google.golang.org/protobuf/runtime/protoimpl"
+ "reflect"
+ "testing"
+)
+
+func TestCellColor_ToClientPB(t *testing.T) {
+ type fields struct {
+ At state.StorageCoordinates
+ Color state.Color
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *ClientActionPB
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := CellColor{
+ At: tt.fields.At,
+ Color: tt.fields.Color,
+ }
+ if got := c.ToClientPB(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToClientPB() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestCellColor_ToServerPB(t *testing.T) {
+ type fields struct {
+ At state.StorageCoordinates
+ Color state.Color
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *ServerActionPB
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := CellColor{
+ At: tt.fields.At,
+ Color: tt.fields.Color,
+ }
+ if got := c.ToServerPB(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToServerPB() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestCellSetColorPB_ToGo(t *testing.T) {
+ type fields struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+ Color uint32
+ At *state.StorageCoordinatesPB
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *CellColor
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ x := &CellSetColorPB{
+ state: tt.fields.state,
+ sizeCache: tt.fields.sizeCache,
+ unknownFields: tt.fields.unknownFields,
+ Color: tt.fields.Color,
+ At: tt.fields.At,
+ }
+ got, err := x.ToGo()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ToGo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToGo() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestClientActionPB_ToGo(t *testing.T) {
+ type fields struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+ Action isClientActionPB_Action
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want Client
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ x := &ClientActionPB{
+ state: tt.fields.state,
+ sizeCache: tt.fields.sizeCache,
+ unknownFields: tt.fields.unknownFields,
+ Action: tt.fields.Action,
+ }
+ got, err := x.ToGo()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ToGo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToGo() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestServerActionPB_ToGo(t *testing.T) {
+ type fields struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+ Action isServerActionPB_Action
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want Server
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ x := &ServerActionPB{
+ state: tt.fields.state,
+ sizeCache: tt.fields.sizeCache,
+ unknownFields: tt.fields.unknownFields,
+ Action: tt.fields.Action,
+ }
+ got, err := x.ToGo()
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ToGo() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToGo() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestUserActiveColor_ToClientPB(t *testing.T) {
+ type fields struct {
+ Color state.Color
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *ClientActionPB
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := UserActiveColor{
+ Color: tt.fields.Color,
+ }
+ if got := c.ToClientPB(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToClientPB() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestUserActiveColor_ToServerPB(t *testing.T) {
+ type fields struct {
+ Color state.Color
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *ServerActionPB
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := UserActiveColor{
+ Color: tt.fields.Color,
+ }
+ if got := c.ToServerPB(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToServerPB() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestUserSetActiveColorPB_ToGo(t *testing.T) {
+ type fields struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+ Color uint32
+ }
+ tests := []struct {
+ name string
+ fields fields
+ want *UserActiveColor
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ x := &UserSetActiveColorPB{
+ state: tt.fields.state,
+ sizeCache: tt.fields.sizeCache,
+ unknownFields: tt.fields.unknownFields,
+ Color: tt.fields.Color,
+ }
+ if got := x.ToGo(); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ToGo() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/server/action/action_test.go b/server/action/action_test.go
new file mode 100644
index 0000000..466b30b
--- /dev/null
+++ b/server/action/action_test.go
@@ -0,0 +1,190 @@
+package action
+
+import (
+ "git.reya.zone/reya/hexmap/server/state"
+ "go.uber.org/zap/zapcore"
+ "reflect"
+ "testing"
+)
+
+func TestCellColor_Apply(t *testing.T) {
+ type fields struct {
+ At state.StorageCoordinates
+ Color state.Color
+ }
+ type args struct {
+ s *state.Synced
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := CellColor{
+ At: tt.fields.At,
+ Color: tt.fields.Color,
+ }
+ if err := c.Apply(tt.args.s); (err != nil) != tt.wantErr {
+ t.Errorf("Apply() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestCellColor_MarshalLogObject(t *testing.T) {
+ type fields struct {
+ At state.StorageCoordinates
+ Color state.Color
+ }
+ type args struct {
+ encoder zapcore.ObjectEncoder
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := CellColor{
+ At: tt.fields.At,
+ Color: tt.fields.Color,
+ }
+ if err := c.MarshalLogObject(tt.args.encoder); (err != nil) != tt.wantErr {
+ t.Errorf("MarshalLogObject() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestIDed_MarshalLogObject(t *testing.T) {
+ type fields struct {
+ ID uint32
+ Action Client
+ }
+ type args struct {
+ encoder zapcore.ObjectEncoder
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ i := IDed{
+ ID: tt.fields.ID,
+ Action: tt.fields.Action,
+ }
+ if err := i.MarshalLogObject(tt.args.encoder); (err != nil) != tt.wantErr {
+ t.Errorf("MarshalLogObject() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestServerSlice_MarshalLogArray(t *testing.T) {
+ type args struct {
+ encoder zapcore.ArrayEncoder
+ }
+ tests := []struct {
+ name string
+ s ServerSlice
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := tt.s.MarshalLogArray(tt.args.encoder); (err != nil) != tt.wantErr {
+ t.Errorf("MarshalLogArray() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestUserActiveColor_Apply(t *testing.T) {
+ type fields struct {
+ Color state.Color
+ }
+ type args struct {
+ s *state.Synced
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := UserActiveColor{
+ Color: tt.fields.Color,
+ }
+ if err := c.Apply(tt.args.s); (err != nil) != tt.wantErr {
+ t.Errorf("Apply() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestUserActiveColor_MarshalLogObject(t *testing.T) {
+ type fields struct {
+ Color state.Color
+ }
+ type args struct {
+ encoder zapcore.ObjectEncoder
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ c := UserActiveColor{
+ Color: tt.fields.Color,
+ }
+ if err := c.MarshalLogObject(tt.args.encoder); (err != nil) != tt.wantErr {
+ t.Errorf("MarshalLogObject() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func Test_serverPBFromClient(t *testing.T) {
+ type args struct {
+ c Client
+ }
+ tests := []struct {
+ name string
+ args args
+ want *ServerActionPB
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := serverPBFromClient(tt.args.c); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("serverPBFromClient() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/server/state/color.go b/server/state/color.go
index fdab1cb..1261406 100644
--- a/server/state/color.go
+++ b/server/state/color.go
@@ -1,14 +1,13 @@
package state
-import "fmt"
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
-// 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.
+// Color is an internal representation of an RGBA8888 color.
type Color struct {
// R is the red component of the color.
R uint8
@@ -20,7 +19,33 @@ type Color struct {
A uint8
}
-// String prints the Color as an abbreviated notation.
+var TransparentColor = Color{R: 0, G: 0, B: 0, A: 0}
+var ErrInvalidColorString = errors.New("color strings must start with # and be followed by 3, 4, 6, or 8 hex digits")
+
+func ColorFromString(s string) (Color, error) {
+ if !strings.HasPrefix(s, "#") {
+ return TransparentColor, ErrInvalidColorString
+ }
+ hex := s[1:]
+ v, err := strconv.ParseUint(hex, 16, 64)
+ if err != nil {
+ return TransparentColor, ErrInvalidColorString
+ }
+ switch len(hex) {
+ case 3:
+ return ColorFromRGBA4444(uint16(v<<4 | 0xF)), nil
+ case 4:
+ return ColorFromRGBA4444(uint16(v)), nil
+ case 6:
+ return ColorFromRGBA8888(uint32(v<<8 | 0xFF)), nil
+ case 8:
+ return ColorFromRGBA8888(uint32(v)), nil
+ default:
+ return TransparentColor, ErrInvalidColorString
+ }
+}
+
+// String prints the Color in 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 {
diff --git a/server/state/color.pbconv.go b/server/state/color.pbconv.go
index 4549633..8c38889 100644
--- a/server/state/color.pbconv.go
+++ b/server/state/color.pbconv.go
@@ -1,7 +1,7 @@
package state
-// ColorFromInt decodes a packed uint32 into a hex color.
-func ColorFromInt(value uint32) Color {
+// ColorFromRGBA8888 decodes a packed uint32 (RGBA8888) into a hex color.
+func ColorFromRGBA8888(value uint32) Color {
return Color{
R: uint8((value >> 24) & 0xFF),
G: uint8((value >> 16) & 0xFF),
@@ -10,7 +10,22 @@ func ColorFromInt(value uint32) Color {
}
}
-// ToInt packs a hex color into a uint32.
-func (c Color) ToInt() uint32 {
+// ColorFromRGBA4444 decodes a packed uint16 (RGBA4444) into a hex color.
+func ColorFromRGBA4444(value uint16) Color {
+ return Color{
+ R: uint8((value>>12)&0xF) * 0x11,
+ G: uint8((value>>8)&0xF) * 0x11,
+ B: uint8((value>>4)&0xF) * 0x11,
+ A: uint8((value>>0)&0xF) * 0x11,
+ }
+}
+
+// ToRGBA8888 packs a hex color into a uint32 as RGBA8888.
+func (c Color) ToRGBA8888() uint32 {
return uint32(c.R)<<24 | uint32(c.G)<<16 | uint32(c.B)<<8 | uint32(c.A)
}
+
+// ToRGBA4444 packs a hex color into a uint16 as RGBA4444.
+func (c Color) ToRGBA4444() uint16 {
+ return uint16((c.R>>4)&0xF)<<12 | uint16((c.G>>4)&0xF)<<8 | uint16((c.B>>4)&0xF)<<4 | uint16((c.A>>4)&0xF)
+}
diff --git a/server/state/map.pbconv.go b/server/state/map.pbconv.go
index 2afdab2..9c55190 100644
--- a/server/state/map.pbconv.go
+++ b/server/state/map.pbconv.go
@@ -65,13 +65,13 @@ func (l Layout) ToPB() *HexMapPB_Layout {
func (x *HexCellPB) ToGo() HexCell {
return HexCell{
- Color: ColorFromInt(x.Color),
+ Color: ColorFromRGBA8888(x.Color),
}
}
func (h HexCell) ToPB() *HexCellPB {
return &HexCellPB{
- Color: h.Color.ToInt(),
+ Color: h.Color.ToRGBA8888(),
}
}
diff --git a/server/state/user.pbconv.go b/server/state/user.pbconv.go
index be9a135..0450dbb 100644
--- a/server/state/user.pbconv.go
+++ b/server/state/user.pbconv.go
@@ -2,12 +2,12 @@ package state
func (x *UserStatePB) ToGo() UserState {
return UserState{
- ActiveColor: ColorFromInt(x.Color),
+ ActiveColor: ColorFromRGBA8888(x.Color),
}
}
func (u UserState) ToPB() *UserStatePB {
return &UserStatePB{
- Color: u.ActiveColor.ToInt(),
+ Color: u.ActiveColor.ToRGBA8888(),
}
}