Put server in control of player IDs

zikaeroh created

Change summary

frontend/package.json                  |   4 
frontend/src/hooks/index.tsx           |   7 
frontend/src/pages/game.tsx            |  23 
frontend/src/pages/gameView.tsx        |   4 
frontend/src/protocol/index.ts         |  10 
frontend/yarn.lock                     |  10 
internal/protocol/protocol.go          |  23 
internal/protocol/protocol_easyjson.go | 479 ++++++++++++++++-----------
internal/server/server.go              |  16 
main.go                                |   2 
10 files changed, 330 insertions(+), 248 deletions(-)

Detailed changes

frontend/package.json 🔗

@@ -15,7 +15,6 @@
         "@types/node": "^14.0.1",
         "@types/react": "^16.9.0",
         "@types/react-dom": "^16.9.7",
-        "@types/uuid": "^8.0.0",
         "clipboard-copy": "^3.1.0",
         "fireworks": "^2.2.5",
         "lodash": "^4.17.15",
@@ -30,8 +29,7 @@
         "react-use-websocket": "^2.0.1",
         "ts-essentials": "^6.0.5",
         "typeface-roboto": "^0.0.75",
-        "typescript": "^3.9.5",
-        "uuid": "^8.0.0"
+        "typescript": "^3.9.5"
     },
     "devDependencies": {
         "@typescript-eslint/eslint-plugin": "^2.31.0",

frontend/src/hooks/index.tsx 🔗

@@ -1,5 +1,4 @@
 import * as React from 'react';
-import { v4 } from 'uuid';
 
 export interface ServerTime {
     setOffset: (v: number) => void;
@@ -17,9 +16,3 @@ export const ServerTimeProvider = (props: React.PropsWithChildren<{}>) => {
 export function useServerTime() {
     return React.useContext(Context);
 }
-
-export function useStableUUID(): string {
-    const id = React.useRef<string | undefined>();
-    id.current = id.current ?? v4();
-    return id.current;
-}

frontend/src/pages/game.tsx 🔗

@@ -4,7 +4,7 @@ import useWebSocket from 'react-use-websocket';
 import { DeepReadonly } from 'ts-essentials';
 
 import { assertIsDefined, assertNever, noop, reloadOutdatedPage, websocketUrl } from '../common';
-import { useServerTime, useStableUUID } from '../hooks';
+import { useServerTime } from '../hooks';
 import { version as codiesVersion } from '../metadata.json';
 import { ClientNote, PartialClientNote, ServerNote, State, StatePlayer, TimeResponse, WordPack } from '../protocol';
 import { GameView, Sender } from './gameView';
@@ -39,26 +39,26 @@ function useSender(dispatch: (action: PartialClientNote) => void): Sender {
     }, [dispatch]);
 }
 
-function usePlayer(playerID: string, state?: State): { pState: StatePlayer; pTeam: number } | undefined {
+function usePlayer(state?: State): { pState: StatePlayer; pTeam: number } | undefined {
     return React.useMemo(() => {
         if (!state) {
             return undefined;
         }
 
-        for (let i = 0; i < state.teams.length; i++) {
-            const pState = state.teams[i].find((p) => p.playerID === playerID);
+        for (let i = 0; i < state.roomState.teams.length; i++) {
+            const pState = state.roomState.teams[i].find((p) => p.playerID === state.playerID);
             if (pState) {
                 return { pState, pTeam: i };
             }
         }
 
         fail('Player not found in any team');
-    }, [playerID, state]);
+    }, [state]);
 }
 
 const reconnectAttempts = 2;
 
-function useWS(roomID: string, playerID: string, nickname: string, dead: () => void, onOpen: () => void) {
+function useWS(roomID: string, nickname: string, dead: () => void, onOpen: () => void) {
     const didUnmount = React.useRef(false);
     const retry = React.useRef(0);
 
@@ -68,7 +68,7 @@ function useWS(roomID: string, playerID: string, nickname: string, dead: () => v
         //
         // X-CODIES-VERSION would be cleaner, but the WS hook doesn't
         // support anything but query params.
-        queryParams: { roomID: roomID, playerID: playerID, nickname: nickname, codiesVersion: codiesVersion },
+        queryParams: { roomID: roomID, nickname: nickname, codiesVersion: codiesVersion },
         reconnectAttempts,
         onMessage: () => {
             retry.current = 0;
@@ -161,7 +161,7 @@ function useStateReducer(sendNote: (r: ClientNote) => void) {
                 case 'setState':
                     return action.state;
                 default:
-                    sendNoteRef.current({ ...action, version: state.version });
+                    sendNoteRef.current({ ...action, version: state.roomState.version });
                     return state;
             }
         },
@@ -176,15 +176,14 @@ export interface GameProps {
 }
 
 export const Game = (props: DeepReadonly<GameProps>) => {
-    const playerID = useStableUUID();
     const nickname = React.useRef(props.nickname); // Preserve a nickname for use in reconnects.
 
     const syncTime = useSyncedServerTime();
-    const { sendJsonMessage, lastJsonMessage } = useWS(props.roomID, playerID, nickname.current, props.leave, syncTime);
+    const { sendJsonMessage, lastJsonMessage } = useWS(props.roomID, nickname.current, props.leave, syncTime);
 
     const reducer = useStateReducer(sendJsonMessage);
     const [state, dispatch] = React.useReducer(reducer, undefined);
-    const player = usePlayer(playerID, state);
+    const player = usePlayer(state);
     const send = useSender(dispatch);
 
     React.useEffect(() => {
@@ -215,7 +214,7 @@ export const Game = (props: DeepReadonly<GameProps>) => {
             roomID={props.roomID}
             leave={props.leave}
             send={send}
-            state={state}
+            state={state.roomState}
             pState={player.pState}
             pTeam={player.pTeam}
         />

frontend/src/pages/gameView.tsx 🔗

@@ -41,7 +41,7 @@ import { isDefined, nameofFactory, noComplete } from '../common';
 import { Board } from '../components/board';
 import { ClipboardButton } from '../components/clipboard';
 import { useServerTime } from '../hooks';
-import { State, StatePlayer, StateTeams, StateTimer, StateWordList, WordPack } from '../protocol';
+import { RoomState, StatePlayer, StateTeams, StateTimer, StateWordList, WordPack } from '../protocol';
 import { teamSpecs } from '../teams';
 
 export interface Sender {
@@ -754,7 +754,7 @@ export interface GameViewProps {
     roomID: string;
     leave: () => void;
     send: Sender;
-    state: State;
+    state: RoomState;
     pState: StatePlayer;
     pTeam: number;
 }

frontend/src/protocol/index.ts 🔗

@@ -131,8 +131,8 @@ const StateWordList = myzod.object({
     enabled: myzod.boolean(),
 });
 
-export type State = DeepReadonly<Infer<typeof State>>;
-export const State = myzod.object({
+export type RoomState = DeepReadonly<Infer<typeof RoomState>>;
+export const RoomState = myzod.object({
     version: myzod.number(),
     teams: StateTeams,
     turn: myzod.number(),
@@ -144,6 +144,12 @@ export const State = myzod.object({
     hideBomb: myzod.boolean(),
 });
 
+export type State = DeepReadonly<Infer<typeof State>>;
+export const State = myzod.object({
+    playerID: myzod.string(),
+    roomState: RoomState,
+});
+
 export type ServerNote = DeepReadonly<Infer<typeof ServerNote>>;
 export const ServerNote = myzod.union([
     myzod.object({

frontend/yarn.lock 🔗

@@ -1710,11 +1710,6 @@
   dependencies:
     "@types/jest" "*"
 
-"@types/uuid@^8.0.0":
-  version "8.0.0"
-  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
-  integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
-
 "@types/yargs-parser@*":
   version "15.0.0"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -10841,11 +10836,6 @@ uuid@^3.0.1, uuid@^3.3.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
-uuid@^8.0.0:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
-  integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
-
 v8-compile-cache@^2.0.3:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"

internal/protocol/protocol.go 🔗

@@ -3,7 +3,6 @@ package protocol
 import (
 	"time"
 
-	"github.com/gofrs/uuid"
 	"github.com/mailru/easyjson"
 	"github.com/zikaeroh/codies/internal/game"
 )
@@ -57,9 +56,8 @@ type StatsResponse struct {
 }
 
 type WSQuery struct {
-	RoomID   string    `queryparam:"roomID"`
-	PlayerID uuid.UUID `queryparam:"playerID"`
-	Nickname string    `queryparam:"nickname"`
+	RoomID   string `queryparam:"roomID"`
+	Nickname string `queryparam:"nickname"`
 }
 
 func (w *WSQuery) Valid() (msg string, valid bool) {
@@ -67,10 +65,6 @@ func (w *WSQuery) Valid() (msg string, valid bool) {
 		return "Room ID cannot be empty.", false
 	}
 
-	if w.PlayerID == uuid.Nil {
-		return "Player ID cannot be empty", false
-	}
-
 	if len(w.Nickname) == 0 {
 		return "Nickname cannot be empty.", false
 	}
@@ -189,15 +183,24 @@ type ChangeHideBombParams struct {
 	HideBomb bool `json:"hideBomb"`
 }
 
-func StateNote(s *State) ServerNote {
+func NewStateNote(playerID game.PlayerID, s *RoomState) ServerNote {
 	return ServerNote{
 		Method: "state",
-		Params: s,
+		Params: &State{
+			PlayerID:  playerID,
+			RoomState: s,
+		},
 	}
 }
 
 //easyjson:json
 type State struct {
+	PlayerID  game.PlayerID `json:"playerID"`
+	RoomState *RoomState    `json:"roomState"`
+}
+
+//easyjson:json
+type RoomState struct {
 	Version   int              `json:"version"`
 	Teams     [][]*StatePlayer `json:"teams"`
 	Turn      game.Team        `json:"turn"`

internal/protocol/protocol_easyjson.go 🔗

@@ -604,6 +604,186 @@ func (v *StatePlayer) UnmarshalEasyJSON(l *jlexer.Lexer) {
 	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol6(l, v)
 }
 func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(in *jlexer.Lexer, out *State) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "playerID":
+			if data := in.UnsafeBytes(); in.Ok() {
+				in.AddError((out.PlayerID).UnmarshalText(data))
+			}
+		case "roomState":
+			if in.IsNull() {
+				in.Skip()
+				out.RoomState = nil
+			} else {
+				if out.RoomState == nil {
+					out.RoomState = new(RoomState)
+				}
+				(*out.RoomState).UnmarshalEasyJSON(in)
+			}
+		default:
+			in.AddError(&jlexer.LexerError{
+				Offset: in.GetPos(),
+				Reason: "unknown field",
+				Data:   key,
+			})
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(out *jwriter.Writer, in State) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"playerID\":"
+		out.RawString(prefix[1:])
+		out.RawText((in.PlayerID).MarshalText())
+	}
+	{
+		const prefix string = ",\"roomState\":"
+		out.RawString(prefix)
+		if in.RoomState == nil {
+			out.RawString("null")
+		} else {
+			(*in.RoomState).MarshalEasyJSON(out)
+		}
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v State) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v State) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *State) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *State) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(l, v)
+}
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(in *jlexer.Lexer, out *ServerNote) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "method":
+			out.Method = ServerMethod(in.String())
+		case "params":
+			if m, ok := out.Params.(easyjson.Unmarshaler); ok {
+				m.UnmarshalEasyJSON(in)
+			} else if m, ok := out.Params.(json.Unmarshaler); ok {
+				_ = m.UnmarshalJSON(in.Raw())
+			} else {
+				out.Params = in.Interface()
+			}
+		default:
+			in.AddError(&jlexer.LexerError{
+				Offset: in.GetPos(),
+				Reason: "unknown field",
+				Data:   key,
+			})
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(out *jwriter.Writer, in ServerNote) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"method\":"
+		out.RawString(prefix[1:])
+		out.String(string(in.Method))
+	}
+	{
+		const prefix string = ",\"params\":"
+		out.RawString(prefix)
+		if m, ok := in.Params.(easyjson.Marshaler); ok {
+			m.MarshalEasyJSON(out)
+		} else if m, ok := in.Params.(json.Marshaler); ok {
+			out.Raw(m.MarshalJSON())
+		} else {
+			out.Raw(json.Marshal(in.Params))
+		}
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v ServerNote) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v ServerNote) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *ServerNote) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *ServerNote) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(l, v)
+}
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(in *jlexer.Lexer, out *RoomState) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -820,7 +1000,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(in *jlexer.L
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(out *jwriter.Writer, in State) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(out *jwriter.Writer, in RoomState) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -959,118 +1139,29 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(out *jwriter
 }
 
 // MarshalJSON supports json.Marshaler interface
-func (v State) MarshalJSON() ([]byte, error) {
+func (v RoomState) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v State) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol7(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *State) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *State) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol7(l, v)
-}
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(in *jlexer.Lexer, out *ServerNote) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeString()
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "method":
-			out.Method = ServerMethod(in.String())
-		case "params":
-			if m, ok := out.Params.(easyjson.Unmarshaler); ok {
-				m.UnmarshalEasyJSON(in)
-			} else if m, ok := out.Params.(json.Unmarshaler); ok {
-				_ = m.UnmarshalJSON(in.Raw())
-			} else {
-				out.Params = in.Interface()
-			}
-		default:
-			in.AddError(&jlexer.LexerError{
-				Offset: in.GetPos(),
-				Reason: "unknown field",
-				Data:   key,
-			})
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(out *jwriter.Writer, in ServerNote) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"method\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Method))
-	}
-	{
-		const prefix string = ",\"params\":"
-		out.RawString(prefix)
-		if m, ok := in.Params.(easyjson.Marshaler); ok {
-			m.MarshalEasyJSON(out)
-		} else if m, ok := in.Params.(json.Marshaler); ok {
-			out.Raw(m.MarshalJSON())
-		} else {
-			out.Raw(json.Marshal(in.Params))
-		}
-	}
-	out.RawByte('}')
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v ServerNote) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
-func (v ServerNote) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol8(w, v)
+func (v RoomState) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
-func (v *ServerNote) UnmarshalJSON(data []byte) error {
+func (v *RoomState) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *ServerNote) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol8(l, v)
+func (v *RoomState) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(in *jlexer.Lexer, out *RoomResponse) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(in *jlexer.Lexer, out *RoomResponse) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1123,7 +1214,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(in *jlexer.L
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(out *jwriter.Writer, in RoomResponse) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(out *jwriter.Writer, in RoomResponse) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1149,27 +1240,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(out *jwriter
 // MarshalJSON supports json.Marshaler interface
 func (v RoomResponse) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v RoomResponse) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol9(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *RoomResponse) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *RoomResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol9(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(in *jlexer.Lexer, out *RoomRequest) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(in *jlexer.Lexer, out *RoomRequest) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1208,7 +1299,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(out *jwriter.Writer, in RoomRequest) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(out *jwriter.Writer, in RoomRequest) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1233,27 +1324,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v RoomRequest) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v RoomRequest) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol10(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *RoomRequest) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *RoomRequest) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol10(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(in *jlexer.Lexer, out *RevealParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(in *jlexer.Lexer, out *RevealParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1290,7 +1381,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(out *jwriter.Writer, in RevealParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(out *jwriter.Writer, in RevealParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1310,27 +1401,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v RevealParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v RevealParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol11(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *RevealParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *RevealParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol11(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(in *jlexer.Lexer, out *RemovePackParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(in *jlexer.Lexer, out *RemovePackParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1365,7 +1456,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(out *jwriter.Writer, in RemovePackParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(out *jwriter.Writer, in RemovePackParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1380,27 +1471,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v RemovePackParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v RemovePackParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol12(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *RemovePackParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *RemovePackParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol12(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(in *jlexer.Lexer, out *RandomizeTeamsParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(in *jlexer.Lexer, out *RandomizeTeamsParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1433,7 +1524,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(out *jwriter.Writer, in RandomizeTeamsParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(out *jwriter.Writer, in RandomizeTeamsParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1443,27 +1534,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v RandomizeTeamsParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v RandomizeTeamsParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol13(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *RandomizeTeamsParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *RandomizeTeamsParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol13(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(in *jlexer.Lexer, out *NewGameParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(in *jlexer.Lexer, out *NewGameParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1496,7 +1587,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(out *jwriter.Writer, in NewGameParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(out *jwriter.Writer, in NewGameParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1506,27 +1597,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v NewGameParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v NewGameParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol14(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *NewGameParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *NewGameParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol14(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(in *jlexer.Lexer, out *EndTurnParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(in *jlexer.Lexer, out *EndTurnParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1559,7 +1650,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(out *jwriter.Writer, in EndTurnParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(out *jwriter.Writer, in EndTurnParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1569,27 +1660,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v EndTurnParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v EndTurnParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol15(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *EndTurnParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *EndTurnParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol15(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(in *jlexer.Lexer, out *ClientNote) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(in *jlexer.Lexer, out *ClientNote) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1628,7 +1719,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(out *jwriter.Writer, in ClientNote) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(out *jwriter.Writer, in ClientNote) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1653,27 +1744,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ClientNote) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ClientNote) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol16(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ClientNote) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ClientNote) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol16(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(in *jlexer.Lexer, out *ChangeTurnTimeParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(in *jlexer.Lexer, out *ChangeTurnTimeParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1708,7 +1799,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(out *jwriter.Writer, in ChangeTurnTimeParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(out *jwriter.Writer, in ChangeTurnTimeParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1723,27 +1814,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeTurnTimeParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeTurnTimeParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol17(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeTurnTimeParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeTurnTimeParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol17(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(in *jlexer.Lexer, out *ChangeTurnModeParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(in *jlexer.Lexer, out *ChangeTurnModeParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1778,7 +1869,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(out *jwriter.Writer, in ChangeTurnModeParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(out *jwriter.Writer, in ChangeTurnModeParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1793,27 +1884,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeTurnModeParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeTurnModeParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol18(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeTurnModeParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeTurnModeParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol18(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(in *jlexer.Lexer, out *ChangeTeamParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(in *jlexer.Lexer, out *ChangeTeamParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1848,7 +1939,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(out *jwriter.Writer, in ChangeTeamParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(out *jwriter.Writer, in ChangeTeamParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1863,27 +1954,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeTeamParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeTeamParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol19(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeTeamParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeTeamParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol19(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(in *jlexer.Lexer, out *ChangeRoleParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(in *jlexer.Lexer, out *ChangeRoleParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1918,7 +2009,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(out *jwriter.Writer, in ChangeRoleParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(out *jwriter.Writer, in ChangeRoleParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -1933,27 +2024,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeRoleParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeRoleParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol20(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeRoleParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeRoleParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol20(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(in *jlexer.Lexer, out *ChangePackParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(in *jlexer.Lexer, out *ChangePackParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -1990,7 +2081,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(out *jwriter.Writer, in ChangePackParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(out *jwriter.Writer, in ChangePackParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -2010,27 +2101,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangePackParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangePackParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol21(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangePackParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangePackParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol21(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(in *jlexer.Lexer, out *ChangeNicknameParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(in *jlexer.Lexer, out *ChangeNicknameParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -2065,7 +2156,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(out *jwriter.Writer, in ChangeNicknameParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(out *jwriter.Writer, in ChangeNicknameParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -2080,27 +2171,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeNicknameParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeNicknameParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol22(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeNicknameParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeNicknameParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol22(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(in *jlexer.Lexer, out *ChangeHideBombParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(in *jlexer.Lexer, out *ChangeHideBombParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -2135,7 +2226,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(out *jwriter.Writer, in ChangeHideBombParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(out *jwriter.Writer, in ChangeHideBombParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -2150,27 +2241,27 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v ChangeHideBombParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v ChangeHideBombParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol23(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *ChangeHideBombParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *ChangeHideBombParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol23(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(l, v)
 }
-func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(in *jlexer.Lexer, out *AddPacksParams) {
+func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol25(in *jlexer.Lexer, out *AddPacksParams) {
 	isTopLevel := in.IsStart()
 	if in.IsNull() {
 		if isTopLevel {
@@ -2235,7 +2326,7 @@ func easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(in *jlexer.
 		in.Consumed()
 	}
 }
-func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(out *jwriter.Writer, in AddPacksParams) {
+func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol25(out *jwriter.Writer, in AddPacksParams) {
 	out.RawByte('{')
 	first := true
 	_ = first
@@ -2261,25 +2352,25 @@ func easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(out *jwrite
 // MarshalJSON supports json.Marshaler interface
 func (v AddPacksParams) MarshalJSON() ([]byte, error) {
 	w := jwriter.Writer{}
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(&w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol25(&w, v)
 	return w.Buffer.BuildBytes(), w.Error
 }
 
 // MarshalEasyJSON supports easyjson.Marshaler interface
 func (v AddPacksParams) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol24(w, v)
+	easyjsonE4425964EncodeGithubComZikaerohCodiesInternalProtocol25(w, v)
 }
 
 // UnmarshalJSON supports json.Unmarshaler interface
 func (v *AddPacksParams) UnmarshalJSON(data []byte) error {
 	r := jlexer.Lexer{Data: data}
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(&r, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol25(&r, v)
 	return r.Error()
 }
 
 // UnmarshalEasyJSON supports easyjson.Unmarshaler interface
 func (v *AddPacksParams) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol24(l, v)
+	easyjsonE4425964DecodeGithubComZikaerohCodiesInternalProtocol25(l, v)
 }
 func easyjsonE4425964Decode(in *jlexer.Lexer, out *struct {
 	Name  string   `json:"name"`

internal/server/server.go 🔗

@@ -224,7 +224,9 @@ type Room struct {
 
 type noteSender func(protocol.ServerNote)
 
-func (r *Room) HandleConn(ctx context.Context, playerID uuid.UUID, nickname string, c *websocket.Conn) {
+func (r *Room) HandleConn(ctx context.Context, nickname string, c *websocket.Conn) {
+	playerID := uuid.Must(uuid.NewV4())
+
 	ctx, cancel := ctxjoin.AddCancel(ctx, r.ctx)
 	defer cancel()
 
@@ -474,12 +476,12 @@ func (r *Room) sendAll() {
 // Must be called with r.mu locked.
 func (r *Room) sendOne(playerID game.PlayerID, sender noteSender) {
 	state := r.createStateFor(playerID)
-	note := protocol.StateNote(state)
+	note := protocol.NewStateNote(playerID, state)
 	sender(note)
 }
 
 // Must be called with r.mu locked.
-func (r *Room) createStateFor(playerID game.PlayerID) *protocol.State {
+func (r *Room) createStateFor(playerID game.PlayerID) *protocol.RoomState {
 	if r.state == nil || r.state.version != r.room.Version {
 		r.state = r.createStateCache()
 	}
@@ -498,8 +500,8 @@ func (r *Room) createStateFor(playerID game.PlayerID) *protocol.State {
 
 type stateCache struct {
 	version   int
-	guesser   *protocol.State
-	spymaster *protocol.State
+	guesser   *protocol.RoomState
+	spymaster *protocol.RoomState
 }
 
 func (r *Room) createStateCache() *stateCache {
@@ -510,10 +512,10 @@ func (r *Room) createStateCache() *stateCache {
 	}
 }
 
-func (r *Room) createRoomState(spymaster bool) *protocol.State {
+func (r *Room) createRoomState(spymaster bool) *protocol.RoomState {
 	room := r.room
 
-	s := &protocol.State{
+	s := &protocol.RoomState{
 		Version:   room.Version,
 		Teams:     make([][]*protocol.StatePlayer, len(room.Teams)),
 		Turn:      room.Turn,

main.go 🔗

@@ -217,7 +217,7 @@ func main() {
 				}
 
 				g.Go(func() error {
-					room.HandleConn(ctx, query.PlayerID, query.Nickname, c)
+					room.HandleConn(ctx, query.Nickname, c)
 					return nil
 				})
 			})