Add generation and sending of game-specific data.

This commit is contained in:
Patrick Fairbank
2018-04-11 23:26:36 -07:00
parent 423baf992c
commit 0c475b0a20
18 changed files with 228 additions and 67 deletions

View File

@@ -21,7 +21,8 @@ CREATE TABLE matches (
blue3issurrogate bool,
status VARCHAR(16),
startedat DATETIME,
winner VARCHAR(16)
winner VARCHAR(16),
gamespecificdata VARCHAR(3)
);
CREATE UNIQUE INDEX type_displayname ON matches(type, displayname);

View File

@@ -24,11 +24,12 @@ const (
const (
PreMatch = 0
StartMatch = 1
AutoPeriod = 2
PausePeriod = 3
TeleopPeriod = 4
EndgamePeriod = 5
PostMatch = 6
WarmupPeriod = 2
AutoPeriod = 3
PausePeriod = 4
TeleopPeriod = 5
EndgamePeriod = 6
PostMatch = 7
)
type Arena struct {
@@ -83,6 +84,7 @@ type ArenaStatus struct {
CanStartMatch bool
PlcIsHealthy bool
FieldEstop bool
GameSpecificData string
}
type AllianceStation struct {
@@ -286,7 +288,12 @@ func (arena *Arena) SubstituteTeam(teamId int, station string) error {
func (arena *Arena) StartMatch() error {
err := arena.checkCanStartMatch()
if err == nil {
// Save the match start time to the database for posterity.
// Generate game-specific data or allow manual input for test matches.
if arena.CurrentMatch.Type != "test" || !game.IsValidGameSpecificData(arena.CurrentMatch.GameSpecificData) {
arena.CurrentMatch.GameSpecificData = game.GenerateGameSpecificData()
}
// Save the match start time and game-specifc data to the database for posterity.
arena.CurrentMatch.StartedAt = time.Now()
if arena.CurrentMatch.Type != "test" {
arena.Database.SaveMatch(arena.CurrentMatch)
@@ -361,23 +368,35 @@ func (arena *Arena) Update() {
auto = true
enabled = false
case StartMatch:
arena.MatchState = AutoPeriod
arena.MatchState = WarmupPeriod
arena.MatchStartTime = time.Now()
arena.LastMatchTimeSec = -1
auto = true
enabled = true
sendDsPacket = true
enabled = false
arena.AudienceDisplayScreen = "match"
arena.AudienceDisplayNotifier.Notify(nil)
if !arena.MuteMatchSounds {
arena.PlaySoundNotifier.Notify("match-start")
}
arena.FieldTestMode = ""
arena.Plc.ResetCounts()
arena.sendGameSpecificDataPacket()
if !arena.MuteMatchSounds {
arena.PlaySoundNotifier.Notify("match-warmup")
}
case WarmupPeriod:
auto = true
enabled = false
if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec) {
arena.MatchState = AutoPeriod
auto = true
enabled = true
sendDsPacket = true
if !arena.MuteMatchSounds {
arena.PlaySoundNotifier.Notify("match-start")
}
}
case AutoPeriod:
auto = true
enabled = true
if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec) {
if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec) {
arena.MatchState = PausePeriod
auto = false
enabled = false
@@ -389,7 +408,8 @@ func (arena *Arena) Update() {
case PausePeriod:
auto = false
enabled = false
if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec) {
if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec) {
arena.MatchState = TeleopPeriod
auto = false
enabled = true
@@ -401,8 +421,8 @@ func (arena *Arena) Update() {
case TeleopPeriod:
auto = false
enabled = true
if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+
game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) {
if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) {
arena.MatchState = EndgamePeriod
sendDsPacket = false
if !arena.MuteMatchSounds {
@@ -412,8 +432,8 @@ func (arena *Arena) Update() {
case EndgamePeriod:
auto = false
enabled = true
if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+
game.MatchTiming.TeleopDurationSec) {
if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) {
arena.MatchState = PostMatch
auto = false
enabled = false
@@ -481,7 +501,7 @@ func (arena *Arena) BlueScoreSummary() *game.ScoreSummary {
func (arena *Arena) GetStatus() *ArenaStatus {
return &ArenaStatus{arena.AllianceStations, arena.MatchState, arena.checkCanStartMatch() == nil,
arena.Plc.IsHealthy, arena.Plc.GetFieldEstop()}
arena.Plc.IsHealthy, arena.Plc.GetFieldEstop(), arena.CurrentMatch.GameSpecificData}
}
// Loads a team into an alliance station, cleaning up the previous team there if there is one.
@@ -587,6 +607,19 @@ func (arena *Arena) sendDsPacket(auto bool, enabled bool) {
arena.lastDsPacketTime = time.Now()
}
func (arena *Arena) sendGameSpecificDataPacket() {
for _, allianceStation := range arena.AllianceStations {
dsConn := allianceStation.DsConn
if dsConn != nil {
err := dsConn.sendGameSpecificDataPacket(arena.CurrentMatch.GameSpecificData)
if err != nil {
log.Printf("Error sending game-specific data packet to Team %d: %v", dsConn.TeamId, err)
}
}
}
arena.lastDsPacketTime = time.Now()
}
// Returns the alliance station identifier for the given team, or the empty string if the team is not present
// in the current match.
func (arena *Arena) getAssignedAllianceStation(teamId int) string {

View File

@@ -87,6 +87,15 @@ func TestArenaMatchFlow(t *testing.T) {
err = arena.StartMatch()
assert.Nil(t, err)
arena.Update()
assert.Equal(t, WarmupPeriod, arena.MatchState)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled)
arena.Update()
assert.Equal(t, WarmupPeriod, arena.MatchState)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second)
arena.Update()
assert.Equal(t, AutoPeriod, arena.MatchState)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled)
@@ -94,7 +103,8 @@ func TestArenaMatchFlow(t *testing.T) {
assert.Equal(t, AutoPeriod, arena.MatchState)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec) * time.Second)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+
game.MatchTiming.AutoDurationSec) * time.Second)
arena.Update()
assert.Equal(t, PausePeriod, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)
@@ -103,8 +113,8 @@ func TestArenaMatchFlow(t *testing.T) {
assert.Equal(t, PausePeriod, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec) * time.Second)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+
game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec) * time.Second)
arena.Update()
assert.Equal(t, TeleopPeriod, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)
@@ -142,8 +152,9 @@ func TestArenaMatchFlow(t *testing.T) {
// Check endgame and match end.
arena.MatchStartTime = time.Now().
Add(-time.Duration(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+
game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) * time.Second)
Add(-time.Duration(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) *
time.Second)
arena.Update()
assert.Equal(t, EndgamePeriod, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)
@@ -152,8 +163,9 @@ func TestArenaMatchFlow(t *testing.T) {
assert.Equal(t, EndgamePeriod, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)
assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec+
game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) * time.Second)
arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+
game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) *
time.Second)
arena.Update()
assert.Equal(t, PostMatch, arena.MatchState)
assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto)

View File

@@ -387,3 +387,25 @@ func (dsConn *DriverStationConnection) handleTcpConnection(arena *Arena) {
}
}
}
func (dsConn *DriverStationConnection) sendGameSpecificDataPacket(gameSpecificData string) error {
byteData := []byte(gameSpecificData)
size := len(byteData)
packet := make([]byte, size+4)
packet[0] = 0 // Packet size
packet[1] = byte(size + 2) // Packet size
packet[2] = 28 // Packet type
packet[3] = byte(size) // Data size
// Fill the rest of the packet with the data.
for i, character := range byteData {
packet[i+4] = character
}
if dsConn.tcpConn != nil {
_, err := dsConn.tcpConn.Write(packet)
return err
}
return nil
}

View File

@@ -0,0 +1,25 @@
// Copyright 2018 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Logic to generate the 2018 game-specific data.
package game
import "math/rand"
var validGameSpecificDatas = []string{"RRR", "LLL", "RLR", "LRL"}
// Returns a random configuration.
func GenerateGameSpecificData() string {
return validGameSpecificDatas[rand.Intn(len(validGameSpecificDatas))]
}
// Returns true if the given game specific data is valid.
func IsValidGameSpecificData(gameSpecificData string) bool {
for _, data := range validGameSpecificDatas {
if data == gameSpecificData {
return true
}
}
return false
}

View File

@@ -0,0 +1,38 @@
// Copyright 2018 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
package game
import (
"github.com/stretchr/testify/assert"
"math/rand"
"testing"
)
func TestGenerateGameSpecificData(t *testing.T) {
rand.Seed(0)
// Make sure all possibilities are hit at least twice.
assert.Equal(t, "RLR", GenerateGameSpecificData())
assert.Equal(t, "RLR", GenerateGameSpecificData())
assert.Equal(t, "LLL", GenerateGameSpecificData())
assert.Equal(t, "RLR", GenerateGameSpecificData())
assert.Equal(t, "LRL", GenerateGameSpecificData())
assert.Equal(t, "RRR", GenerateGameSpecificData())
assert.Equal(t, "LRL", GenerateGameSpecificData())
assert.Equal(t, "LLL", GenerateGameSpecificData())
assert.Equal(t, "RRR", GenerateGameSpecificData())
assert.Equal(t, "RRR", GenerateGameSpecificData())
}
func TestIsValidGameSpecificData(t *testing.T) {
for _, data := range validGameSpecificDatas {
assert.True(t, IsValidGameSpecificData(data))
}
assert.False(t, IsValidGameSpecificData(""))
assert.False(t, IsValidGameSpecificData("R"))
assert.False(t, IsValidGameSpecificData("RL"))
assert.False(t, IsValidGameSpecificData("RRL"))
assert.False(t, IsValidGameSpecificData("RRRL"))
}

View File

@@ -8,21 +8,23 @@ package game
import "time"
var MatchTiming = struct {
WarmupDurationSec int
AutoDurationSec int
PauseDurationSec int
TeleopDurationSec int
EndgameTimeLeftSec int
}{15, 2, 135, 30}
}{3, 15, 2, 135, 30}
func GetAutoEndTime(matchStartTime time.Time) time.Time {
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec))
return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec) * time.Second)
}
func GetTeleopStartTime(matchStartTime time.Time) time.Time {
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec + MatchTiming.PauseDurationSec))
return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec+
MatchTiming.PauseDurationSec) * time.Second)
}
func GetMatchEndTime(matchStartTime time.Time) time.Time {
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
MatchTiming.TeleopDurationSec) * time.Second)
return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec+
MatchTiming.PauseDurationSec+MatchTiming.TeleopDurationSec) * time.Second)
}

View File

@@ -34,6 +34,7 @@ type Match struct {
Status string
StartedAt time.Time
Winner string
GameSpecificData string
}
var ElimRoundNames = map[int]string{1: "F", 2: "SF", 4: "QF", 8: "EF"}

View File

@@ -21,7 +21,7 @@ func TestMatchCrud(t *testing.T) {
db := setupTestDb(t)
match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false,
5, false, 6, false, "", time.Now().UTC(), ""}
5, false, 6, false, "", time.Now().UTC(), "", ""}
db.CreateMatch(&match)
match2, err := db.GetMatchById(1)
assert.Nil(t, err)
@@ -46,7 +46,7 @@ func TestTruncateMatches(t *testing.T) {
db := setupTestDb(t)
match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false,
5, false, 6, false, "", time.Now().UTC(), ""}
5, false, 6, false, "", time.Now().UTC(), "", ""}
db.CreateMatch(&match)
db.TruncateMatches()
match2, err := db.GetMatchById(1)
@@ -83,13 +83,13 @@ func TestGetMatchesByType(t *testing.T) {
db := setupTestDb(t)
match := Match{0, "qualification", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false,
5, false, 6, false, "", time.Now().UTC(), ""}
5, false, 6, false, "", time.Now().UTC(), "", ""}
db.CreateMatch(&match)
match2 := Match{0, "practice", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5,
false, 6, false, "", time.Now().UTC(), ""}
false, 6, false, "", time.Now().UTC(), "", ""}
db.CreateMatch(&match2)
match3 := Match{0, "practice", "2", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5,
false, 6, false, "", time.Now().UTC(), ""}
false, 6, false, "", time.Now().UTC(), "", ""}
db.CreateMatch(&match3)
matches, err := db.GetMatchesByType("test")

Binary file not shown.

View File

@@ -18,7 +18,8 @@ var toggleBypass = function(station) {
// Sends a websocket message to start the match.
var startMatch = function() {
websocket.send("startMatch", { muteMatchSounds: $("#muteMatchSounds").prop("checked") });
websocket.send("startMatch",
{ muteMatchSounds: $("#muteMatchSounds").prop("checked"), gameSpecificData: $("#gameSpecificData").val() });
};
// Sends a websocket message to abort the match.
@@ -135,7 +136,11 @@ var handleStatus = function(data) {
$("#plcStatus").text("Not Connected");
$("#plcStatus").attr("data-ready", false);
}
$("#fieldEstop").attr("data-ready", !data.FieldEstop)
$("#fieldEstop").attr("data-ready", !data.FieldEstop);
if (matchStates[data.MatchState] != "PRE_MATCH") {
$("#gameSpecificData").val(data.GameSpecificData);
}
};
// Handles a websocket message to update the match time countdown.

View File

@@ -6,11 +6,12 @@
var matchStates = {
0: "PRE_MATCH",
1: "START_MATCH",
2: "AUTO_PERIOD",
3: "PAUSE_PERIOD",
4: "TELEOP_PERIOD",
5: "ENDGAME_PERIOD",
6: "POST_MATCH"
2: "WARMUP_PERIOD",
3: "AUTO_PERIOD",
4: "PAUSE_PERIOD",
5: "TELEOP_PERIOD",
6: "ENDGAME_PERIOD",
7: "POST_MATCH"
};
var matchTiming;
@@ -28,6 +29,9 @@ var translateMatchTime = function(data, callback) {
matchStateText = "PRE-MATCH";
break;
case "START_MATCH":
case "WARMUP_PERIOD":
matchStateText = "WARMUP";
break;
case "AUTO_PERIOD":
matchStateText = "AUTONOMOUS";
break;
@@ -49,14 +53,15 @@ var translateMatchTime = function(data, callback) {
var getCountdown = function(matchState, matchTimeSec) {
switch (matchStates[matchState]) {
case "PRE_MATCH":
return matchTiming.AutoDurationSec;
case "START_MATCH":
case "WARMUP_PERIOD":
return matchTiming.AutoDurationSec;
case "AUTO_PERIOD":
return matchTiming.AutoDurationSec - matchTimeSec;
return matchTiming.WarmupDurationSec + matchTiming.AutoDurationSec - matchTimeSec;
case "TELEOP_PERIOD":
case "ENDGAME_PERIOD":
return matchTiming.TeleopDurationSec + matchTiming.AutoDurationSec + matchTiming.PauseDurationSec -
matchTimeSec;
return matchTiming.WarmupDurationSec + matchTiming.AutoDurationSec + matchTiming.TeleopDurationSec +
matchTiming.PauseDurationSec - matchTimeSec;
default:
return 0;
}

View File

@@ -158,6 +158,7 @@
{{"{{/each}}"}}
</table>
</script>
<audio id="match-warmup" src="/static/audio/match_warmup.wav" preload="auto"></audio>
<audio id="match-start" src="/static/audio/match_start.wav" preload="auto"></audio>
<audio id="match-end" src="/static/audio/match_end.wav" preload="auto"></audio>
<audio id="match-abort" src="/static/audio/match_abort.mp3" preload="auto"></audio>

View File

@@ -167,7 +167,7 @@
</div>
</div>
<div class="col-lg-4">
Alliance Station Display
<p>Alliance Station Display</p>
<div class="form-group">
<div class="radio">
<label>
@@ -187,6 +187,10 @@
onclick="setAllianceStationDisplay();">Logo
</label>
</div>
<br />
<p>Game-Specific Data</p>
<input type="text" id="gameSpecificData" size="10"
{{if eq .CurrentMatchType "qualification" }}disabled{{end}} />
</div>
{{if .EventSettings.PlcAddress}}
PLC Status

View File

@@ -4,6 +4,8 @@
package web
import (
"fmt"
"github.com/Team254/cheesy-arena/game"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"sync"
@@ -59,7 +61,11 @@ func TestAllianceStationDisplayWebsocket(t *testing.T) {
web.arena.AllianceStations["B3"].Bypass = true
web.arena.StartMatch()
web.arena.Update()
readWebsocketType(t, ws, "matchTime")
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second)
web.arena.Update()
messages := readWebsocketMultiple(t, ws, 2)
fmt.Println(messages)
_, ok := messages["status"]
assert.True(t, ok)
_, ok = messages["matchTime"]

View File

@@ -55,7 +55,7 @@ func TestAudienceDisplayWebsocket(t *testing.T) {
}
sound, ok := messages["playSound"]
if assert.True(t, ok) {
assert.Equal(t, "match-start", sound)
assert.Equal(t, "match-warmup", sound)
}
_, ok = messages["matchTime"]
assert.True(t, ok)

View File

@@ -333,7 +333,8 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request
web.arena.AllianceStations[station].Bypass = !web.arena.AllianceStations[station].Bypass
case "startMatch":
args := struct {
MuteMatchSounds bool
MuteMatchSounds bool
GameSpecificData string
}{}
err = mapstructure.Decode(data, &args)
if err != nil {
@@ -341,6 +342,7 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request
continue
}
web.arena.MuteMatchSounds = args.MuteMatchSounds
web.arena.CurrentMatch.GameSpecificData = args.GameSpecificData
err = web.arena.StartMatch()
if err != nil {
websocket.WriteError(err.Error())

View File

@@ -351,43 +351,47 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) {
web.arena.AllianceStations["B3"].Bypass = true
web.arena.StartMatch()
web.arena.Update()
messages := readWebsocketMultiple(t, ws, 4)
statusReceived, matchTime := getStatusMatchTime(t, messages)
assert.Equal(t, true, statusReceived)
assert.Equal(t, 2, matchTime.MatchState)
assert.Equal(t, 0, matchTime.MatchTimeSec)
_, ok := messages["setAudienceDisplay"]
messages := readWebsocketMultiple(t, ws, 3)
_, ok := messages["matchTime"]
assert.True(t, ok)
_, ok = messages["setAudienceDisplay"]
assert.True(t, ok)
_, ok = messages["setAllianceStationDisplay"]
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second)
web.arena.Update()
messages = readWebsocketMultiple(t, ws, 2)
statusReceived, matchTime := getStatusMatchTime(t, messages)
assert.Equal(t, true, statusReceived)
assert.Equal(t, 3, matchTime.MatchState)
assert.Equal(t, 3, matchTime.MatchTimeSec)
assert.True(t, ok)
web.arena.ScoringStatusNotifier.Notify(nil)
readWebsocketType(t, ws, "scoringStatus")
// Should get a tick notification when an integer second threshold is crossed.
web.arena.MatchStartTime = time.Now().Add(-time.Second + 10*time.Millisecond) // Not crossed yet
web.arena.Update()
web.arena.MatchStartTime = time.Now().Add(-time.Second - 10*time.Millisecond) // Crossed
web.arena.Update()
err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime)
assert.Nil(t, err)
assert.Equal(t, 3, matchTime.MatchState)
assert.Equal(t, 1, matchTime.MatchTimeSec)
web.arena.MatchStartTime = time.Now().Add(-2*time.Second + 10*time.Millisecond) // Not crossed yet
web.arena.Update()
web.arena.MatchStartTime = time.Now().Add(-2*time.Second - 10*time.Millisecond) // Crossed
web.arena.Update()
err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime)
assert.Nil(t, err)
assert.Equal(t, 2, matchTime.MatchState)
assert.Equal(t, 1, matchTime.MatchTimeSec)
err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime)
assert.Nil(t, err)
assert.Equal(t, 2, matchTime.MatchState)
assert.Equal(t, 3, matchTime.MatchState)
assert.Equal(t, 2, matchTime.MatchTimeSec)
// Check across a match state boundary.
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec) * time.Second)
web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+
game.MatchTiming.AutoDurationSec) * time.Second)
web.arena.Update()
statusReceived, matchTime = readWebsocketStatusMatchTime(t, ws)
assert.Equal(t, true, statusReceived)
assert.Equal(t, 3, matchTime.MatchState)
assert.Equal(t, game.MatchTiming.AutoDurationSec, matchTime.MatchTimeSec)
assert.Equal(t, 4, matchTime.MatchState)
assert.Equal(t, game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec, matchTime.MatchTimeSec)
}
// Handles the status and matchTime messages arriving in either order.