Skip to content

Commit d9d47ae

Browse files
authored
feat: capture color field in sector events (#149)
* fix: add empty color field to v1 sector event format The v1 JSON format includes a color field at index 3 between side and position: [objectType, unitName, side, color, [x, y, z]]. The builder was omitting this field, shifting position from index 4 to index 3 and breaking index-based consumers. Add empty string placeholder for color since the extension does not capture it. * feat: capture color field in sector events Add Color field to SectorEvent struct and parse it from :EVENT:SECTOR: args at index 5. Update v1 JSON export to use the actual color value instead of a hardcoded empty string. Args format changes from [frame, type, objectType, unitName, side, posX?, posY?, posZ?] to [frame, type, objectType, unitName, side, color, posX?, posY?, posZ?]. * fix: use sector test data instead of flag in sector event tests
1 parent a85c71e commit d9d47ae

7 files changed

Lines changed: 34 additions & 23 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ All commands follow a `:RESOURCE:ACTION:` naming convention. New commands must u
127127
| Command | Buffer | Purpose |
128128
|---------|--------|---------|
129129
| `:EVENT:GENERAL:` | 1,000 | General gameplay event |
130-
| `:EVENT:SECTOR:` | 1,000 | Sector state change (captured/contested/capturedFlag) |
130+
| `:EVENT:SECTOR:` | 1,000 | Sector state change (captured/contested) |
131131
| `:EVENT:ENDMISSION:` | 100 | End-of-mission event with side and message |
132132
| `:EVENT:CHAT:` | 1,000 | Chat message |
133133
| `:EVENT:RADIO:` | 1,000 | Radio transmission |

internal/parser/parse_events.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func (p *Parser) ParseGeneralEvent(data []string) (core.GeneralEvent, error) {
185185

186186
// ParseSectorEvent parses sector state change events.
187187
// Handles: captured, contested.
188-
// Args: [frame, type, objectType, unitName, side, posX?, posY?, posZ?]
188+
// Args: [frame, type, objectType, unitName, side, color, posX?, posY?, posZ?]
189189
func (p *Parser) ParseSectorEvent(data []string) (core.SectorEvent, error) {
190190
var event core.SectorEvent
191191

@@ -212,16 +212,20 @@ func (p *Parser) ParseSectorEvent(data []string) (core.SectorEvent, error) {
212212
event.Side = data[4]
213213
}
214214

215-
if len(data) >= 8 {
216-
event.PosX, err = strconv.ParseFloat(data[5], 64)
215+
if len(data) >= 6 {
216+
event.Color = data[5]
217+
}
218+
219+
if len(data) >= 9 {
220+
event.PosX, err = strconv.ParseFloat(data[6], 64)
217221
if err != nil {
218222
return event, fmt.Errorf("invalid position X for sector event: %w", err)
219223
}
220-
event.PosY, err = strconv.ParseFloat(data[6], 64)
224+
event.PosY, err = strconv.ParseFloat(data[7], 64)
221225
if err != nil {
222226
return event, fmt.Errorf("invalid position Y for sector event: %w", err)
223227
}
224-
event.PosZ, err = strconv.ParseFloat(data[7], 64)
228+
event.PosZ, err = strconv.ParseFloat(data[8], 64)
225229
if err != nil {
226230
return event, fmt.Errorf("invalid position Z for sector event: %w", err)
227231
}

internal/parser/parse_events_test.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,46 +537,50 @@ func TestParseSectorEvent(t *testing.T) {
537537
wantErr bool
538538
}{
539539
{
540-
name: "captured with side and position",
541-
input: []string{"200", "captured", "sector", "Sector Alpha", "WEST", "100.5", "200.3", "0"},
540+
name: "captured with side, color and position",
541+
input: []string{"200", "captured", "sector", "Sector Alpha", "WEST", "#0000FF", "100.5", "200.3", "0"},
542542
check: func(t *testing.T, e core.SectorEvent) {
543543
assert.Equal(t, core.Frame(200), e.CaptureFrame)
544544
assert.Equal(t, "captured", e.Name)
545545
assert.Equal(t, "sector", e.ObjectType)
546546
assert.Equal(t, "Sector Alpha", e.UnitName)
547547
assert.Equal(t, "WEST", e.Side)
548+
assert.Equal(t, "#0000FF", e.Color)
548549
assert.InDelta(t, 100.5, e.PosX, 0.001)
549550
assert.InDelta(t, 200.3, e.PosY, 0.001)
550551
assert.InDelta(t, 0.0, e.PosZ, 0.001)
551552
},
552553
},
553554
{
554-
name: "contested with empty side",
555-
input: []string{"300", "contested", "sector", "Sector,With,Commas", "", "50", "60", "0"},
555+
name: "contested with empty side and color",
556+
input: []string{"300", "contested", "sector", "Sector,With,Commas", "", "", "50", "60", "0"},
556557
check: func(t *testing.T, e core.SectorEvent) {
557558
assert.Equal(t, "contested", e.Name)
558559
assert.Equal(t, "Sector,With,Commas", e.UnitName)
559560
assert.Equal(t, "", e.Side)
561+
assert.Equal(t, "", e.Color)
560562
},
561563
},
562564
{
563565
name: "captured without position",
564-
input: []string{"200", "captured", "sector", "Sector Alpha", "WEST"},
566+
input: []string{"200", "captured", "sector", "Sector Alpha", "WEST", ""},
565567
check: func(t *testing.T, e core.SectorEvent) {
566568
assert.Equal(t, "captured", e.Name)
567569
assert.Equal(t, "Sector Alpha", e.UnitName)
568570
assert.Equal(t, "WEST", e.Side)
571+
assert.Equal(t, "", e.Color)
569572
assert.Equal(t, 0.0, e.PosX)
570573
assert.Equal(t, 0.0, e.PosY)
571574
assert.Equal(t, 0.0, e.PosZ)
572575
},
573576
},
574577
{
575-
name: "minimal without side or position",
578+
name: "minimal without side, color or position",
576579
input: []string{"200", "captured", "sector", "Alpha"},
577580
check: func(t *testing.T, e core.SectorEvent) {
578581
assert.Equal(t, "captured", e.Name)
579582
assert.Equal(t, "", e.Side)
583+
assert.Equal(t, "", e.Color)
580584
},
581585
},
582586
{
@@ -591,17 +595,17 @@ func TestParseSectorEvent(t *testing.T) {
591595
},
592596
{
593597
name: "error: bad position X",
594-
input: []string{"200", "captured", "sector", "Alpha", "WEST", "not_a_number", "200", "0"},
598+
input: []string{"200", "captured", "sector", "Alpha", "WEST", "#FF0000", "not_a_number", "200", "0"},
595599
wantErr: true,
596600
},
597601
{
598602
name: "error: bad position Y",
599-
input: []string{"200", "captured", "sector", "Alpha", "WEST", "100", "not_a_number", "0"},
603+
input: []string{"200", "captured", "sector", "Alpha", "WEST", "#FF0000", "100", "not_a_number", "0"},
600604
wantErr: true,
601605
},
602606
{
603607
name: "error: bad position Z",
604-
input: []string{"200", "captured", "sector", "Alpha", "WEST", "100", "200", "not_a_number"},
608+
input: []string{"200", "captured", "sector", "Alpha", "WEST", "#FF0000", "100", "200", "not_a_number"},
605609
wantErr: true,
606610
},
607611
}

internal/storage/memory/export/v1/builder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,12 @@ func Build(data *MissionData) Export {
280280
}
281281

282282
// Convert sector events
283-
// Format: [frameNum, "captured"|"contested", [objectType, unitName, side, [x, y, z]]]
283+
// Format: [frameNum, "captured"|"contested", [objectType, unitName, side, color, [x, y, z]]]
284284
for _, evt := range data.SectorEvents {
285285
export.Events = append(export.Events, []any{
286286
frameToV1(evt.CaptureFrame),
287287
evt.Name,
288-
[]any{evt.ObjectType, evt.UnitName, evt.Side, []float64{evt.PosX, evt.PosY, evt.PosZ}},
288+
[]any{evt.ObjectType, evt.UnitName, evt.Side, evt.Color, []float64{evt.PosX, evt.PosY, evt.PosZ}},
289289
})
290290
}
291291

internal/storage/memory/export/v1/builder_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,8 @@ func TestBuildWithSectorEvents(t *testing.T) {
490490
Vehicles: make(map[uint16]*VehicleRecord),
491491
Markers: make(map[string]*MarkerRecord),
492492
SectorEvents: []core.SectorEvent{
493-
{CaptureFrame: 15, Name: "captured", ObjectType: "sector", UnitName: "Sector Alpha", Side: "WEST", PosX: 100.5, PosY: 200.3, PosZ: 0},
494-
{CaptureFrame: 30, Name: "contested", ObjectType: "flag", UnitName: "Flag Bravo", Side: "", PosX: 300, PosY: 400, PosZ: 10},
493+
{CaptureFrame: 15, Name: "captured", ObjectType: "sector", UnitName: "Sector Alpha", Side: "WEST", Color: "#0000FF", PosX: 100.5, PosY: 200.3, PosZ: 0},
494+
{CaptureFrame: 30, Name: "contested", ObjectType: "sector", UnitName: "Sector Bravo", Side: "", PosX: 300, PosY: 400, PosZ: 10},
495495
},
496496
}
497497

@@ -507,7 +507,8 @@ func TestBuildWithSectorEvents(t *testing.T) {
507507
assert.Equal(t, "sector", payload0[0])
508508
assert.Equal(t, "Sector Alpha", payload0[1])
509509
assert.Equal(t, "WEST", payload0[2])
510-
pos0 := payload0[3].([]float64)
510+
assert.Equal(t, "#0000FF", payload0[3])
511+
pos0 := payload0[4].([]float64)
511512
assert.Equal(t, 100.5, pos0[0])
512513
assert.Equal(t, 200.3, pos0[1])
513514
assert.Equal(t, 0.0, pos0[2])

internal/storage/memory/export_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func TestIntegrationFullExport(t *testing.T) {
7777
CaptureFrame: 15, Name: "connected", Message: "Player1 connected", ExtraData: map[string]any{"uid": "12345"},
7878
}))
7979
require.NoError(t, b.RecordSectorEvent(&core.SectorEvent{
80-
CaptureFrame: 8, Name: "captured", ObjectType: "sector", UnitName: "Sector Alpha", Side: "WEST", PosX: 100.5, PosY: 200.3, PosZ: 0,
80+
CaptureFrame: 8, Name: "captured", ObjectType: "sector", UnitName: "Sector Alpha", Side: "WEST", Color: "#00FF00", PosX: 100.5, PosY: 200.3, PosZ: 0,
8181
}))
8282
require.NoError(t, b.RecordEndMissionEvent(&core.EndMissionEvent{
8383
CaptureFrame: 25, Side: "WEST", Message: "BLUFOR wins",
@@ -168,6 +168,7 @@ func TestIntegrationFullExport(t *testing.T) {
168168
assert.Equal(t, "sector", sectorPayload[0])
169169
assert.Equal(t, "Sector Alpha", sectorPayload[1])
170170
assert.Equal(t, "WEST", sectorPayload[2])
171+
assert.Equal(t, "#00FF00", sectorPayload[3])
171172

172173
// General event at frame 15 (v1: 14)
173174
assert.EqualValues(t, 14, export.Events[1][0])

pkg/core/events.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ type SectorEvent struct {
3434
Time time.Time
3535
CaptureFrame Frame
3636
Name string // "captured", "contested"
37-
ObjectType string // "sector", etc.
38-
UnitName string // name of the sector
37+
ObjectType string // "sector", "flag", etc.
38+
UnitName string // name of the sector or player
3939
Side string // capturing side ("WEST", "EAST", etc.) or empty
40+
Color string // hex color (e.g. "#FF0000") or empty
4041
PosX float64
4142
PosY float64
4243
PosZ float64

0 commit comments

Comments
 (0)