@@ -4,12 +4,8 @@ import (
4
4
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/config"
5
5
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"
6
6
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"
7
- "github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
8
7
"github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
9
- "github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
10
- "github.com/pkg/errors"
11
8
"log"
12
- "math"
13
9
"sync"
14
10
"time"
15
11
)
@@ -32,12 +28,6 @@ type GameController struct {
32
28
VisionReceiver * vision.Receiver
33
29
}
34
30
35
- type TeamChoice struct {
36
- Team Team
37
- Event Event
38
- IssueTime time.Time
39
- }
40
-
41
31
// NewGameController creates a new RefBox
42
32
func NewGameController () (c * GameController ) {
43
33
@@ -86,150 +76,45 @@ func (c *GameController) Run() {
86
76
c .Engine .State .TeamState [TeamBlue ].Name }
87
77
88
78
c .timer .Start ()
89
- go c .publishApi ()
90
- go c .publishNetwork ()
79
+ go c .mainLoop ()
80
+ go c .publishToNetwork ()
91
81
go c .AutoRefServer .Listen (c .Config .Server .AutoRef .Address )
92
82
go c .TeamServer .Listen (c .Config .Server .Team .Address )
93
83
}
94
84
95
- func (c * GameController ) ProcessGeometry (data * sslproto.SSL_GeometryData ) {
96
- if int32 (math .Round (c .Engine .Geometry .FieldWidth * 1000.0 )) != * data .Field .FieldWidth {
97
- newFieldWidth := float64 (* data .Field .FieldWidth ) / 1000.0
98
- log .Printf ("FieldWidth changed from %v to %v" , c .Engine .Geometry .FieldWidth , newFieldWidth )
99
- c .Engine .Geometry .FieldWidth = newFieldWidth
100
- }
101
- if int32 (math .Round (c .Engine .Geometry .FieldLength * 1000 )) != * data .Field .FieldLength {
102
- newFieldLength := float64 (* data .Field .FieldLength ) / 1000.0
103
- log .Printf ("FieldLength changed from %v to %v" , c .Engine .Geometry .FieldLength , newFieldLength )
104
- c .Engine .Geometry .FieldLength = newFieldLength
105
- }
106
- for _ , line := range data .Field .FieldLines {
107
- if * line .Name == "LeftFieldLeftPenaltyStretch" {
108
- defenseAreaDepth := math .Abs (float64 (* line .P1 .X - * line .P2 .X )) / 1000.0
109
- if math .Abs (defenseAreaDepth - c .Engine .Geometry .DefenseAreaDepth ) > 1e-3 {
110
- log .Printf ("DefenseAreaDepth changed from %v to %v" , c .Engine .Geometry .DefenseAreaDepth , defenseAreaDepth )
111
- c .Engine .Geometry .DefenseAreaDepth = defenseAreaDepth
112
- }
113
- } else if * line .Name == "LeftPenaltyStretch" {
114
- defenseAreaWidth := math .Abs (float64 (* line .P1 .Y - * line .P2 .Y )) / 1000.0
115
- if math .Abs (defenseAreaWidth - c .Engine .Geometry .DefenseAreaWidth ) > 1e-3 {
116
- log .Printf ("DefenseAreaDepth changed from %v to %v" , c .Engine .Geometry .DefenseAreaWidth , defenseAreaWidth )
117
- c .Engine .Geometry .DefenseAreaWidth = defenseAreaWidth
118
- }
119
- }
120
- }
121
- }
122
-
123
- func (c * GameController ) ProcessAutoRefRequests (id string , request refproto.AutoRefToControllerRequest ) error {
124
- c .Mutex .Lock ()
125
- defer c .Mutex .Unlock ()
126
- log .Printf ("Received request from autoRef '%v': %v" , id , request )
127
-
128
- if request .GameEvent != nil {
129
- details := GameEventDetailsFromProto (* request .GameEvent )
130
- gameEventType := details .EventType ()
131
- event := Event {GameEvent : & GameEvent {Type : gameEventType , Details : details }}
132
-
133
- if c .Engine .State .GameEventBehavior [event .GameEvent .Type ] == GameEventBehaviorMajority {
134
- validUntil := c .Engine .TimeProvider ().Add (c .Config .Game .AutoRefProposalTimeout )
135
- newProposal := GameEventProposal {GameEvent : * event .GameEvent , ProposerId : id , ValidUntil : validUntil }
136
-
137
- eventPresent := false
138
- for _ , proposal := range c .Engine .State .GameEventProposals {
139
- if proposal .GameEvent .Type == event .GameEvent .Type && proposal .ProposerId == newProposal .ProposerId {
140
- // update proposal
141
- * proposal = newProposal
142
- eventPresent = true
143
- }
144
- }
145
- if ! eventPresent {
146
- c .Engine .State .GameEventProposals = append (c .Engine .State .GameEventProposals , & newProposal )
147
- }
148
-
149
- totalProposals := 0
150
- for _ , proposal := range c .Engine .State .GameEventProposals {
151
- if proposal .GameEvent .Type == event .GameEvent .Type && proposal .ValidUntil .After (c .Engine .TimeProvider ()) {
152
- totalProposals ++
153
- }
154
- }
85
+ // mainLoop updates several states every full second and publishes the new state
86
+ func (c * GameController ) mainLoop () {
87
+ defer c .historyPreserver .Close ()
88
+ for {
89
+ c .timer .WaitTillNextFullSecond ()
90
+ c .Engine .Tick (c .timer .Delta ())
155
91
156
- majority := int (math .Floor (float64 (len (c .AutoRefServer .Clients )) / 2.0 ))
157
- if totalProposals > majority {
158
- c .OnNewEvent (event )
159
- }
160
- } else {
161
- c .OnNewEvent (event )
162
- }
92
+ c .publish ()
163
93
}
164
-
165
- return nil
166
94
}
167
95
168
- func (c * GameController ) ProcessTeamRequests (teamName string , request refproto.TeamToControllerRequest ) error {
169
- c .Mutex .Lock ()
170
- defer c .Mutex .Unlock ()
171
- log .Print ("Received request from team: " , request )
172
-
173
- if x , ok := request .GetRequest ().(* refproto.TeamToControllerRequest_AdvantageResponse_ ); ok {
174
- if c .outstandingTeamChoice == nil {
175
- return errors .New ("No outstanding choice available. You are probably too late." )
176
- }
177
- responseTime := c .Engine .TimeProvider ().Sub (c .outstandingTeamChoice .IssueTime )
178
- if x .AdvantageResponse == refproto .TeamToControllerRequest_CONTINUE {
179
- log .Printf ("Team %v decided to continue the game within %v" , c .outstandingTeamChoice .Team , responseTime )
180
- switch c .outstandingTeamChoice .Event .GameEvent .Type {
181
- case GameEventBotCrashUnique :
182
- c .outstandingTeamChoice .Event .GameEvent .Details .BotCrashUniqueSkipped = c .outstandingTeamChoice .Event .GameEvent .Details .BotCrashUnique
183
- c .outstandingTeamChoice .Event .GameEvent .Details .BotCrashUnique = nil
184
- c .outstandingTeamChoice .Event .GameEvent .Type = GameEventBotCrashUniqueSkipped
185
- case GameEventBotPushedBot :
186
- c .outstandingTeamChoice .Event .GameEvent .Details .BotPushedBotSkipped = c .outstandingTeamChoice .Event .GameEvent .Details .BotPushedBot
187
- c .outstandingTeamChoice .Event .GameEvent .Details .BotPushedBot = nil
188
- c .outstandingTeamChoice .Event .GameEvent .Type = GameEventBotPushedBotSkipped
189
- default :
190
- return errors .Errorf ("Unsupported advantage choice game event: %v" , c .outstandingTeamChoice .Event .GameEvent .Type )
191
- }
192
- } else {
193
- log .Printf ("Team %v decided to stop the game within %v" , c .outstandingTeamChoice .Team , responseTime )
194
- }
195
- c .OnNewEvent (c .outstandingTeamChoice .Event )
196
- c .outstandingTeamChoice = nil
197
- return nil
198
- }
199
-
200
- team := c .Engine .State .TeamByName (teamName )
201
- if team == TeamUnknown {
202
- return errors .New ("Your team is not playing?!" )
203
- }
204
-
205
- if x , ok := request .GetRequest ().(* refproto.TeamToControllerRequest_SubstituteBot ); ok {
206
- log .Printf ("Team %v updated bot substituation intend to %v" , team , x .SubstituteBot )
207
- c .Engine .State .TeamState [team ].BotSubstitutionIntend = x .SubstituteBot
208
- }
209
-
210
- if c .Engine .State .GameState () != GameStateStopped {
211
- return errors .New ("Game is not stopped." )
212
- }
96
+ // publish publishes the state to the UI and the teams
97
+ func (c * GameController ) publish () {
98
+ c .updateOnlineStates ()
99
+ c .historyPreserver .Save (c .Engine .History )
213
100
214
- if x , ok := request .GetRequest ().(* refproto.TeamToControllerRequest_DesiredKeeper ); ok {
215
- log .Printf ("Changing goalie for team %v to %v" , team , x .DesiredKeeper )
216
- c .Engine .State .TeamState [team ].Goalie = int (x .DesiredKeeper )
217
- }
101
+ c .TeamServer .AllowedTeamNames = []string {
102
+ c .Engine .State .TeamState [TeamYellow ].Name ,
103
+ c .Engine .State .TeamState [TeamBlue ].Name }
218
104
219
- return nil
105
+ c .publishUiProtocol ()
106
+ c .ApiServer .PublishState (* c .Engine .State )
220
107
}
221
108
222
- func (c * GameController ) publishApi () {
223
- defer c .historyPreserver .Close ()
224
- for {
225
- c .timer .WaitTillNextFullSecond ()
226
- c .updateOnlineStates ()
227
- c .Engine .Tick (c .timer .Delta ())
228
- c .historyPreserver .Save (c .Engine .History )
229
- c .publish ()
109
+ // publishUiProtocol publishes the UI protocol, if it has changed
110
+ func (c * GameController ) publishUiProtocol () {
111
+ if len (c .Engine .UiProtocol ) != c .numUiProtocolsLastPublish {
112
+ c .ApiServer .PublishUiProtocol (c .Engine .UiProtocol )
113
+ c .numUiProtocolsLastPublish = len (c .Engine .UiProtocol )
230
114
}
231
115
}
232
116
117
+ // updateOnlineStates checks if teams and autoRefs are online and writes this into the state
233
118
func (c * GameController ) updateOnlineStates () {
234
119
for _ , team := range []Team {TeamYellow , TeamBlue } {
235
120
c .Engine .State .TeamState [team ].Connected = c .teamConnected (team )
@@ -241,21 +126,15 @@ func (c *GameController) updateOnlineStates() {
241
126
c .Engine .State .AutoRefsConnected = autoRefs
242
127
}
243
128
244
- func (c * GameController ) teamConnected (team Team ) bool {
245
- teamName := c .Engine .State .TeamState [team ].Name
246
- if _ , ok := c .TeamServer .Clients [teamName ]; ok {
247
- return true
248
- }
249
- return false
250
- }
251
-
252
- func (c * GameController ) publishNetwork () {
129
+ // publishToNetwork publishes the current state to the network (multicast) every 100ms
130
+ func (c * GameController ) publishToNetwork () {
253
131
for {
254
132
c .timer .WaitTillNextFull (100 * time .Millisecond )
255
133
c .Publisher .Publish (c .Engine .State )
256
134
}
257
135
}
258
136
137
+ // OnNewEvent processes the given event
259
138
func (c * GameController ) OnNewEvent (event Event ) {
260
139
261
140
if event .GameEvent != nil && ! c .Engine .disabledGameEvent (event .GameEvent .Type ) && c .askForTeamDecisionIfRequired (event ) {
@@ -266,52 +145,11 @@ func (c *GameController) OnNewEvent(event Event) {
266
145
if err != nil {
267
146
log .Println ("Could not process event:" , event , err )
268
147
} else {
269
- c .historyPreserver .Save (c .Engine .History )
270
148
c .publish ()
271
149
}
272
150
}
273
151
274
- func (c * GameController ) askForTeamDecisionIfRequired (event Event ) (handled bool ) {
275
- handled = false
276
- if c .outstandingTeamChoice == nil && c .Engine .State .GameState () == GameStateRunning {
277
- var byTeamProto refproto.Team
278
- var choiceType refproto.ControllerToTeamRequest_AdvantageChoice_Foul
279
- if event .GameEvent .Details .BotCrashUnique != nil {
280
- byTeamProto = * event .GameEvent .Details .BotCrashUnique .ByTeam
281
- choiceType = refproto .ControllerToTeamRequest_AdvantageChoice_COLLISION
282
- } else if event .GameEvent .Details .BotPushedBot != nil {
283
- byTeamProto = * event .GameEvent .Details .BotPushedBot .ByTeam
284
- choiceType = refproto .ControllerToTeamRequest_AdvantageChoice_PUSHING
285
- }
286
-
287
- forTeam := NewTeam (byTeamProto ).Opposite ()
288
- if forTeam != "" {
289
- teamName := c .Engine .State .TeamState [forTeam ].Name
290
- choice := refproto.ControllerToTeamRequest_AdvantageChoice {Foul : & choiceType }
291
- requestPayload := refproto.ControllerToTeamRequest_AdvantageChoice_ {AdvantageChoice : & choice }
292
- request := refproto.ControllerToTeamRequest {Request : & requestPayload }
293
- err := c .TeamServer .SendRequest (teamName , request )
294
- if err != nil {
295
- log .Print ("Failed to ask for advantage choice: " , err )
296
- } else {
297
- c .outstandingTeamChoice = & TeamChoice {Team : forTeam , Event : event , IssueTime : c .Engine .TimeProvider ()}
298
- go c .timeoutTeamChoice ()
299
- handled = true
300
- }
301
- }
302
- }
303
- return
304
- }
305
-
306
- func (c * GameController ) timeoutTeamChoice () {
307
- time .Sleep (c .Config .Game .TeamChoiceTimeout )
308
- c .Mutex .Lock ()
309
- defer c .Mutex .Unlock ()
310
- if c .outstandingTeamChoice != nil {
311
- c .OnNewEvent (c .outstandingTeamChoice .Event )
312
- }
313
- }
314
-
152
+ // loadPublisher creates a new publisher for multicast
315
153
func loadPublisher (config config.Controller ) Publisher {
316
154
publisher , err := NewPublisher (config .Network .PublishAddress )
317
155
if err != nil {
@@ -320,22 +158,11 @@ func loadPublisher(config config.Controller) Publisher {
320
158
return publisher
321
159
}
322
160
161
+ // loadConfig loads the controller config
323
162
func loadConfig () config.Controller {
324
163
cfg , err := config .LoadControllerConfig (configFileName )
325
164
if err != nil {
326
165
log .Printf ("Could not load config: %v" , err )
327
166
}
328
167
return cfg
329
168
}
330
-
331
- // publish publishes the state to the UI and the teams
332
- func (c * GameController ) publish () {
333
- if len (c .Engine .UiProtocol ) != c .numUiProtocolsLastPublish {
334
- c .ApiServer .PublishUiProtocol (c .Engine .UiProtocol )
335
- c .numUiProtocolsLastPublish = len (c .Engine .UiProtocol )
336
- }
337
-
338
- c .TeamServer .AllowedTeamNames = []string {c .Engine .State .TeamState [TeamYellow ].Name ,
339
- c .Engine .State .TeamState [TeamBlue ].Name }
340
- c .ApiServer .PublishState (* c .Engine .State )
341
- }
0 commit comments