mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
feat(server): integrate UDP video service and normalize database schema
- Implement UDPVideoService to stream MJPEG video frames from ESP32s via UDP and send control commands. - Normalize race database schema: - Remove redundant driver_ids array from races table (migration 011). - Move per-driver outcomes (total_time_ms, best_lap_ms, position) to race_drivers table (migration 012). - Add device_id column to lobby_drivers to link active physical devices (migration 013). - Update WebSocket handler to pass lobbySvc, driversSvc, and videoSvc for driver identity tracking, syncing in-memory driver metadata, and forwarding WS controls to physical cars. - Add CORS middleware to HTTP routes. - Regenerate Swagger documentation.
This commit is contained in:
@@ -394,9 +394,9 @@ func (r *Runner) makeFinished(idx int) races.FinishedRace {
|
||||
}
|
||||
bestLapMs := int64(20_000 + rng.Intn(8_000))
|
||||
|
||||
// Podium: top-3 by total time. The winner always finishes; the
|
||||
// remaining drivers are sorted by a per-driver total time derived
|
||||
// from the winner's best lap.
|
||||
// Per-driver total time. The winner is fastest; the rest are
|
||||
// winner's time + 2..16 seconds. Build the Results slice with
|
||||
// computed positions.
|
||||
perDriver := make([]int64, len(picked))
|
||||
for i, d := range picked {
|
||||
if d.ID == winner.ID {
|
||||
@@ -419,35 +419,35 @@ func (r *Runner) makeFinished(idx int) races.FinishedRace {
|
||||
all[j-1], all[j] = all[j], all[j-1]
|
||||
}
|
||||
}
|
||||
podium := make([]races.PodiumEntry, 0, 3)
|
||||
for i := 0; i < len(all) && i < 3; i++ {
|
||||
podium = append(podium, races.PodiumEntry{
|
||||
Position: i + 1,
|
||||
DriverID: all[i].d.ID,
|
||||
Name: all[i].d.Nickname,
|
||||
TotalTimeMs: all[i].time,
|
||||
results := make([]races.DriverResult, 0, len(all))
|
||||
for i, row := range all {
|
||||
pos := i + 1
|
||||
// Podium only for the top-3; the rest are just DNF-ish finishers
|
||||
// (they have a time and a position).
|
||||
results = append(results, races.DriverResult{
|
||||
DriverID: row.d.ID,
|
||||
TotalTimeMs: row.time,
|
||||
BestLapMs: bestLapMs,
|
||||
Position: &pos,
|
||||
})
|
||||
}
|
||||
|
||||
return races.FinishedRace{
|
||||
ID: fmt.Sprintf("seed-finished-%03d", idx+1),
|
||||
Name: fmt.Sprintf("Seed Race #%d (%s)", idx+1, track),
|
||||
TrackID: track,
|
||||
MaxCars: 4,
|
||||
Laps: laps,
|
||||
TimeLimitS: 0,
|
||||
DriverIDs: driverIDs,
|
||||
Status: "finished",
|
||||
CreatedMs: finishedMs - randDuration(rng, 5, 120).Milliseconds(),
|
||||
StartedMs: startedMs,
|
||||
FinishedMs: finishedMs,
|
||||
DurationMs: finishedMs - startedMs,
|
||||
TotalLaps: totalLaps,
|
||||
TotalDrivers: len(driverIDs),
|
||||
WinnerDriverID: winner.ID,
|
||||
WinnerName: winner.Nickname,
|
||||
BestLapMs: bestLapMs,
|
||||
Podium: podium,
|
||||
ID: fmt.Sprintf("seed-finished-%03d", idx+1),
|
||||
Name: fmt.Sprintf("Seed Race #%d (%s)", idx+1, track),
|
||||
TrackID: track,
|
||||
MaxCars: 4,
|
||||
Laps: laps,
|
||||
TimeLimitS: 0,
|
||||
DriverIDs: driverIDs,
|
||||
Status: "finished",
|
||||
CreatedMs: finishedMs - randDuration(rng, 5, 120).Milliseconds(),
|
||||
StartedMs: startedMs,
|
||||
FinishedMs: finishedMs,
|
||||
DurationMs: finishedMs - startedMs,
|
||||
TotalLaps: totalLaps,
|
||||
TotalDrivers: len(driverIDs),
|
||||
Results: results,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +481,8 @@ func (r *Runner) createLive(status lobby.RaceStatus, idx int) (lobby.RaceMeta, e
|
||||
continue
|
||||
}
|
||||
r.lb.SetDriverProfile(d.ID, d.Nickname, "", d.ClanID, d.ClanTag)
|
||||
devID := i
|
||||
_ = r.lb.SelectDevice(d.ID, &devID)
|
||||
if err := r.lb.AddDriverToRace(d.ID, meta.ID); err != nil {
|
||||
// ignore: race may be at capacity
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user