Run pushes typed SCUM facts through POST /api/v1/run/scum/facts, but the production router rejected that path before the signature middleware because runServiceRequest listed individual Run channel prefixes, and MySQL kept SCUM rows inside the whole metadata snapshot instead of platform tables. - /api/v1/run/ is now the signed machine channel space while /api/v1/run/endpoints keeps normal bearer/admin authorization. - MySQL gets real scum_user, scum_user_trajectory, scum_vehicle, scum_vehicle_trajectory and scum_vehicle_lock tables with parameterized per-row repositories instead of full snapshot rewrites. Snapshot-shaped tables from the unreleased interim build are replaced, and SCUM rows still inside a metadata snapshot are migrated once. - Facts ingest verifies the target server plugin type and converges stale online users to offline after SCUMUserOfflineAfter. - The plugin page and browser read one bounded /scum/surface response instead of five list calls per refresh. Call-count budget for one server: per 5s facts batch, one SELECT plus one INSERT/UPDATE per reported user and vehicle, one INSERT per moved trajectory sample or new lock row, one bounded stale-user SELECT, and a trajectory retention DELETE at most once per hour. One browser refresh issues one surface request every 15s instead of five list requests.
318 lines
7.2 KiB
Go
318 lines
7.2 KiB
Go
package domain
|
|
|
|
import "time"
|
|
|
|
const (
|
|
SCUMUserOfflineAfter = 15 * time.Minute
|
|
SCUMWelcomeMessageDelay = time.Minute
|
|
SCUMDefaultListLimit = 500
|
|
SCUMDefaultTrajectoryLimit = 500
|
|
)
|
|
|
|
type SCUMPosition struct {
|
|
X float64
|
|
Y float64
|
|
Z float64
|
|
}
|
|
|
|
type SCUMUser struct {
|
|
ID string
|
|
ServerInstanceID string
|
|
SteamID string
|
|
DisplayName string
|
|
SquadID int64
|
|
RegisteredAt time.Time
|
|
LastLoginAt time.Time
|
|
LastActivityAt time.Time
|
|
LastLoginIP string
|
|
Online bool
|
|
X *float64
|
|
Y *float64
|
|
Z *float64
|
|
BankBalance *int64
|
|
GoldBars *int64
|
|
RiddenVehicleID string
|
|
GameVehicleID string
|
|
LastLoginAge time.Duration
|
|
WelcomeQueuedAt time.Time
|
|
WelcomeLastError string
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type SCUMUserFilter struct {
|
|
ServerInstanceID string
|
|
SteamID string
|
|
Online *bool
|
|
ChangedAfter time.Time
|
|
// StaleBefore matches users whose latest activity is older than the given
|
|
// instant. It backs the offline convergence query for stale online users.
|
|
StaleBefore time.Time
|
|
Limit int
|
|
}
|
|
|
|
type SCUMUserTrajectory struct {
|
|
ID string
|
|
ServerInstanceID string
|
|
SCUMUserID string
|
|
SteamID string
|
|
DisplayName string
|
|
X float64
|
|
Y float64
|
|
Z float64
|
|
RiddenVehicleID string
|
|
GameVehicleID string
|
|
ObservedAt time.Time
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
type SCUMUserTrajectoryFilter struct {
|
|
ServerInstanceID string
|
|
SCUMUserID string
|
|
SteamID string
|
|
After time.Time
|
|
Limit int
|
|
}
|
|
|
|
type SCUMVehicle struct {
|
|
ID string
|
|
ServerInstanceID string
|
|
GameVehicleID string
|
|
VehicleClass string
|
|
DisplayName string
|
|
Exists bool
|
|
Locked bool
|
|
X *float64
|
|
Y *float64
|
|
Z *float64
|
|
LastObservedAt time.Time
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type SCUMVehicleFilter struct {
|
|
ServerInstanceID string
|
|
GameVehicleID string
|
|
Exists *bool
|
|
ChangedAfter time.Time
|
|
Limit int
|
|
}
|
|
|
|
type SCUMVehicleTrajectory struct {
|
|
ID string
|
|
ServerInstanceID string
|
|
SCUMVehicleID string
|
|
GameVehicleID string
|
|
VehicleClass string
|
|
X float64
|
|
Y float64
|
|
Z float64
|
|
ObservedAt time.Time
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
type SCUMVehicleTrajectoryFilter struct {
|
|
ServerInstanceID string
|
|
SCUMVehicleID string
|
|
GameVehicleID string
|
|
After time.Time
|
|
Limit int
|
|
}
|
|
|
|
type SCUMVehicleLock struct {
|
|
ID string
|
|
ServerInstanceID string
|
|
SCUMVehicleID string
|
|
GameVehicleID string
|
|
SCUMUserID string
|
|
SteamID string
|
|
LockedAt time.Time
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
type SCUMVehicleLockFilter struct {
|
|
ServerInstanceID string
|
|
SCUMVehicleID string
|
|
GameVehicleID string
|
|
SCUMUserID string
|
|
SteamID string
|
|
After time.Time
|
|
Limit int
|
|
}
|
|
|
|
type SCUMSurface struct {
|
|
Users []SCUMUser
|
|
Vehicles []SCUMVehicle
|
|
UserTrajectories []SCUMUserTrajectory
|
|
VehicleTrajectories []SCUMVehicleTrajectory
|
|
VehicleLocks []SCUMVehicleLock
|
|
}
|
|
|
|
type SCUMFactIngest struct {
|
|
RunEndpointID string
|
|
SessionToken string
|
|
ServerInstanceID string
|
|
Users []SCUMUserFact
|
|
Vehicles []SCUMVehicleFact
|
|
}
|
|
|
|
type SCUMUserFact struct {
|
|
SteamID string
|
|
DisplayName string
|
|
SquadID *int64
|
|
LoginIP string
|
|
Online bool
|
|
Login bool
|
|
ObservedAt time.Time
|
|
LoginObservedAt time.Time
|
|
Position *SCUMPosition
|
|
BankBalance *int64
|
|
GoldBars *int64
|
|
RiddenGameVehicleID string
|
|
}
|
|
|
|
type SCUMVehicleFact struct {
|
|
GameVehicleID string
|
|
VehicleClass string
|
|
DisplayName string
|
|
Exists *bool
|
|
Locked *bool
|
|
LockedBySteamID string
|
|
LockedAt time.Time
|
|
ObservedAt time.Time
|
|
Position *SCUMPosition
|
|
}
|
|
|
|
type SCUMFactIngestResult struct {
|
|
Accepted bool
|
|
AcceptedUserCount int
|
|
AcceptedVehicleCount int
|
|
WelcomeQueuedCount int
|
|
ServerTime time.Time
|
|
}
|
|
|
|
func CopySCUMUser(value SCUMUser) SCUMUser {
|
|
value.X = copyFloatPtr(value.X)
|
|
value.Y = copyFloatPtr(value.Y)
|
|
value.Z = copyFloatPtr(value.Z)
|
|
value.BankBalance = copyInt64Ptr(value.BankBalance)
|
|
value.GoldBars = copyInt64Ptr(value.GoldBars)
|
|
return value
|
|
}
|
|
|
|
func CopySCUMUsers(values []SCUMUser) []SCUMUser {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMUser, len(values))
|
|
for i, value := range values {
|
|
out[i] = CopySCUMUser(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func CopySCUMUserTrajectory(value SCUMUserTrajectory) SCUMUserTrajectory { return value }
|
|
func CopySCUMUserTrajectories(values []SCUMUserTrajectory) []SCUMUserTrajectory {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMUserTrajectory, len(values))
|
|
copy(out, values)
|
|
return out
|
|
}
|
|
|
|
func CopySCUMVehicle(value SCUMVehicle) SCUMVehicle {
|
|
value.X = copyFloatPtr(value.X)
|
|
value.Y = copyFloatPtr(value.Y)
|
|
value.Z = copyFloatPtr(value.Z)
|
|
return value
|
|
}
|
|
|
|
func CopySCUMVehicles(values []SCUMVehicle) []SCUMVehicle {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMVehicle, len(values))
|
|
for i, value := range values {
|
|
out[i] = CopySCUMVehicle(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func CopySCUMVehicleTrajectory(value SCUMVehicleTrajectory) SCUMVehicleTrajectory { return value }
|
|
func CopySCUMVehicleTrajectories(values []SCUMVehicleTrajectory) []SCUMVehicleTrajectory {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMVehicleTrajectory, len(values))
|
|
copy(out, values)
|
|
return out
|
|
}
|
|
|
|
func CopySCUMVehicleLock(value SCUMVehicleLock) SCUMVehicleLock { return value }
|
|
func CopySCUMVehicleLocks(values []SCUMVehicleLock) []SCUMVehicleLock {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMVehicleLock, len(values))
|
|
copy(out, values)
|
|
return out
|
|
}
|
|
|
|
func CopySCUMFactIngest(value SCUMFactIngest) SCUMFactIngest {
|
|
value.Users = CopySCUMUserFacts(value.Users)
|
|
value.Vehicles = CopySCUMVehicleFacts(value.Vehicles)
|
|
return value
|
|
}
|
|
|
|
func CopySCUMUserFacts(values []SCUMUserFact) []SCUMUserFact {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMUserFact, len(values))
|
|
for i, value := range values {
|
|
out[i] = value
|
|
out[i].SquadID = copyInt64Ptr(value.SquadID)
|
|
out[i].BankBalance = copyInt64Ptr(value.BankBalance)
|
|
out[i].GoldBars = copyInt64Ptr(value.GoldBars)
|
|
if value.Position != nil {
|
|
position := *value.Position
|
|
out[i].Position = &position
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func CopySCUMVehicleFacts(values []SCUMVehicleFact) []SCUMVehicleFact {
|
|
if values == nil {
|
|
return nil
|
|
}
|
|
out := make([]SCUMVehicleFact, len(values))
|
|
for i, value := range values {
|
|
out[i] = value
|
|
out[i].Exists = copyBoolPtr(value.Exists)
|
|
out[i].Locked = copyBoolPtr(value.Locked)
|
|
if value.Position != nil {
|
|
position := *value.Position
|
|
out[i].Position = &position
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func copyInt64Ptr(value *int64) *int64 {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copy := *value
|
|
return ©
|
|
}
|
|
|
|
func copyBoolPtr(value *bool) *bool {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
copy := *value
|
|
return ©
|
|
}
|