Skip to content

Commit f272195

Browse files
committed
Combine game event proposals with accepted game events
1 parent c13f596 commit f272195

File tree

9 files changed

+148
-90
lines changed

9 files changed

+148
-90
lines changed

frontend/src/components/match/GameEventItem.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {gameEventNames} from "@/helpers/texts";
55
import type {GameEvent} from "@/proto/ssl_gc_game_event";
66
import {gameEventForTeam, originIcon} from "@/helpers";
77
import GameEventDetailsTree from "@/components/match/GameEventDetailsTree.vue";
8+
import dayjs from "dayjs";
89
910
const props = defineProps<{
1011
gameEvent: GameEvent,
11-
caption?: string,
1212
}>()
1313
1414
const label = computed(() => {
@@ -22,6 +22,13 @@ const team = computed(() => {
2222
const origins = computed(() => {
2323
return props.gameEvent.origin
2424
})
25+
26+
const time = computed(() => {
27+
if (props.gameEvent.createdTimestamp) {
28+
return dayjs(props.gameEvent.createdTimestamp / 1e3).format("MMM, DD YYYY HH:mm:ss,SSS")
29+
}
30+
return undefined
31+
})
2532
</script>
2633

2734
<template>
@@ -32,7 +39,7 @@ const origins = computed(() => {
3239
<TeamBadge :team="team"/>
3340
{{ label }}
3441
</q-item-label>
35-
<q-item-label caption v-if="caption">{{ caption }}</q-item-label>
42+
<q-item-label caption v-if="time">{{ time }}</q-item-label>
3643
</q-item-section>
3744
<q-item-section side>
3845
<div class="row">

frontend/src/components/match/GameEventList.vue

Lines changed: 0 additions & 20 deletions
This file was deleted.

frontend/src/components/match/GameEventProposalGroupItem.vue

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import {gameEventNames} from "@/helpers/texts";
55
import type {ControlApi} from "@/providers/controlApi/ControlApi";
66
import type {ProposalGroup} from "@/proto/ssl_gc_state";
77
import type {GameEvent} from "@/proto/ssl_gc_game_event";
8+
import TeamBadge from "@/components/common/TeamBadge.vue";
9+
import {gameEventForTeam, originIcon} from "@/helpers";
10+
import GameEventItem from "@/components/match/GameEventItem.vue";
811
912
const props = defineProps<{
1013
proposalGroup: ProposalGroup,
1114
groupId: string,
15+
acceptedGameEvent?: GameEvent,
1216
}>()
1317
1418
const control = inject<ControlApi>('control-api')
@@ -22,9 +26,26 @@ const gameEventLabel = (gameEvent: GameEvent) => {
2226
}
2327
2428
const label = computed(() => {
29+
if (props.acceptedGameEvent) {
30+
return gameEventLabel(props.acceptedGameEvent)
31+
}
2532
return Array.from(new Set(proposals.value?.map(p => gameEventLabel(p.gameEvent!))).values()).join(" / ")
2633
})
2734
35+
const teams = computed(() => {
36+
if (props.acceptedGameEvent) {
37+
return Array.of(gameEventForTeam(props.acceptedGameEvent))
38+
}
39+
return Array.from(new Set(proposals.value?.map(p => gameEventForTeam(p.gameEvent!))).values())
40+
})
41+
42+
const origins = computed(() => {
43+
if (props.acceptedGameEvent) {
44+
return props.acceptedGameEvent.origin
45+
}
46+
return Array.from(new Set(proposals.value?.flatMap(p => p.gameEvent?.origin || []).values()))
47+
})
48+
2849
const pending = computed(() => {
2950
return !props.proposalGroup.accepted
3051
})
@@ -48,17 +69,29 @@ const acceptGroup = () => {
4869
:default-opened="pending"
4970
>
5071
<template v-slot:header>
51-
<q-item-section side>
72+
<q-item-section side v-if="pending">
5273
<q-icon :name="groupIcon"/>
5374
</q-item-section>
5475
<q-item-section>
55-
<q-item-label>{{ label }}</q-item-label>
76+
<q-item-label>
77+
<TeamBadge v-for="team in teams" :team="team" :key="team"/>
78+
{{ label }}
79+
</q-item-label>
5680
</q-item-section>
57-
<q-item-section side v-if="pending">
58-
<q-btn flat dense round icon="done" @click="acceptGroup"/>
81+
<q-item-section side>
82+
<div class="row">
83+
<q-icon class="q-mx-xs" :name="originIcon(origin)" color="primary" :alt="origin"
84+
v-for="(origin, key) in origins" :key="key">
85+
<q-tooltip>
86+
{{ origin }}
87+
</q-tooltip>
88+
</q-icon>
89+
</div>
90+
<q-btn flat dense round icon="done" @click="acceptGroup" v-if="pending"/>
5991
</q-item-section>
6092
</template>
6193
<q-list>
94+
<GameEventItem :game-event="acceptedGameEvent" v-if="acceptedGameEvent"/>
6295
<GameEventProposalItem :proposal="proposal" v-for="(proposal, key) in proposals" :key="key"/>
6396
</q-list>
6497
</q-expansion-item>

frontend/src/components/match/GameEventProposalGroupItemList.vue

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
<script setup lang="ts">
22
import {computed} from "vue";
33
import GameEventItem from "@/components/match/GameEventItem.vue";
4-
import dayjs from 'dayjs'
54
import type {Proposal} from "@/proto/ssl_gc_state";
65
76
const props = defineProps<{
87
proposal: Proposal,
98
}>()
109
11-
const time = computed(() => {
12-
return dayjs(props.proposal.timestamp).format("MMM, DD YYYY HH:mm:ss,SSS")
13-
})
14-
1510
const gameEvent = computed(() => {
1611
return props.proposal.gameEvent!
1712
})
1813
</script>
1914

2015
<template>
21-
<GameEventItem :game-event="gameEvent" :caption="time"/>
16+
<GameEventItem :game-event="gameEvent"/>
2217
</template>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script setup lang="ts">
2+
import {computed} from "vue";
3+
import GameEventItem from "@/components/match/GameEventItem.vue";
4+
import GameEventProposalGroupItem from "@/components/match/GameEventProposalGroupItem.vue";
5+
import {useMatchStateStore} from "@/store/matchState";
6+
import type {ProposalGroup} from "@/proto/ssl_gc_state";
7+
import type {GameEvent} from "@/proto/ssl_gc_game_event";
8+
9+
const store = useMatchStateStore()
10+
11+
type GameEventWrappedItem = {
12+
id: string
13+
timestamp: number
14+
proposalGroup?: ProposalGroup
15+
gameEvent?: GameEvent
16+
}
17+
18+
const gameEventItems = computed(() => {
19+
const gameEvents = store.matchState.gameEvents!
20+
const proposalGroups = store.matchState.proposalGroups!
21+
const items: GameEventWrappedItem[] = []
22+
for (const proposalGroup of proposalGroups) {
23+
const item: GameEventWrappedItem = {
24+
id: proposalGroup.id!,
25+
timestamp: proposalGroup.proposals![0].timestamp!.getTime(),
26+
proposalGroup: proposalGroup,
27+
}
28+
items.push(item)
29+
}
30+
for (const gameEvent of gameEvents) {
31+
let gameEventItem = items.find(item => item.id === gameEvent.id);
32+
if (gameEventItem) {
33+
gameEventItem.gameEvent = gameEvent
34+
} else {
35+
const item: GameEventWrappedItem = {
36+
id: gameEvent.id!,
37+
timestamp: gameEvent.createdTimestamp! / 1000,
38+
gameEvent: gameEvent,
39+
}
40+
items.push(item)
41+
}
42+
}
43+
items.sort((a, b) => a.timestamp - b.timestamp)
44+
return items
45+
})
46+
47+
</script>
48+
49+
<template>
50+
<q-list bordered class="rounded-borders" v-if="gameEventItems.length > 0">
51+
<template v-for="(item) in gameEventItems">
52+
<GameEventProposalGroupItem
53+
v-if="item.proposalGroup"
54+
:proposal-group="item.proposalGroup"
55+
:group-id="item.id"
56+
:accepted-game-event="item.gameEvent"
57+
:key="item.id"
58+
/>
59+
<GameEventItem
60+
v-else-if="item.gameEvent"
61+
:game-event="item.gameEvent"
62+
:key="item.id"
63+
/>
64+
</template>
65+
</q-list>
66+
<div v-else>
67+
No recent game event proposals
68+
</div>
69+
</template>

frontend/src/views/MatchView.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import {computed, inject, onMounted, onUnmounted} from "vue";
33
import ContinueActionButtonList from "@/components/match/ContinueActionButtonList.vue";
44
import AutoContinueInput from "@/components/match/AutoContinueInput.vue";
5-
import GameEventProposalGroupItemList from "@/components/match/GameEventProposalGroupItemList.vue";
6-
import GameEventList from "@/components/match/GameEventList.vue";
75
import MatchTeamTable from "@/components/match/MatchTeamTable.vue";
86
import type {ControlApi} from "@/providers/controlApi/ControlApi";
97
import {useGcStateStore} from "@/store/gcState";
@@ -13,6 +11,7 @@ import {useMatchStateStore} from "@/store/matchState";
1311
import {Referee_Stage} from "@/proto/ssl_gc_referee_message";
1412
import SwitchSidesButton from "@/components/start/SwitchSidesButton.vue";
1513
import HaltButton from "@/components/control/HaltButton.vue";
14+
import GameEvents from "@/components/match/GameEvents.vue";
1615
1716
const store = useMatchStateStore()
1817
const gcStore = useGcStateStore()
@@ -113,8 +112,7 @@ onUnmounted(() => {
113112
<ContinueActionButtonList/>
114113
</div>
115114
<div class="col q-gutter-md q-mr-md" style="min-width: 300px">
116-
<GameEventProposalGroupItemList/>
117-
<GameEventList/>
115+
<GameEvents/>
118116
</div>
119117
</div>
120118
</template>

internal/app/engine/engine.go

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -82,27 +82,6 @@ func NewEngine(gameConfig config.Game, engineConfig config.Engine) (e *Engine) {
8282

8383
// Enqueue adds the change to the change queue
8484
func (e *Engine) Enqueue(change *statemachine.Change) {
85-
if change.GetAddGameEventChange() != nil {
86-
change.GetAddGameEventChange().GameEvent = e.processGameEvent(change.GetAddGameEventChange().GameEvent)
87-
change = e.handleGameEventBehavior(change)
88-
if change == nil {
89-
return
90-
}
91-
} else if change.GetAddPassiveGameEventChange() != nil {
92-
change.GetAddPassiveGameEventChange().GameEvent = e.processGameEvent(change.GetAddPassiveGameEventChange().GameEvent)
93-
} else if change.GetAddProposalChange() != nil {
94-
log.Println("Ignoring unexpected proposal change: ", change.GetAddProposalChange())
95-
return
96-
}
97-
98-
if change.Revertible == nil {
99-
change.Revertible = new(bool)
100-
// Assume that changes from outside are by default revertible, except if the flag is already set
101-
*change.Revertible = true
102-
}
103-
if change.Origin == nil {
104-
change.Origin = proto.String("Unknown")
105-
}
10685
e.changeQueue <- change
10786
}
10887

@@ -146,18 +125,16 @@ func isNonMajorityOrigin(origins []string) bool {
146125

147126
func (e *Engine) processGameEvent(gameEvent *state.GameEvent) *state.GameEvent {
148127
// Set creation timestamp
149-
if gameEvent.CreatedTimestamp != nil {
150-
log.Printf("Ignore existing created_timestamp in enqueued game event: %v", gameEvent)
128+
if gameEvent.CreatedTimestamp == nil {
129+
gameEvent.CreatedTimestamp = new(uint64)
130+
*gameEvent.CreatedTimestamp = uint64(e.timeProvider().UnixMicro())
151131
}
152-
gameEvent.CreatedTimestamp = new(uint64)
153-
*gameEvent.CreatedTimestamp = uint64(e.timeProvider().UnixMicro())
154132

155133
// Set unique id
156-
if gameEvent.Id != nil {
157-
log.Printf("Ignore existing id in enqueued game event: %v", gameEvent)
134+
if gameEvent.Id == nil {
135+
gameEvent.Id = new(string)
136+
*gameEvent.Id = uuid.NewString()
158137
}
159-
gameEvent.Id = new(string)
160-
*gameEvent.Id = uuid.NewString()
161138

162139
// convert aimless kick if necessary
163140
if e.currentState.Division.Div() == config.DivA && *gameEvent.Type == state.GameEvent_AIMLESS_KICK {
@@ -382,6 +359,27 @@ func (e *Engine) processChange(change *statemachine.Change) (newChanges []*state
382359
e.mutex.Lock()
383360
defer e.mutex.Unlock()
384361

362+
if change.GetAddGameEventChange() != nil {
363+
change.GetAddGameEventChange().GameEvent = e.processGameEvent(change.GetAddGameEventChange().GameEvent)
364+
change = e.handleGameEventBehavior(change)
365+
if change == nil {
366+
return
367+
}
368+
} else if change.GetAddPassiveGameEventChange() != nil {
369+
change.GetAddPassiveGameEventChange().GameEvent = e.processGameEvent(change.GetAddPassiveGameEventChange().GameEvent)
370+
} else if change.GetAddProposalChange() != nil {
371+
change.GetAddProposalChange().Proposal.GameEvent = e.processGameEvent(change.GetAddProposalChange().Proposal.GameEvent)
372+
}
373+
374+
if change.Revertible == nil {
375+
change.Revertible = new(bool)
376+
// Assume that changes from outside are by default revertible, except if the flag is already set
377+
*change.Revertible = true
378+
}
379+
if change.Origin == nil {
380+
change.Origin = proto.String("Unknown")
381+
}
382+
385383
var entry = e.stateStore.CreateEntry(change, e.timeProvider(), e.currentState)
386384
log.Printf("Process change %d: %+v", *entry.Id, change.StringJson())
387385

internal/app/engine/process_proposals.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ func (e *Engine) uniqueAcceptingOrigins(group *state.ProposalGroup) int {
4646
origins := map[string]bool{}
4747
for _, p := range group.Proposals {
4848
for _, o := range p.GameEvent.Origin {
49+
if _, ok := e.config.AutoRefConfigs[o]; !ok {
50+
continue
51+
}
4952
if e.config.AutoRefConfigs[o].GameEventBehavior[p.GameEvent.Type.String()] == AutoRefConfig_BEHAVIOR_ACCEPT {
5053
origins[o] = true
5154
}

0 commit comments

Comments
 (0)