Skip to content

Commit 923f143

Browse files
committed
Implement ready and start functionality
1 parent cfcfdc0 commit 923f143

File tree

7 files changed

+220
-39
lines changed

7 files changed

+220
-39
lines changed

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ docker run -d -p <port-on-host-machine>:8080 --name hoverrace codegameproject/ho
3838
## Event Flow
3939

4040
1. Send a `ready` event to the server when you think the game should begin.
41-
2. A `ready_players` event updates all players on the readiness of all players.
42-
3. A `start` event is sent to all players when the race begins. It includes the positions of all checkpoints.
43-
4. A `next_checkpoint` event is sent to every player to tell them where their next target is located.
44-
5. Send a `throttle` event to begin moving.
45-
6. A `hovercrafts` event is sent repeatedly to all players to update them on the state of all hovercrafts.
46-
7. A `finished` event is sent to all players when a player crosses the finish line. The game keeps going until all players have finished.
47-
8. Send a `ready` event if you want to play again.
41+
2. The `ready_players` event updates all players on the readiness of all players.
42+
3. The `checkpoints` event contains the checkpoints you have to reach.
43+
4. The `countdown` event counts down 5 seconds.
44+
5. The `start` event is sent to all players when the race begins.
45+
6. Send a `throttle` event to begin moving.
46+
7. The `hovercrafts` event is sent repeatedly to all players to update them on the state of all hovercrafts.
47+
8. The `checkpoints` event is sent again when you cross a checkpoint.
48+
9. The `finished` event is sent to all players when a player crosses the finish line. The game keeps going until all players have finished.
49+
10. Send a `ready` event if you want to play again.
4850

4951
## Building
5052

events.cge

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ version 0.3
33

44
// The `start` event is sent when the race begins.
55
// The game begins once at least 2 players have joined and all players have sent the `ready` event.
6-
event start {
7-
// The positions of all checkpoints.
6+
event start {}
7+
8+
// The `countdown` counts down from 5. When the value reaches 0 a `start` event will be sent instead of the `countdown` event.
9+
event countdown {
10+
// The current value of the countdown (5-1).
11+
value: int
12+
}
13+
14+
// The `checkpoints` event contains all of the remaining checkpoints.
15+
event checkpoints {
16+
// The positions of all the remaining checkpoints.
817
checkpoints: list<position>
918
}
1019

@@ -30,33 +39,31 @@ event throttle {
3039
event hovercrafts {
3140
// All hovercrafts mapped to their respective player IDs.
3241
hovercrafts: map<hovercraft>,
33-
// The time in UNIX seconds when this event occured.
42+
// The time in UNIX milliseconds when this event occured.
3443
time: int64
3544
}
3645

37-
// The `next_checkpoint` tells you the position of the next checkpoint.
38-
event next_checkpoint {
39-
// The position of the next checkpoint.
40-
pos: position
41-
}
42-
4346
// The `finished` event is sent when a player crosses the finish line.
4447
event finished {
45-
// The ID of the player who reached the finish line.
46-
player: string,
4748
// The place which the player has reached.
48-
place: int
49+
place: int,
50+
// The amount of time in milliseconds the player needed to finish the race.
51+
duration: int64
4952
}
5053

54+
// A hovercraft is a circle with a diameter of 1 unit.
5155
type hovercraft {
52-
// The position of the hovercraft.
56+
// The position of the center of the hovercraft.
5357
pos: position,
5458
// The current speed of the hovercraft.
5559
speed: float,
5660
// The angle in degrees the hovercraft is facing (up = 0°).
57-
angle: float
61+
angle: float,
62+
// The amount of reached checkpoints.
63+
checkpoints: int
5864
}
5965

66+
// One unit equals the width of the hovercrafts and checkpoints.
6067
type position {
6168
x: float,
6269
y: float

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/code-game-project/hoverrace
33
go 1.18
44

55
require (
6-
github.com/code-game-project/go-server v0.7.1
6+
github.com/code-game-project/go-server v0.7.2
77
github.com/spf13/pflag v1.0.5
88
)
99

@@ -14,5 +14,5 @@ require (
1414
github.com/gorilla/websocket v1.5.0 // indirect
1515
github.com/mattn/go-colorable v0.1.12 // indirect
1616
github.com/mattn/go-isatty v0.0.14 // indirect
17-
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
17+
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect
1818
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ github.com/Bananenpro/log v0.0.0-20220531131028-71d66f5df6ae h1:3soztN/rE1IKMZse
22
github.com/Bananenpro/log v0.0.0-20220531131028-71d66f5df6ae/go.mod h1:deSMMVnGJzim4MopRkA5zp+QJ5I0p+Fg3iBD2edIZpU=
33
github.com/code-game-project/go-server v0.7.1 h1:2gD2uYNMf7fMqfMuJM2AckGB8H+EZ68SFRTObUBu25I=
44
github.com/code-game-project/go-server v0.7.1/go.mod h1:V9vz3gbpTXRlRGzcr/Wmyx47W3Ex8sZ6Odtri8pvcEs=
5+
github.com/code-game-project/go-server v0.7.2 h1:+pOxZs6FYcJzU+76RldYA0nrPCC400vvaCDMAhFSMSo=
6+
github.com/code-game-project/go-server v0.7.2/go.mod h1:V9vz3gbpTXRlRGzcr/Wmyx47W3Ex8sZ6Odtri8pvcEs=
57
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
68
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
79
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -18,3 +20,5 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
1820
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1921
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
2022
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23+
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s=
24+
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

hoverrace/event_definitions.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ import "github.com/code-game-project/go-server/cg"
77
const StartEvent cg.EventName = "start"
88

99
type StartEventData struct {
10-
// The positions of all checkpoints.
10+
}
11+
12+
// The `countdown` counts down from 5. When the value reaches 0 a `start` event will be sent instead of the `countdown` event.
13+
const CountdownEvent cg.EventName = "countdown"
14+
15+
type CountdownEventData struct {
16+
// The current value of the countdown (5-1).
17+
Value int `json:"value"`
18+
}
19+
20+
// The `checkpoints` event contains all of the remaining checkpoints.
21+
const CheckpointsEvent cg.EventName = "checkpoints"
22+
23+
type CheckpointsEventData struct {
24+
// The positions of all the remaining checkpoints.
1125
Checkpoints []Position `json:"checkpoints"`
1226
}
1327

@@ -42,37 +56,33 @@ const HovercraftsEvent cg.EventName = "hovercrafts"
4256
type HovercraftsEventData struct {
4357
// All hovercrafts mapped to their respective player IDs.
4458
Hovercrafts map[string]Hovercraft `json:"hovercrafts"`
45-
// The time in UNIX seconds when this event occured.
59+
// The time in UNIX milliseconds when this event occured.
4660
Time int64 `json:"time"`
4761
}
4862

49-
// The `next_checkpoint` tells you the position of the next checkpoint.
50-
const NextCheckpointEvent cg.EventName = "next_checkpoint"
51-
52-
type NextCheckpointEventData struct {
53-
// The position of the next checkpoint.
54-
Pos Position `json:"pos"`
55-
}
56-
5763
// The `finished` event is sent when a player crosses the finish line.
5864
const FinishedEvent cg.EventName = "finished"
5965

6066
type FinishedEventData struct {
61-
// The ID of the player who reached the finish line.
62-
Player string `json:"player"`
6367
// The place which the player has reached.
6468
Place int `json:"place"`
69+
// The amount of time in milliseconds the player needed to finish the race.
70+
Duration int64 `json:"duration"`
6571
}
6672

73+
// A hovercraft is a circle with a diameter of 1 unit.
6774
type Hovercraft struct {
68-
// The position of the hovercraft.
75+
// The position of the center of the hovercraft.
6976
Pos Position `json:"pos"`
7077
// The current speed of the hovercraft.
7178
Speed float64 `json:"speed"`
7279
// The angle in degrees the hovercraft is facing (up = 0°).
7380
Angle float64 `json:"angle"`
81+
// The amount of reached checkpoints.
82+
Checkpoints int `json:"checkpoints"`
7483
}
7584

85+
// One unit equals the width of the hovercrafts and checkpoints.
7686
type Position struct {
7787
X float64 `json:"x"`
7888
Y float64 `json:"y"`

hoverrace/game.go

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@ import (
88
)
99

1010
type Game struct {
11-
cg *cg.Game
11+
cg *cg.Game
12+
players map[string]*Player
13+
checkpoints []Position
14+
finishedPlayers int
15+
running bool
16+
startTime time.Time
1217
}
1318

1419
const targetFrameTime time.Duration = 1 * time.Second / 30
1520

1621
func NewGame(cgGame *cg.Game) *Game {
1722
game := &Game{
18-
cg: cgGame,
23+
cg: cgGame,
24+
players: make(map[string]*Player),
1925
}
2026
game.cg.OnPlayerJoined = game.onPlayerJoined
2127
game.cg.OnPlayerLeft = game.onPlayerLeft
@@ -32,28 +38,154 @@ func (g *Game) Run() {
3238
break
3339
}
3440
g.handleEvent(event.Player, event.Event)
41+
g.update()
3542
}
3643
time.Sleep(targetFrameTime - time.Now().Sub(frameStart))
3744
}
3845
}
3946

40-
func (g *Game) onPlayerJoined(player *cg.Player) {
47+
func (g *Game) onPlayerJoined(cgPlayer *cg.Player) {
48+
if g.running {
49+
cgPlayer.Send("server", cg.ErrorEvent, cg.ErrorEventData{
50+
Message: "the game has already begun",
51+
})
52+
cgPlayer.Leave()
53+
return
54+
}
4155

56+
g.players[cgPlayer.Id] = &Player{
57+
id: cgPlayer.Id,
58+
cg: cgPlayer,
59+
username: cgPlayer.Username,
60+
game: g,
61+
}
4262
}
4363

4464
func (g *Game) onPlayerLeft(player *cg.Player) {
65+
_, ok := g.players[player.Id]
66+
if !ok {
67+
return
68+
}
69+
70+
delete(g.players, player.Id)
71+
72+
if !g.running {
73+
for _, p := range g.players {
74+
if !p.ready {
75+
return
76+
}
77+
}
78+
g.start()
79+
} else {
80+
if g.finishedPlayers == len(g.players) {
81+
g.finish()
82+
}
83+
}
4584

4685
}
4786

4887
func (g *Game) onPlayerSocketConnected(player *cg.Player, socket *cg.Socket) {
88+
if !g.running {
89+
return
90+
}
91+
92+
p := g.players[player.Id]
93+
p.cg.Send("server", CheckpointsEvent, CheckpointsEventData{
94+
Checkpoints: p.checkpoints,
95+
})
4996

97+
socket.Send("server", StartEvent, StartEventData{})
98+
99+
for _, player := range g.players {
100+
if player.finished {
101+
socket.Send(player.id, FinishedEvent, FinishedEventData{
102+
Place: player.place,
103+
Duration: player.duration,
104+
})
105+
}
106+
}
107+
}
108+
109+
func (g *Game) update() {
110+
if !g.running {
111+
return
112+
}
50113
}
51114

52115
func (g *Game) handleEvent(player *cg.Player, event cg.Event) {
53116
switch event.Name {
117+
case ReadyEvent:
118+
g.handleReady(player.Id)
54119
default:
55120
player.Send(player.Id, cg.ErrorEvent, cg.ErrorEventData{
56121
Message: fmt.Sprintf("unexpected event: %s", event.Name),
57122
})
58123
}
59124
}
125+
126+
func (g *Game) handleReady(playerId string) {
127+
player := g.players[playerId]
128+
if g.running {
129+
player.cg.Send(playerId, cg.ErrorEvent, cg.ErrorEventData{
130+
Message: "the game has already begun",
131+
})
132+
return
133+
}
134+
135+
player.ready = true
136+
137+
if len(g.players) < 2 {
138+
return
139+
}
140+
141+
readyPlayers := make([]string, 0, len(g.players))
142+
for _, p := range g.players {
143+
if p.ready {
144+
readyPlayers = append(readyPlayers, p.id)
145+
}
146+
}
147+
148+
g.cg.Send("server", ReadyPlayersEvent, ReadyPlayersEventData{
149+
Players: readyPlayers,
150+
})
151+
152+
if len(readyPlayers) == len(g.players) {
153+
g.start()
154+
}
155+
}
156+
157+
func (g *Game) start() {
158+
g.finishedPlayers = 0
159+
g.createCheckpoints()
160+
161+
for _, player := range g.players {
162+
player.checkpoints = g.checkpoints
163+
}
164+
165+
g.running = true
166+
g.startTime = time.Now()
167+
168+
g.cg.Send("server", CheckpointsEvent, CheckpointsEventData{
169+
Checkpoints: g.checkpoints,
170+
})
171+
172+
for countdown := 5; countdown > 0; countdown-- {
173+
g.cg.Send("server", CountdownEvent, CountdownEventData{
174+
Value: countdown,
175+
})
176+
time.Sleep(1 * time.Second)
177+
}
178+
179+
g.cg.Send("server", StartEvent, StartEventData{})
180+
}
181+
182+
func (g *Game) finish() {
183+
for _, player := range g.players {
184+
player.reset()
185+
}
186+
g.running = false
187+
}
188+
189+
func (g *Game) createCheckpoints() {
190+
g.checkpoints = make([]Position, 5)
191+
}

hoverrace/player.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package hoverrace
2+
3+
import (
4+
"github.com/code-game-project/go-server/cg"
5+
)
6+
7+
type Player struct {
8+
id string
9+
username string
10+
11+
ready bool
12+
13+
finished bool
14+
place int
15+
duration int64
16+
17+
checkpoints []Position
18+
19+
cg *cg.Player
20+
game *Game
21+
}
22+
23+
func (p *Player) reset() {
24+
p.ready = false
25+
p.finished = false
26+
}

0 commit comments

Comments
 (0)