diff --git a/src/device/input/parse/v01/config.go b/src/device/input/parse/v01/config.go index 9b64907..98f9525 100644 --- a/src/device/input/parse/v01/config.go +++ b/src/device/input/parse/v01/config.go @@ -22,13 +22,22 @@ type ( } configUser struct { - Player int - Message string - Alias string + Meta configUserMeta + State configUserState + } + + configUserMeta struct { LastTSMS int64 LastLag int64 } + configUserState struct { + Player int + Message string + Alias string + Vote string + } + configPlayer struct { Transformation transformation } diff --git a/src/device/input/parse/v01/server.go b/src/device/input/parse/v01/server.go index f54519a..d71ac2c 100644 --- a/src/device/input/parse/v01/server.go +++ b/src/device/input/parse/v01/server.go @@ -78,7 +78,7 @@ func (v01 *V01) getUserFeedback(w http.ResponseWriter, r *http.Request) { if !ok { user = v01.cfg.Users["broadcast"] } - w.Write([]byte(user.Message)) + w.Write([]byte(user.State.Message)) } func (v01 *V01) servePutBroadcast(w http.ResponseWriter, r *http.Request) { @@ -88,7 +88,7 @@ func (v01 *V01) servePutBroadcast(w http.ResponseWriter, r *http.Request) { func (v01 *V01) servePutBroadcastValue(v string) { u := v01.cfg.Users["broadcast"] - u.Message = v + u.State.Message = v v01.cfg.Users["broadcast"] = u } @@ -178,10 +178,10 @@ func (v01 *V01) serveGMStatus(w http.ResponseWriter, r *http.Request) { }{} for k, v := range v01.cfg.Users { v2 := users[k] - v2.Lag = time.Duration(v.LastLag) * time.Millisecond - v2.Player = v.Player - if v.LastTSMS > 0 { - v2.IdleFor = time.Since(time.Unix(0, v.LastTSMS*int64(time.Millisecond))) + v2.Lag = time.Duration(v.Meta.LastLag) * time.Millisecond + v2.Player = v.State.Player + if v.Meta.LastTSMS > 0 { + v2.IdleFor = time.Since(time.Unix(0, v.Meta.LastTSMS*int64(time.Millisecond))) } users[k] = v2 } @@ -194,8 +194,8 @@ func (v01 *V01) serveGMStatus(w http.ResponseWriter, r *http.Request) { func (v01 *V01) serveGMSomeoneSaidAlias(w http.ResponseWriter, r *http.Request) { v01.cfg.Quiet = true for k, v := range v01.cfg.Users { - v.Message = v.Alias - v.Alias = "" + v.State.Message = v.State.Alias + v.State.Alias = "" v01.cfg.Users[k] = v } v01.servePutBroadcastValue(fmt.Sprintf("<>", strings.ToUpper(r.URL.Query().Get("message")))) @@ -207,7 +207,7 @@ func (v01 *V01) serveGMFillNonPlayerAliases(w http.ResponseWriter, r *http.Reque yaml.Unmarshal(b, &pool) n := 0 for _, v := range v01.cfg.Users { - if v.Player == 0 { + if v.State.Player == 0 { n += 1 } } @@ -225,8 +225,8 @@ func (v01 *V01) serveGMFillNonPlayerAliases(w http.ResponseWriter, r *http.Reque } i := 0 for k, v := range v01.cfg.Users { - if v.Player == 0 { - v.Alias = pool[i] + if v.State.Player == 0 { + v.State.Alias = pool[i] v01.cfg.Users[k] = v i += 1 } @@ -235,12 +235,18 @@ func (v01 *V01) serveGMFillNonPlayerAliases(w http.ResponseWriter, r *http.Reque func (v01 *V01) serveGMElect(w http.ResponseWriter, r *http.Request) { alias := r.URL.Query().Get("alias") + aliasWinner := "" votes := map[string]int{} - for _, v := range v01.cfg.Users { - if v2 := strings.Split(v.Message, "//"); len(v2) > 1 { - votes[v2[1]] = votes[v2[1]] + 1 + for k, v := range v01.cfg.Users { + votes[v.State.Vote] = votes[v.State.Vote] + 1 // todo into state.gm + if v.State.Message == alias { // todo lowkey aliases pls // todo into state.gm + aliasWinner = k } } + if aliasWinner == "" { + http.Error(w, "who is "+alias+"?", http.StatusBadRequest) + return + } threshold := 0.1 + float64(len(votes))/2.0 winner := "" for k, v := range votes { @@ -250,7 +256,7 @@ func (v01 *V01) serveGMElect(w http.ResponseWriter, r *http.Request) { } if winner == "" { v01.serveGMShuffle(r) - } else if _, err := v01.serveGMSwap(winner, alias); err != nil { + } else if _, err := v01.serveGMSwap(winner, aliasWinner); err != nil { v01.serveGMShuffle(r) } yaml.NewEncoder(w).Encode(votes) @@ -261,7 +267,7 @@ func (v01 *V01) serveGMVote(w http.ResponseWriter, r *http.Request) { case http.MethodGet: counts := map[string]string{} for k, v := range v01.cfg.Users { - if strings.Contains(v.Message, "//") { + if v.State.Vote != "" { counts[k] = "voted" } else { counts[k] = "voting" @@ -276,7 +282,7 @@ func (v01 *V01) serveGMVote(w http.ResponseWriter, r *http.Request) { http.Error(w, "bad voter/candidate", http.StatusBadRequest) return } - v.Message = strings.Split(v.Message, "//")[0] + "//" + candidate + v.State.Vote = candidate v01.cfg.Users[voter] = v } } @@ -300,10 +306,10 @@ func (v01 *V01) serveGMShuffle(r *http.Request) { i := 0 msg := []string{} for k, v := range v01.cfg.Users { - v.Player = pool[i] + v.State.Player = pool[i] v01.cfg.Users[k] = v if pool[i] > 0 { - msg = append(msg, fmt.Sprintf("%s is now player %v", k, v.Player)) + msg = append(msg, fmt.Sprintf("%s is now player %v", k, v.State.Player)) } i += 1 } @@ -311,29 +317,21 @@ func (v01 *V01) serveGMShuffle(r *http.Request) { v01.cfg.Quiet = false } -func (v01 *V01) serveGMSwap(nameA, nameB string) (int, error) { - getUserNameFor := func(like string) string { - for k, v := range v01.cfg.Users { - if k == like || v.Alias == like || (strings.Contains(v.Message, "//") && strings.Split(v.Message, "//")[0] == like) { - return k - } - } - return "" - } - userA := getUserNameFor(nameA) - userB := getUserNameFor(nameB) - if userA == "" || userB == "" { - return http.StatusBadRequest, errors.New("who dat?") - } +func (v01 *V01) serveGMSwap(userA, userB string) (int, error) { if userA == userB { return http.StatusConflict, errors.New("/spiderman-pointing") } + _, okA := v01.cfg.Users[userA] + _, okB := v01.cfg.Users[userB] + if !okA || !okB { + return http.StatusBadRequest, errors.New("who dat?") + } a := v01.cfg.Users[userA] b := v01.cfg.Users[userB] - a.Player, b.Player = b.Player, a.Player + a.State.Player, b.State.Player = b.State.Player, a.State.Player v01.cfg.Users[userA] = a v01.cfg.Users[userB] = b v01.cfg.Quiet = false - v01.servePutBroadcastValue(fmt.Sprintf(`%s is now player %v and %s is now player %v`, userA, a.Player, userB, b.Player)) + v01.servePutBroadcastValue(fmt.Sprintf(`%s is now player %v and %s is now player %v`, userA, a.State.Player, userB, b.State.Player)) return http.StatusOK, nil } diff --git a/src/device/input/parse/v01/server_test.go b/src/device/input/parse/v01/server_test.go index 41ad150..c4d486f 100644 --- a/src/device/input/parse/v01/server_test.go +++ b/src/device/input/parse/v01/server_test.go @@ -27,19 +27,19 @@ func TestPatchConfig(t *testing.T) { "replace entire doc": { was: config{ Feedback: configFeedback{Addr: "a", TTSURL: "a"}, - Users: map[string]configUser{"a": configUser{Player: 1, Message: "a"}}, + Users: map[string]configUser{"a": configUser{State: configUserState{Player: 1, Message: "a"}}}, Players: []configPlayer{configPlayer{Transformation: transformation{"a": "a"}}}, Quiet: true, }, patch: `[{"op": "replace", "path": "", "value": { "Feedback": {"Addr": "b", "TTSURL": "b"}, - "Users": {"b": {"Player": 2, "Message": "b"}}, + "Users": {"b": {"State":{"Player": 2, "Message": "b"}}}, "Players": [{"Transformation": {"b": "b"}}], "Quiet": false }}]`, want: config{ Feedback: configFeedback{Addr: "b", TTSURL: "b"}, - Users: map[string]configUser{"b": configUser{Player: 2, Message: "b"}}, + Users: map[string]configUser{"b": configUser{State: configUserState{Player: 2, Message: "b"}}}, Players: []configPlayer{configPlayer{Transformation: transformation{"b": "b"}}}, Quiet: false, }, @@ -130,9 +130,11 @@ func TestServeGM(t *testing.T) { } v01.cfg.Users = map[string]configUser{ "bel": configUser{ - Player: 3, - LastTSMS: time.Now().Add(-1*time.Minute).UnixNano() / int64(time.Millisecond), - LastLag: int64(time.Second / time.Millisecond), + State: configUserState{Player: 3}, + Meta: configUserMeta{ + LastTSMS: time.Now().Add(-1*time.Minute).UnixNano() / int64(time.Millisecond), + LastLag: int64(time.Second / time.Millisecond), + }, }, "zach": configUser{}, "chase": configUser{}, @@ -175,24 +177,24 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Quiet = false v01.cfg.Users = map[string]configUser{ - "bel": configUser{ + "bel": configUser{State: configUserState{ Alias: "driver", Message: "if someone else says 'driver', then you get to play", - }, - "broadcast": configUser{ + }}, + "broadcast": configUser{State: configUserState{ Message: ":)", - }, + }}, } do(v01, "/gm/rpc/broadcastSomeoneSaidAlias", "") if !v01.cfg.Quiet { t.Error(v01.cfg.Quiet) } - if v := v01.cfg.Users["bel"]; v.Alias != "" { - t.Error(v.Alias) - } else if v.Message != "driver" { - t.Error(v.Message) + if v := v01.cfg.Users["bel"]; v.State.Alias != "" { + t.Error(v.State.Alias) + } else if v.State.Message != "driver" { + t.Error(v.State.Message) } - if bc := v01.cfg.Users["broadcast"]; bc.Message == ":)" { + if bc := v01.cfg.Users["broadcast"]; bc.State.Message == ":)" { t.Error(bc) } }) @@ -210,7 +212,7 @@ func TestServeGM(t *testing.T) { t.Run("not enough", func(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Users = map[string]configUser{ - "zach": configUser{Player: 0}, + "zach": configUser{State: configUserState{Player: 0}}, } resp := do(v01, "/gm/rpc/fillNonPlayerAliases", "[]") if resp.Code != http.StatusBadRequest { @@ -221,19 +223,19 @@ func TestServeGM(t *testing.T) { t.Run("happy", func(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Users = map[string]configUser{ - "bel": configUser{Player: 1}, - "zach": configUser{Player: 0}, + "bel": configUser{State: configUserState{Player: 1}}, + "zach": configUser{State: configUserState{Player: 0}}, } do(v01, "/gm/rpc/fillNonPlayerAliases", "[qt]") - if v := v01.cfg.Users["bel"]; v.Alias != "" { - t.Error(v.Alias) - } else if v.Player != 1 { - t.Error(v.Player) + if v := v01.cfg.Users["bel"]; v.State.Alias != "" { + t.Error(v.State.Alias) + } else if v.State.Player != 1 { + t.Error(v.State.Player) } - if v := v01.cfg.Users["zach"]; v.Alias != "qt" { - t.Error(v.Alias) - } else if v.Player != 0 { - t.Error(v.Player) + if v := v01.cfg.Users["zach"]; v.State.Alias != "qt" { + t.Error(v.State.Alias) + } else if v.State.Player != 0 { + t.Error(v.State.Player) } }) }) @@ -248,8 +250,8 @@ func TestServeGM(t *testing.T) { if resp.Code != http.StatusBadRequest { t.Error(resp) } - if v01.cfg.Users["bel"].Message != "" { - t.Error(v01.cfg.Users["bel"].Message) + if v01.cfg.Users["bel"].State.Message != "" { + t.Error(v01.cfg.Users["bel"].State.Message) } }) @@ -257,8 +259,8 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Users = map[string]configUser{"bel": {}, "zach": {}} do(v01, "/gm/rpc/vote?user=bel&payload=zach", "") - if v01.cfg.Users["bel"].Message != "//zach" { - t.Error(v01.cfg.Users["bel"].Message) + if v01.cfg.Users["bel"].State.Vote != "zach" { + t.Error(v01.cfg.Users["bel"].State.Vote) } }) @@ -281,7 +283,7 @@ func TestServeGM(t *testing.T) { t.Run("get mid vote", func(t *testing.T) { v01 := NewV01(ctx, nil) - v01.cfg.Users = map[string]configUser{"bel": {Message: "driver//zach"}} + v01.cfg.Users = map[string]configUser{"bel": {State: configUserState{Vote: "zach", Message: "driver"}}} resp := do(v01, "/gm/rpc/vote", "", "GET") var result result if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { @@ -317,14 +319,14 @@ func TestServeGM(t *testing.T) { t.Run("happy", func(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Users = map[string]configUser{ - "bel": configUser{Message: "driver//zach", Player: 1}, - "zach": configUser{Message: "pizza//bel"}, - "bill": configUser{Message: "//bel", Player: 2}, + "bel": configUser{State: configUserState{Vote: "zach", Message: "driver", Player: 1}}, + "zach": configUser{State: configUserState{Vote: "bel", Message: "pizza"}}, + "bill": configUser{State: configUserState{Vote: "bel", Message: "", Player: 2}}, } resp := do(v01, "/gm/rpc/elect?alias=pizza", "") var result result if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { - t.Error(err) + t.Errorf("%s => %v", resp.Body.Bytes(), err) } if len(result) != 2 { t.Error(result) @@ -333,13 +335,13 @@ func TestServeGM(t *testing.T) { } else if result["zach"] != 1 { t.Error(result) } - if v01.cfg.Users["bel"].Player != 0 { - t.Error(v01.cfg.Users["bel"].Player) - } else if v01.cfg.Users["zach"].Player != 1 { - t.Error(v01.cfg.Users["zach"].Player) + if v01.cfg.Users["bel"].State.Player != 0 { + t.Error(v01.cfg.Users["bel"].State.Player) + } else if v01.cfg.Users["zach"].State.Player != 1 { + t.Error(v01.cfg.Users["zach"].State.Player) } - if v01.cfg.Users["broadcast"].Message != `bel is now player 0 and zach is now player 1` { - t.Error(v01.cfg.Users["broadcast"].Message) + if v01.cfg.Users["broadcast"].State.Message != `bel is now player 0 and zach is now player 1` { + t.Error(v01.cfg.Users["broadcast"].State.Message) } }) @@ -347,9 +349,9 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Players = []configPlayer{{}} v01.cfg.Users = map[string]configUser{ - "bel": configUser{Message: "driver//zach", Player: 1}, - "zach": configUser{Message: "//bel"}, - "bill": configUser{Message: "//bel"}, + "bel": configUser{State: configUserState{Vote: "zach", Message: "driver", Player: 1}}, + "zach": configUser{State: configUserState{Vote: "bel", Message: ""}}, + "bill": configUser{State: configUserState{Vote: "bel", Message: ""}}, } resp := do(v01, "/gm/rpc/elect?alias=driver", "") var result result @@ -363,12 +365,12 @@ func TestServeGM(t *testing.T) { } else if result["zach"] != 1 { t.Error(result) } - if !strings.HasSuffix(v01.cfg.Users["broadcast"].Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].Message, ",") { - t.Error(v01.cfg.Users["broadcast"].Message) + if !strings.HasSuffix(v01.cfg.Users["broadcast"].State.Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].State.Message, ",") { + t.Error(v01.cfg.Users["broadcast"].State.Message) } assignments := map[int]int{} for _, v := range v01.cfg.Users { - assignments[v.Player] = assignments[v.Player] + 1 + assignments[v.State.Player] = assignments[v.State.Player] + 1 } if len(assignments) != 2 { t.Error(assignments) @@ -383,8 +385,8 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Players = []configPlayer{{}} v01.cfg.Users = map[string]configUser{ - "bel": configUser{Message: "driver//zach", Player: 1}, - "zach": configUser{Message: "pizza//bel"}, + "bel": configUser{State: configUserState{Vote: "zach", Message: "driver", Player: 1}}, + "zach": configUser{State: configUserState{Vote: "bel", Message: "pizza"}}, } resp := do(v01, "/gm/rpc/elect?alias=pizza", "") var result result @@ -398,12 +400,12 @@ func TestServeGM(t *testing.T) { } else if result["zach"] != 1 { t.Error(result) } - if !strings.HasSuffix(v01.cfg.Users["broadcast"].Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].Message, ",") { - t.Error(v01.cfg.Users["broadcast"].Message) + if !strings.HasSuffix(v01.cfg.Users["broadcast"].State.Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].State.Message, ",") { + t.Error(v01.cfg.Users["broadcast"].State.Message) } assignments := map[int]int{} for _, v := range v01.cfg.Users { - assignments[v.Player] = assignments[v.Player] + 1 + assignments[v.State.Player] = assignments[v.State.Player] + 1 } if len(assignments) != 2 { t.Error(assignments) @@ -421,8 +423,8 @@ func TestServeGM(t *testing.T) { for i := 0; i < 100; i++ { v01.cfg.Quiet = true v01.cfg.Users = map[string]configUser{ - "bel": configUser{Player: 1}, - "zach": configUser{Player: 2}, + "bel": configUser{State: configUserState{Player: 1}}, + "zach": configUser{State: configUserState{Player: 2}}, } v01.cfg.Players = []configPlayer{{}, {}} do(v01, "/gm/rpc/shuffle", "") @@ -433,9 +435,9 @@ func TestServeGM(t *testing.T) { t.Error(v01.cfg.Users) } else if len(v01.cfg.Players) != 2 { t.Error(v01.cfg.Users) - } else if bp := v01.cfg.Users["bel"].Player; bp != 1 && bp != 2 { + } else if bp := v01.cfg.Users["bel"].State.Player; bp != 1 && bp != 2 { t.Error(bp) - } else if zp := v01.cfg.Users["zach"].Player; zp != 1 && zp != 2 { + } else if zp := v01.cfg.Users["zach"].State.Player; zp != 1 && zp != 2 { t.Error(zp) } else if bp == zp { t.Error(bp, zp) @@ -468,7 +470,7 @@ func TestServeGM(t *testing.T) { for i := 0; i < c.users; i++ { v01.cfg.Users[strconv.Itoa(i)] = configUser{} if i < c.usersAssigned { - v01.cfg.Users[strconv.Itoa(i)] = configUser{Player: i} + v01.cfg.Users[strconv.Itoa(i)] = configUser{State: configUserState{Player: i}} } } v01.cfg.Players = make([]configPlayer, c.players) @@ -490,8 +492,8 @@ func TestServeGM(t *testing.T) { } assignments := map[int]int{} for _, v := range v01.cfg.Users { - if v.Player > 0 { - assignments[v.Player] = assignments[v.Player] + 1 + if v.State.Player > 0 { + assignments[v.State.Player] = assignments[v.State.Player] + 1 } } lesser := c.users @@ -515,7 +517,7 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Quiet = true v01.cfg.Users = map[string]configUser{ - "bel": configUser{Player: 1}, + "bel": configUser{State: configUserState{Player: 1}}, } resp := do(v01, "/gm/rpc/swap?a=bel&b=bel", "") if resp.Code != http.StatusConflict { @@ -542,8 +544,8 @@ func TestServeGM(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Quiet = true v01.cfg.Users = map[string]configUser{ - "bel": configUser{Player: 1}, - "zach": configUser{Player: 2}, + "bel": configUser{State: configUserState{Player: 1}}, + "zach": configUser{State: configUserState{Player: 2}}, } resp := do(v01, "/gm/rpc/swap?a=bel&b=zach", "") if resp.Code != http.StatusOK { @@ -552,9 +554,9 @@ func TestServeGM(t *testing.T) { if v01.cfg.Quiet { t.Error(v01.cfg.Quiet) } - if v01.cfg.Users["bel"].Player != 2 { + if v01.cfg.Users["bel"].State.Player != 2 { t.Error(v01.cfg.Users["bel"]) - } else if v01.cfg.Users["zach"].Player != 1 { + } else if v01.cfg.Users["zach"].State.Player != 1 { t.Error(v01.cfg.Users["zach"]) } }) diff --git a/src/device/input/parse/v01/testdata/v01.yaml b/src/device/input/parse/v01/testdata/v01.yaml index c5ca9d6..bd85e01 100644 --- a/src/device/input/parse/v01/testdata/v01.yaml +++ b/src/device/input/parse/v01/testdata/v01.yaml @@ -3,11 +3,13 @@ feedback: ttsurl: http://localhost:15002 users: bel: - player: 0 - message: "hi" - alias: driver - tsms: 1 - lastlag: 2 + state: + player: 0 + message: "hi" + alias: driver + meta: + tsms: 1 + lastlag: 2 players: - buttons: up: "w" diff --git a/src/device/input/parse/v01/v01.go b/src/device/input/parse/v01/v01.go index 7c00cfd..b65729c 100644 --- a/src/device/input/parse/v01/v01.go +++ b/src/device/input/parse/v01/v01.go @@ -79,10 +79,10 @@ func (v01 *V01) telemetry(msg message) { v01.cfg.Users = map[string]configUser{} } u := v01.cfg.Users[msg.U] - u.LastLag = time.Now().UnixNano()/int64(time.Millisecond) - msg.T - u.LastTSMS = msg.T + u.Meta.LastLag = time.Now().UnixNano()/int64(time.Millisecond) - msg.T + u.Meta.LastTSMS = msg.T if FlagDebug { - log.Printf("%s|%dms", msg.U, u.LastLag) + log.Printf("%s|%dms", msg.U, u.Meta.LastLag) } v01.cfg.Users[msg.U] = u } @@ -92,12 +92,12 @@ func (v01 *V01) transform(msg message) message { return msg } user := v01.cfg.Users[msg.U] - if user.Player < 1 { + if user.State.Player < 1 { msg.Y = "" msg.N = "" return msg } - player := v01.cfg.Players[user.Player-1] + player := v01.cfg.Players[user.State.Player-1] msg.Y = player.Transformation.pipe(msg.Y) msg.N = player.Transformation.pipe(msg.N) return msg diff --git a/src/device/input/parse/v01/v01_exported_test.go b/src/device/input/parse/v01/v01_exported_test.go index 1afbc98..cb76d9a 100644 --- a/src/device/input/parse/v01/v01_exported_test.go +++ b/src/device/input/parse/v01/v01_exported_test.go @@ -44,7 +44,8 @@ func TestV01WithCfg(t *testing.T) { os.WriteFile(p, []byte(` users: bel: - player: 2 + state: + player: 2 players: - transformation: w: t @@ -87,10 +88,12 @@ func TestV01Feedback(t *testing.T) { ttsurl: http://localhost:15002 users: bel: - player: 2 - message: to bel + state: + player: 2 + message: to bel broadcast: - message: to everyone + state: + message: to everyone players: - transformation: w: t