@@ -4,12 +4,8 @@ import (
44 "github.com/RoboCup-SSL/ssl-game-controller/internal/app/config"
55 "github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"
66 "github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"
7- "github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
87 "github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
9- "github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
10- "github.com/pkg/errors"
118 "log"
12- "math"
139 "sync"
1410 "time"
1511)
@@ -32,12 +28,6 @@ type GameController struct {
3228 VisionReceiver * vision.Receiver
3329}
3430
35- type TeamChoice struct {
36- Team Team
37- Event Event
38- IssueTime time.Time
39- }
40-
4131// NewGameController creates a new RefBox
4232func NewGameController () (c * GameController ) {
4333
@@ -86,150 +76,45 @@ func (c *GameController) Run() {
8676 c .Engine .State .TeamState [TeamBlue ].Name }
8777
8878 c .timer .Start ()
89- go c .publishApi ()
90- go c .publishNetwork ()
79+ go c .mainLoop ()
80+ go c .publishToNetwork ()
9181 go c .AutoRefServer .Listen (c .Config .Server .AutoRef .Address )
9282 go c .TeamServer .Listen (c .Config .Server .Team .Address )
9383}
9484
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 ())
15591
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 ()
16393 }
164-
165- return nil
16694}
16795
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 )
213100
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 }
218104
219- return nil
105+ c .publishUiProtocol ()
106+ c .ApiServer .PublishState (* c .Engine .State )
220107}
221108
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 )
230114 }
231115}
232116
117+ // updateOnlineStates checks if teams and autoRefs are online and writes this into the state
233118func (c * GameController ) updateOnlineStates () {
234119 for _ , team := range []Team {TeamYellow , TeamBlue } {
235120 c .Engine .State .TeamState [team ].Connected = c .teamConnected (team )
@@ -241,21 +126,15 @@ func (c *GameController) updateOnlineStates() {
241126 c .Engine .State .AutoRefsConnected = autoRefs
242127}
243128
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 () {
253131 for {
254132 c .timer .WaitTillNextFull (100 * time .Millisecond )
255133 c .Publisher .Publish (c .Engine .State )
256134 }
257135}
258136
137+ // OnNewEvent processes the given event
259138func (c * GameController ) OnNewEvent (event Event ) {
260139
261140 if event .GameEvent != nil && ! c .Engine .disabledGameEvent (event .GameEvent .Type ) && c .askForTeamDecisionIfRequired (event ) {
@@ -266,52 +145,11 @@ func (c *GameController) OnNewEvent(event Event) {
266145 if err != nil {
267146 log .Println ("Could not process event:" , event , err )
268147 } else {
269- c .historyPreserver .Save (c .Engine .History )
270148 c .publish ()
271149 }
272150}
273151
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
315153func loadPublisher (config config.Controller ) Publisher {
316154 publisher , err := NewPublisher (config .Network .PublishAddress )
317155 if err != nil {
@@ -320,22 +158,11 @@ func loadPublisher(config config.Controller) Publisher {
320158 return publisher
321159}
322160
161+ // loadConfig loads the controller config
323162func loadConfig () config.Controller {
324163 cfg , err := config .LoadControllerConfig (configFileName )
325164 if err != nil {
326165 log .Printf ("Could not load config: %v" , err )
327166 }
328167 return cfg
329168}
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