Skip to content

Commit 6c53d2c

Browse files
committed
[feature/bugfix] Rewrite processing of game event proposals
* Make sure that proposers always get it to the submitted events * Remove proposals, after they were submitted or are obsolete
1 parent 2fd457e commit 6c53d2c

File tree

3 files changed

+97
-42
lines changed

3 files changed

+97
-42
lines changed

internal/app/controller/autoRefConnection.go

Lines changed: 90 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,99 @@ func (c *GameController) ProcessAutoRefRequests(id string, request refproto.Auto
1111
defer c.ConnectionMutex.Unlock()
1212
log.Printf("Received request from autoRef '%v': %v", id, request)
1313

14-
if request.GameEvent != nil {
15-
details := GameEventDetailsFromProto(*request.GameEvent)
16-
event := Event{GameEvent: &GameEvent{Type: details.EventType(), Details: details}}
17-
18-
c.Engine.applyGameEventFilters(event.GameEvent)
19-
20-
if c.Engine.GcState.GameEventBehavior[event.GameEvent.Type] == GameEventBehaviorMajority {
21-
validUntil := c.Engine.TimeProvider().Add(c.Config.Game.AutoRefProposalTimeout)
22-
newProposal := GameEventProposal{GameEvent: *event.GameEvent, ProposerId: id, ValidUntil: validUntil}
23-
24-
eventPresent := false
25-
for _, proposal := range c.Engine.GcState.GameEventProposals {
26-
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ProposerId == newProposal.ProposerId {
27-
// update proposal
28-
*proposal = newProposal
29-
eventPresent = true
30-
}
31-
}
32-
if !eventPresent {
33-
c.Engine.GcState.GameEventProposals = append(c.Engine.GcState.GameEventProposals, &newProposal)
34-
}
35-
36-
totalProposals := 0
37-
var origins []string
38-
for _, proposal := range c.Engine.GcState.GameEventProposals {
39-
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ValidUntil.After(c.Engine.TimeProvider()) {
40-
totalProposals++
41-
origins = append(origins, proposal.ProposerId)
42-
}
43-
}
44-
45-
majority := int(math.Floor(float64(len(c.AutoRefServer.Clients)) / 2.0))
46-
if totalProposals > majority {
47-
event.GameEvent.Origins = origins
48-
c.OnNewEvent(event)
49-
}
50-
} else {
51-
event.GameEvent.Origins = []string{id}
14+
if request.GameEvent == nil {
15+
return nil
16+
}
17+
18+
details := GameEventDetailsFromProto(*request.GameEvent)
19+
event := Event{GameEvent: &GameEvent{Type: details.EventType(), Details: details}}
20+
21+
c.Engine.applyGameEventFilters(event.GameEvent)
22+
23+
validUntil := c.Engine.TimeProvider().Add(c.Config.Game.AutoRefProposalTimeout)
24+
proposal := GameEventProposal{GameEvent: *event.GameEvent, ProposerId: id, ValidUntil: validUntil}
25+
26+
if matchingEvent := c.Engine.State.FindMatchingRecentGameEvent(event.GameEvent); matchingEvent != nil {
27+
// there was already such a game event recently. Just add the proposer to the existing event.
28+
matchingEvent.Origins = append(matchingEvent.Origins, id)
29+
} else if c.Engine.applyMajority(&event) { // look for a majority
30+
31+
if c.Engine.isNewProposal(&proposal) {
32+
// the autoRef has not submitted the same event recently, so add it to the proposals - waiting for majority
33+
c.Engine.GcState.GameEventProposals = append(c.Engine.GcState.GameEventProposals, &proposal)
34+
}
35+
36+
numProposals := c.Engine.numberOfMatchingProposals(event.GameEvent)
37+
majority := int(math.Floor(float64(len(c.AutoRefServer.Clients)) / 2.0))
38+
if numProposals > majority {
39+
// there is a majority for a game event that has not yet been submitted to the GC
40+
// remove the matching proposals and submit the event to the GC
5241
c.OnNewEvent(event)
5342
}
43+
44+
} else {
45+
// game event has not been submitted recently (by any autoRef) and no majority required.
46+
// Just submit this event
47+
event.GameEvent.Origins = []string{id}
48+
c.OnNewEvent(event)
5449
}
5550

5651
return nil
5752
}
53+
54+
func (e *Engine) numberOfMatchingProposals(event *GameEvent) (count int) {
55+
count = 0
56+
for _, proposal := range e.GcState.GameEventProposals {
57+
if proposal.GameEvent.Type == event.Type && e.proposalValid(proposal) {
58+
count++
59+
}
60+
}
61+
return
62+
}
63+
64+
func (e *Engine) collectAllMatchingProposals(event *GameEvent) []*GameEventProposal {
65+
var proposals []*GameEventProposal
66+
for _, proposal := range e.GcState.GameEventProposals {
67+
if proposal.GameEvent.Type == event.Type {
68+
proposals = append(proposals, proposal)
69+
}
70+
}
71+
return proposals
72+
}
73+
74+
func (e *Engine) collectNonMatchingProposals(event *GameEvent) []*GameEventProposal {
75+
var proposals []*GameEventProposal
76+
for _, proposal := range e.GcState.GameEventProposals {
77+
if proposal.GameEvent.Type != event.Type {
78+
proposals = append(proposals, proposal)
79+
}
80+
}
81+
return proposals
82+
}
83+
84+
func collectAllOrigins(proposals []*GameEventProposal) []string {
85+
var origins []string
86+
for _, proposal := range proposals {
87+
origins = append(origins, proposal.ProposerId)
88+
}
89+
return origins
90+
}
91+
92+
func (e *Engine) proposalValid(proposal *GameEventProposal) bool {
93+
return proposal.ValidUntil.After(e.TimeProvider())
94+
}
95+
96+
func (e *Engine) isNewProposal(newProposal *GameEventProposal) bool {
97+
for _, proposal := range e.GcState.GameEventProposals {
98+
if proposal.GameEvent.Type == newProposal.GameEvent.Type &&
99+
proposal.ProposerId == newProposal.ProposerId &&
100+
e.proposalValid(proposal) {
101+
return false
102+
}
103+
}
104+
return true
105+
}
106+
107+
func (e *Engine) applyMajority(event *Event) bool {
108+
return e.GcState.GameEventBehavior[event.GameEvent.Type] == GameEventBehaviorMajority
109+
}

internal/app/controller/engine.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,9 @@ func (e *Engine) processGameEvent(event *GameEvent) error {
804804

805805
e.applyGameEventFilters(event)
806806

807+
event.Origins = append(event.Origins, collectAllOrigins(e.collectAllMatchingProposals(event))...)
808+
e.GcState.GameEventProposals = e.collectNonMatchingProposals(event)
809+
807810
if e.disabledGameEvent(event.Type) {
808811
e.LogIgnoredGameEvent(*event)
809812
return nil
@@ -812,7 +815,7 @@ func (e *Engine) processGameEvent(event *GameEvent) error {
812815

813816
var additionalEvents []*GameEvent
814817

815-
if !e.State.MatchesRecentGameEvent(event) && event.IncrementsFoulCounter() {
818+
if event.IncrementsFoulCounter() {
816819
team := event.ByTeam()
817820
if team.Unknown() {
818821
e.State.TeamState[TeamYellow].FoulCounter++

internal/app/controller/state.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,14 @@ func (s *State) GetFirstGameEvent(gameEventType GameEventType) *GameEvent {
557557
return nil
558558
}
559559

560-
func (s *State) MatchesRecentGameEvent(event *GameEvent) bool {
560+
func (s *State) FindMatchingRecentGameEvent(event *GameEvent) *GameEvent {
561561
for _, gameEvent := range s.GameEvents {
562562
if gameEvent.Type == event.Type &&
563563
event.Occurred().Sub(gameEvent.Occurred()) < 5*time.Second {
564-
return true
564+
return gameEvent
565565
}
566566
}
567-
return false
567+
return nil
568568
}
569569

570570
// Location is a two-dimensional coordinate

0 commit comments

Comments
 (0)