anti cheat: don't trust the client, move trip completions to server
All checks were successful
pedestrian-simulator / build (push) Successful in 1m11s
All checks were successful
pedestrian-simulator / build (push) Successful in 1m11s
This commit is contained in:
@@ -7,11 +7,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const METERS_PER_STEP = 0.762 // Match frontend
|
||||
|
||||
type TripState struct {
|
||||
StartDate string `json:"start_date"` // YYYY-MM-DD
|
||||
StartTime time.Time `json:"start_time"` // Exact start time
|
||||
StartDayInitialSteps int `json:"start_day_initial_steps"` // Steps on the tracker when trip started
|
||||
DailyCache map[string]int `json:"daily_cache"` // Cache of steps for past days
|
||||
|
||||
// Trip Metadata
|
||||
TripType string `json:"trip_type"`
|
||||
RouteName string `json:"route_name"`
|
||||
StartAddress string `json:"start_address"`
|
||||
EndAddress string `json:"end_address"`
|
||||
KmlID *int `json:"kml_id"`
|
||||
TotalDistance float64 `json:"total_distance"` // in km
|
||||
}
|
||||
|
||||
type StepManager struct {
|
||||
@@ -47,13 +57,20 @@ func (sm *StepManager) LoadTripState() error {
|
||||
|
||||
func (sm *StepManager) loadTripStateLocked() error {
|
||||
var startTime time.Time
|
||||
var kmlID sql.NullInt64
|
||||
err := db.QueryRow(`
|
||||
SELECT start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time
|
||||
SELECT start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time,
|
||||
trip_type, route_name, start_address, end_address, kml_id, total_distance
|
||||
FROM trips WHERE user_id = ?
|
||||
`, sm.userID).Scan(
|
||||
&sm.tripState.StartDate, &startTime, &sm.tripState.StartDayInitialSteps,
|
||||
&sm.previousTotalSteps, &sm.targetTotalSteps, &sm.lastSyncTime, &sm.nextSyncTime,
|
||||
&sm.tripState.TripType, &sm.tripState.RouteName, &sm.tripState.StartAddress, &sm.tripState.EndAddress, &kmlID, &sm.tripState.TotalDistance,
|
||||
)
|
||||
if err == nil && kmlID.Valid {
|
||||
id := int(kmlID.Int64)
|
||||
sm.tripState.KmlID = &id
|
||||
}
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// Initialize with defaults if no trip exists
|
||||
@@ -94,8 +111,9 @@ func (sm *StepManager) SaveTripState() {
|
||||
|
||||
func (sm *StepManager) saveTripStateLocked() {
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO trips (user_id, start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO trips (user_id, start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time,
|
||||
trip_type, route_name, start_address, end_address, kml_id, total_distance)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
start_date = VALUES(start_date),
|
||||
start_time = VALUES(start_time),
|
||||
@@ -103,9 +121,16 @@ func (sm *StepManager) saveTripStateLocked() {
|
||||
previous_total_steps = VALUES(previous_total_steps),
|
||||
target_total_steps = VALUES(target_total_steps),
|
||||
last_sync_time = VALUES(last_sync_time),
|
||||
next_sync_time = VALUES(next_sync_time)
|
||||
next_sync_time = VALUES(next_sync_time),
|
||||
trip_type = VALUES(trip_type),
|
||||
route_name = VALUES(route_name),
|
||||
start_address = VALUES(start_address),
|
||||
end_address = VALUES(end_address),
|
||||
kml_id = VALUES(kml_id),
|
||||
total_distance = VALUES(total_distance)
|
||||
`, sm.userID, sm.tripState.StartDate, sm.tripState.StartTime, sm.tripState.StartDayInitialSteps,
|
||||
sm.previousTotalSteps, sm.targetTotalSteps, sm.lastSyncTime, sm.nextSyncTime)
|
||||
sm.previousTotalSteps, sm.targetTotalSteps, sm.lastSyncTime, sm.nextSyncTime,
|
||||
sm.tripState.TripType, sm.tripState.RouteName, sm.tripState.StartAddress, sm.tripState.EndAddress, sm.tripState.KmlID, sm.tripState.TotalDistance)
|
||||
if err != nil {
|
||||
fmt.Printf("Error saving trip state: %v\n", err)
|
||||
}
|
||||
@@ -123,7 +148,7 @@ func (sm *StepManager) saveTripStateLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *StepManager) StartNewTrip() {
|
||||
func (sm *StepManager) StartNewTrip(metadata TripState) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
now := time.Now()
|
||||
@@ -137,6 +162,12 @@ func (sm *StepManager) StartNewTrip() {
|
||||
StartTime: now,
|
||||
StartDayInitialSteps: initialSteps,
|
||||
DailyCache: make(map[string]int),
|
||||
TripType: metadata.TripType,
|
||||
RouteName: metadata.RouteName,
|
||||
StartAddress: metadata.StartAddress,
|
||||
EndAddress: metadata.EndAddress,
|
||||
KmlID: metadata.KmlID,
|
||||
TotalDistance: metadata.TotalDistance,
|
||||
}
|
||||
// On new trip, previous total is 0
|
||||
sm.previousTotalSteps = 0
|
||||
@@ -244,8 +275,6 @@ func (sm *StepManager) performSync(interval time.Duration) {
|
||||
sm.lastSyncTime = time.Now()
|
||||
sm.nextSyncTime = time.Now().Add(interval)
|
||||
|
||||
// Save final state with explicit NOW() for last_sync_time to ensure DB consistency
|
||||
// next_sync_time is also calculated in SQL relative to the same NOW()
|
||||
_, err = db.Exec(`
|
||||
UPDATE trips SET
|
||||
last_sync_time = NOW(),
|
||||
@@ -258,9 +287,42 @@ func (sm *StepManager) performSync(interval time.Duration) {
|
||||
fmt.Printf("Error saving sync completion: %v\n", err)
|
||||
}
|
||||
|
||||
// Check for Trip Completion
|
||||
if sm.tripState.TotalDistance > 0 {
|
||||
currentDistance := float64(sm.targetTotalSteps) * METERS_PER_STEP / 1000.0
|
||||
if currentDistance >= sm.tripState.TotalDistance {
|
||||
fmt.Printf("[StepManager] Trip Fulfillment Detected! %.2f / %.2f km\n", currentDistance, sm.tripState.TotalDistance)
|
||||
sm.recordTripCompletionLocked()
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Sync Complete. Total Trip Steps: %d\n", sm.targetTotalSteps)
|
||||
}
|
||||
|
||||
func (sm *StepManager) recordTripCompletionLocked() {
|
||||
// 1. Insert into completed_trips
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO completed_trips (user_id, trip_type, route_name, start_address, end_address, kml_id, distance)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`, sm.userID, sm.tripState.TripType, sm.tripState.RouteName, sm.tripState.StartAddress, sm.tripState.EndAddress, sm.tripState.KmlID, sm.tripState.TotalDistance)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("[StepManager] Error recording completion: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Delete from active trips
|
||||
_, err = db.Exec("DELETE FROM trips WHERE user_id = ?", sm.userID)
|
||||
if err != nil {
|
||||
fmt.Printf("[StepManager] Error clearing active trip: %v\n", err)
|
||||
}
|
||||
|
||||
// 3. Reset local state
|
||||
sm.tripState.StartDate = ""
|
||||
sm.previousTotalSteps = 0
|
||||
sm.targetTotalSteps = 0
|
||||
}
|
||||
|
||||
// calculateSmoothedTokenAt returns the interpolated step count at a given time
|
||||
func (sm *StepManager) calculateSmoothedTokenAt(t time.Time) int {
|
||||
totalDuration := sm.nextSyncTime.Sub(sm.lastSyncTime)
|
||||
|
||||
Reference in New Issue
Block a user