diff --git a/cmd/bind.go b/cmd/bind.go index 6bff2ca..2b15ce5 100644 --- a/cmd/bind.go +++ b/cmd/bind.go @@ -3,13 +3,6 @@ package cmd import ( "context" "fmt" - "io" - "net" - "net/http" - "os" - "os/signal" - "strings" - "github.com/jakoblorz/autofone/constants" "github.com/jakoblorz/autofone/packets/process" "github.com/jakoblorz/autofone/packets/process/reader" @@ -19,6 +12,12 @@ import ( "github.com/jakoblorz/autofone/pkg/streamdb" "github.com/spf13/cobra" "golang.org/x/net/websocket" + "io" + "net" + "net/http" + "os" + "os/signal" + "strings" ) type snapshotWriter struct { @@ -162,15 +161,16 @@ for the packet ids to select. }() go func() { boltWriter := &writer.Bolt{ - P: &stream, - Client: api, - DB: db, + P: &stream, + DB: db, } boltWriter.Motion = writer.NewMotionDebouncer(boltWriter, 0) boltWriter.Lap = writer.NewPacketDebouncer(boltWriter, 0) boltWriter.CarTelemetry = writer.NewPacketDebouncer(boltWriter, 0) boltWriter.CarStatus = writer.NewPacketDebouncer(boltWriter, 0) boltWriter.SessionHistory = writer.NewSessionHistoryDebouncer(boltWriter, 0) + boltWriter.TyreSets = writer.NewTyreSetsDebouncer(boltWriter, 0) + boltWriter.MotionEx = writer.NewMotionExDebouncer(boltWriter, 0) defer boltWriter.Close() for { diff --git a/go.mod b/go.mod index faa60c7..3e1bf92 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/boltdb/bolt v1.3.1 + github.com/boltdb/boltd v0.0.0-20150220181201-1f04e2021e45 // indirect github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf github.com/jmoiron/sqlx v1.3.5 github.com/kr/text v0.2.0 // indirect diff --git a/go.sum b/go.sum index 88cfb25..0e3d272 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/boltdb/boltd v0.0.0-20150220181201-1f04e2021e45 h1:ep0QrTbZFgXa8e4lTAdd/NPY9ZiSP/ysDf26+UNPogo= +github.com/boltdb/boltd v0.0.0-20150220181201-1f04e2021e45/go.mod h1:512MO8jN3usqrPdyZ8YtG6vLsQ8wGV5W9cykDwCy1WI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= diff --git a/packets/process/writer/bolt_writer.go b/packets/process/writer/bolt_writer.go index eab3059..4f26591 100644 --- a/packets/process/writer/bolt_writer.go +++ b/packets/process/writer/bolt_writer.go @@ -7,20 +7,20 @@ import ( "github.com/boltdb/bolt" "github.com/jakoblorz/autofone/constants" "github.com/jakoblorz/autofone/packets/process" - "github.com/jakoblorz/autofone/pkg/privateapi" "github.com/jakoblorz/autofone/pkg/streamdb" "go.mongodb.org/mongo-driver/bson/primitive" ) type Bolt struct { *process.P - privateapi.Client Motion *motionDebouncer Lap *packetDebouncer CarTelemetry *packetDebouncer CarStatus *packetDebouncer SessionHistory *sessionHistoryDebouncer + TyreSets *tyreSetsDebouncer + MotionEx *motionExDebouncer DB streamdb.I } @@ -41,6 +41,12 @@ func (ch *Bolt) Close() error { if ch.SessionHistory != nil { ch.SessionHistory.Stop() } + if ch.TyreSets != nil { + ch.TyreSets.Stop() + } + if ch.MotionEx != nil { + ch.MotionEx.timer.Stop() + } return nil } @@ -82,5 +88,13 @@ func (ch *Bolt) Write(m *process.M) { ch.SessionHistory.Write(m) return } + if ch.TyreSets != nil && m.Header.GetPacketID() == constants.PacketTyreSets { + ch.TyreSets.Write(m) + return + } + if ch.MotionEx != nil && m.Header.GetPacketID() == constants.PacketMotionEx { + ch.MotionEx.Write(m) + return + } ch.write(m) } diff --git a/packets/process/writer/bolt_writer_debouncer.go b/packets/process/writer/bolt_writer_debouncer.go index 8884f97..fc73ebc 100644 --- a/packets/process/writer/bolt_writer_debouncer.go +++ b/packets/process/writer/bolt_writer_debouncer.go @@ -13,8 +13,10 @@ import ( const ( motionDebouncerInterval = 250 * time.Millisecond + motionExDebouncerInterval = 100 * time.Millisecond packetDebouncerInterval = 1 * time.Second sessionHistoryDebouncerInterval = 10 * time.Second + tyreSetsDebouncerInterval = 10 * time.Second ) type writer interface { @@ -50,11 +52,25 @@ func NewMotionDebouncer(ch writer, interval time.Duration) *motionDebouncer { packages_21: make([]*packets.PacketMotionData21, 0), packages_22: make([]*packets.PacketMotionData22, 0), + packages_23: make([]*packets.PacketMotionData23, 0), } mdc.timer = time.AfterFunc(motionDebouncerInterval, func() { mdc.WriteTo(ch) }) return mdc } +func NewMotionExDebouncer(ch writer, interval time.Duration) *motionExDebouncer { + if interval == 0 { + interval = motionExDebouncerInterval + } + mdc := &motionExDebouncer{ + mx: &sync.Mutex{}, + + packages_23: make([]*packets.PacketMotionExData23, 0), + } + mdc.timer = time.AfterFunc(motionExDebouncerInterval, func() { mdc.WriteTo(ch) }) + return mdc +} + func (dbc *packetDebouncer) Write(m *process.M) { dbc.mx.Lock() defer dbc.mx.Unlock() @@ -78,6 +94,67 @@ func (dbc *packetDebouncer) WriteTo(ch writer) { dbc.currentPacket = nil } +type motionExDebouncer struct { + mx sync.Locker + timer *time.Timer + + h packets.PacketHeader + + packages_23 []*packets.PacketMotionExData23 +} + +func (dbc *motionExDebouncer) Write(m *process.M) { + dbc.mx.Lock() + defer dbc.mx.Unlock() + + dbc.h = m.Header + + if m.Header.GetPacketFormat() == constants.PacketFormat_2023 { + dbc.packages_23 = append(dbc.packages_23, m.Pack.(*packets.PacketMotionExData23)) + } +} + +func (dbc *motionExDebouncer) WriteTo(ch writer) { + dbc.mx.Lock() + defer dbc.mx.Unlock() + defer func() { + dbc.timer = time.AfterFunc(motionDebouncerInterval, func() { + dbc.WriteTo(ch) + }) + }() + if dbc.h == nil || len(dbc.packages_23) == 0 { + return + } + + var pack interface{} + if dbc.h.GetPacketFormat() == constants.PacketFormat_2023 { + if len(dbc.packages_23) == 0 { + return + } + if len(dbc.packages_23) == 1 { + pack = dbc.packages_23[0] + } else { + pack = averageAndLastPlayerCarMotionEx23(dbc.packages_23).AverageAndLastPlayerCarMotionEx() + } + } + + m := &process.M{ + Header: dbc.h, + Pack: pack, + } + + var err error + m.Buffer, err = packets.Write_LE(pack) + if err != nil { + log.Printf("error encoding averaged packet: %v", err) + return + } + + ch.write(m) + dbc.h = nil + dbc.packages_23 = make([]*packets.PacketMotionExData23, 0) +} + type motionDebouncer struct { mx sync.Locker timer *time.Timer @@ -86,6 +163,7 @@ type motionDebouncer struct { packages_21 []*packets.PacketMotionData21 packages_22 []*packets.PacketMotionData22 + packages_23 []*packets.PacketMotionData23 } func (dbc *motionDebouncer) Write(m *process.M) { @@ -100,6 +178,9 @@ func (dbc *motionDebouncer) Write(m *process.M) { if m.Header.GetPacketFormat() == constants.PacketFormat_2022 { dbc.packages_22 = append(dbc.packages_22, m.Pack.(*packets.PacketMotionData22)) } + if m.Header.GetPacketFormat() == constants.PacketFormat_2023 { + dbc.packages_23 = append(dbc.packages_23, m.Pack.(*packets.PacketMotionData23)) + } } func (dbc *motionDebouncer) WriteTo(ch writer) { @@ -110,7 +191,7 @@ func (dbc *motionDebouncer) WriteTo(ch writer) { dbc.WriteTo(ch) }) }() - if dbc.h == nil || (len(dbc.packages_21) == 0 && len(dbc.packages_22) == 0) { + if dbc.h == nil || (len(dbc.packages_21) == 0 && len(dbc.packages_22) == 0 && len(dbc.packages_23) == 0) { return } @@ -135,6 +216,16 @@ func (dbc *motionDebouncer) WriteTo(ch writer) { pack = averageAndLastPlayerCarMotion22(dbc.packages_22).AverageAndLastPlayerCarMotion() } } + if dbc.h.GetPacketFormat() == constants.PacketFormat_2023 { + if len(dbc.packages_23) == 0 { + return + } + if len(dbc.packages_23) == 1 { + pack = dbc.packages_23[0] + } else { + pack = averageAndLastPlayerCarMotion23(dbc.packages_23).AverageAndLastPlayerCarMotion() + } + } m := &process.M{ Header: dbc.h, @@ -152,6 +243,7 @@ func (dbc *motionDebouncer) WriteTo(ch writer) { dbc.h = nil dbc.packages_21 = make([]*packets.PacketMotionData21, 0) dbc.packages_22 = make([]*packets.PacketMotionData22, 0) + dbc.packages_23 = make([]*packets.PacketMotionData23, 0) } type averageAndLastPlayerCarMotion21 []*packets.PacketMotionData21 @@ -262,6 +354,173 @@ func (a averageAndLastPlayerCarMotion22) AverageAndLastPlayerCarMotion() *packet return &pmd } +type averageAndLastPlayerCarMotion23 []*packets.PacketMotionData23 + +func (a averageAndLastPlayerCarMotion23) AverageAndLastPlayerCarMotion() *packets.PacketMotionData23 { + pmd := *a[len(a)-1] + pmd.CarMotionData = [22]packets.CarMotionData23{} + for i := 0; i < len(pmd.CarMotionData); i++ { + for _, p := range a { + pmd.CarMotionData[i].WorldPositionX += p.CarMotionData[i].WorldPositionX + pmd.CarMotionData[i].WorldPositionY += p.CarMotionData[i].WorldPositionY + pmd.CarMotionData[i].WorldPositionZ += p.CarMotionData[i].WorldPositionZ + pmd.CarMotionData[i].WorldVelocityX += p.CarMotionData[i].WorldVelocityX + pmd.CarMotionData[i].WorldVelocityY += p.CarMotionData[i].WorldVelocityY + pmd.CarMotionData[i].WorldVelocityZ += p.CarMotionData[i].WorldVelocityZ + + pmd.CarMotionData[i].WorldForwardDirX += int16(p.CarMotionData[i].WorldForwardDirX) + pmd.CarMotionData[i].WorldForwardDirY += int16(p.CarMotionData[i].WorldForwardDirY) + pmd.CarMotionData[i].WorldForwardDirZ += int16(p.CarMotionData[i].WorldForwardDirZ) + pmd.CarMotionData[i].WorldRightDirX += int16(p.CarMotionData[i].WorldRightDirX) + pmd.CarMotionData[i].WorldRightDirY += int16(p.CarMotionData[i].WorldRightDirY) + pmd.CarMotionData[i].WorldRightDirZ += int16(p.CarMotionData[i].WorldRightDirZ) + + pmd.CarMotionData[i].GForceLateral += p.CarMotionData[i].GForceLateral + pmd.CarMotionData[i].GForceLongitudinal += p.CarMotionData[i].GForceLongitudinal + pmd.CarMotionData[i].GForceVertical += p.CarMotionData[i].GForceVertical + pmd.CarMotionData[i].Yaw += p.CarMotionData[i].Yaw + pmd.CarMotionData[i].Pitch += p.CarMotionData[i].Pitch + pmd.CarMotionData[i].Roll += p.CarMotionData[i].Roll + } + + pmd.CarMotionData[i].WorldPositionX /= float32(len(a)) + pmd.CarMotionData[i].WorldPositionY /= float32(len(a)) + pmd.CarMotionData[i].WorldPositionZ /= float32(len(a)) + pmd.CarMotionData[i].WorldVelocityX /= float32(len(a)) + pmd.CarMotionData[i].WorldVelocityY /= float32(len(a)) + pmd.CarMotionData[i].WorldVelocityZ /= float32(len(a)) + + pmd.CarMotionData[i].WorldForwardDirX /= int16(len(a)) + pmd.CarMotionData[i].WorldForwardDirY /= int16(len(a)) + pmd.CarMotionData[i].WorldForwardDirZ /= int16(len(a)) + pmd.CarMotionData[i].WorldRightDirX /= int16(len(a)) + pmd.CarMotionData[i].WorldRightDirY /= int16(len(a)) + pmd.CarMotionData[i].WorldRightDirZ /= int16(len(a)) + + pmd.CarMotionData[i].GForceLateral /= float32(len(a)) + pmd.CarMotionData[i].GForceLongitudinal /= float32(len(a)) + pmd.CarMotionData[i].GForceVertical /= float32(len(a)) + pmd.CarMotionData[i].Yaw /= float32(len(a)) + pmd.CarMotionData[i].Pitch /= float32(len(a)) + pmd.CarMotionData[i].Roll /= float32(len(a)) + } + + return &pmd +} + +type averageAndLastPlayerCarMotionEx23 []*packets.PacketMotionExData23 + +func (a averageAndLastPlayerCarMotionEx23) AverageAndLastPlayerCarMotionEx() *packets.PacketMotionExData23 { + pmd := *a[len(a)-1] + for _, p := range a { + pmd.SuspensionPosition[0] += p.SuspensionPosition[0] + pmd.SuspensionPosition[1] += p.SuspensionPosition[1] + pmd.SuspensionPosition[2] += p.SuspensionPosition[2] + pmd.SuspensionPosition[3] += p.SuspensionPosition[3] + + pmd.SuspensionVelocity[0] += p.SuspensionVelocity[0] + pmd.SuspensionVelocity[1] += p.SuspensionVelocity[1] + pmd.SuspensionVelocity[2] += p.SuspensionVelocity[2] + pmd.SuspensionVelocity[3] += p.SuspensionVelocity[3] + + pmd.SuspensionAcceleration[0] += p.SuspensionAcceleration[0] + pmd.SuspensionAcceleration[1] += p.SuspensionAcceleration[1] + pmd.SuspensionAcceleration[2] += p.SuspensionAcceleration[2] + pmd.SuspensionAcceleration[3] += p.SuspensionAcceleration[3] + + pmd.WheelSpeed[0] += p.WheelSpeed[0] + pmd.WheelSpeed[1] += p.WheelSpeed[1] + pmd.WheelSpeed[2] += p.WheelSpeed[2] + pmd.WheelSpeed[3] += p.WheelSpeed[3] + + pmd.WheelSlipRatio[0] += p.WheelSlipRatio[0] + pmd.WheelSlipRatio[1] += p.WheelSlipRatio[1] + pmd.WheelSlipRatio[2] += p.WheelSlipRatio[2] + pmd.WheelSlipRatio[3] += p.WheelSlipRatio[3] + + pmd.WheelLatForce[0] += p.WheelLatForce[0] + pmd.WheelLatForce[1] += p.WheelLatForce[1] + pmd.WheelLatForce[2] += p.WheelLatForce[2] + pmd.WheelLatForce[3] += p.WheelLatForce[3] + + pmd.WheelLongForce[0] += p.WheelLongForce[0] + pmd.WheelLongForce[1] += p.WheelLongForce[1] + pmd.WheelLongForce[2] += p.WheelLongForce[2] + pmd.WheelLongForce[3] += p.WheelLongForce[3] + + pmd.HeightOfCOGAboveGround += p.HeightOfCOGAboveGround + pmd.LocalVelocityX += p.LocalVelocityX + pmd.LocalVelocityY += p.LocalVelocityY + pmd.LocalVelocityZ += p.LocalVelocityZ + pmd.AngularVelocityX += p.AngularVelocityX + pmd.AngularVelocityY += p.AngularVelocityY + pmd.AngularVelocityZ += p.AngularVelocityZ + pmd.AngularAccelerationX += p.AngularAccelerationX + pmd.AngularAccelerationY += p.AngularAccelerationY + pmd.AngularAccelerationZ += p.AngularAccelerationZ + pmd.FrontWheelAngle += p.FrontWheelAngle + + pmd.WheelVertForce[0] += p.WheelVertForce[0] + pmd.WheelVertForce[1] += p.WheelVertForce[1] + pmd.WheelVertForce[2] += p.WheelVertForce[2] + pmd.WheelVertForce[3] += p.WheelVertForce[3] + } + + pmd.SuspensionPosition[0] /= float32(len(a)) + pmd.SuspensionPosition[1] /= float32(len(a)) + pmd.SuspensionPosition[2] /= float32(len(a)) + pmd.SuspensionPosition[3] /= float32(len(a)) + + pmd.SuspensionVelocity[0] /= float32(len(a)) + pmd.SuspensionVelocity[1] /= float32(len(a)) + pmd.SuspensionVelocity[2] /= float32(len(a)) + pmd.SuspensionVelocity[3] /= float32(len(a)) + + pmd.SuspensionAcceleration[0] /= float32(len(a)) + pmd.SuspensionAcceleration[1] /= float32(len(a)) + pmd.SuspensionAcceleration[2] /= float32(len(a)) + pmd.SuspensionAcceleration[3] /= float32(len(a)) + + pmd.WheelSpeed[0] /= float32(len(a)) + pmd.WheelSpeed[1] /= float32(len(a)) + pmd.WheelSpeed[2] /= float32(len(a)) + pmd.WheelSpeed[3] /= float32(len(a)) + + pmd.WheelSlipRatio[0] /= float32(len(a)) + pmd.WheelSlipRatio[1] /= float32(len(a)) + pmd.WheelSlipRatio[2] /= float32(len(a)) + pmd.WheelSlipRatio[3] /= float32(len(a)) + + pmd.WheelLatForce[0] /= float32(len(a)) + pmd.WheelLatForce[1] /= float32(len(a)) + pmd.WheelLatForce[2] /= float32(len(a)) + pmd.WheelLatForce[3] /= float32(len(a)) + + pmd.WheelLongForce[0] /= float32(len(a)) + pmd.WheelLongForce[1] /= float32(len(a)) + pmd.WheelLongForce[2] /= float32(len(a)) + pmd.WheelLongForce[3] /= float32(len(a)) + + pmd.HeightOfCOGAboveGround /= float32(len(a)) + pmd.LocalVelocityX /= float32(len(a)) + pmd.LocalVelocityY /= float32(len(a)) + pmd.LocalVelocityZ /= float32(len(a)) + pmd.AngularVelocityX /= float32(len(a)) + pmd.AngularVelocityY /= float32(len(a)) + pmd.AngularVelocityZ /= float32(len(a)) + pmd.AngularAccelerationX /= float32(len(a)) + pmd.AngularAccelerationY /= float32(len(a)) + pmd.AngularAccelerationZ /= float32(len(a)) + pmd.FrontWheelAngle /= float32(len(a)) + + pmd.WheelVertForce[0] /= float32(len(a)) + pmd.WheelVertForce[1] /= float32(len(a)) + pmd.WheelVertForce[2] /= float32(len(a)) + pmd.WheelVertForce[3] /= float32(len(a)) + + return &pmd +} + func NewSessionHistoryDebouncer(ch writer, interval time.Duration) *sessionHistoryDebouncer { if interval == 0 { interval = sessionHistoryDebouncerInterval @@ -303,4 +562,47 @@ func (dbc *sessionHistoryDebouncer) Write(m *process.M) { dbc.debouncers[sh22.CarIdx].Write(m) return } + if sh23, ok := m.Pack.(*packets.PacketSessionHistoryData23); ok { + if dbc.debouncers[sh23.CarIdx] == nil { + dbc.debouncers[sh23.CarIdx] = NewPacketDebouncer(dbc.ch, dbc.interval) + } + dbc.debouncers[sh23.CarIdx].Write(m) + return + } +} + +func NewTyreSetsDebouncer(ch writer, interval time.Duration) *tyreSetsDebouncer { + if interval == 0 { + interval = tyreSetsDebouncerInterval + } + pdc := &tyreSetsDebouncer{ + ch: ch, + interval: packetDebouncerInterval, + debouncers: make(map[uint8]*packetDebouncer), + } + return pdc +} + +type tyreSetsDebouncer struct { + ch writer + interval time.Duration + debouncers map[uint8]*packetDebouncer +} + +func (dbc *tyreSetsDebouncer) Stop() { + for _, d := range dbc.debouncers { + if d.timer != nil { + d.timer.Stop() + } + } +} + +func (dbc *tyreSetsDebouncer) Write(m *process.M) { + if sh23, ok := m.Pack.(*packets.PacketTyreSetsData23); ok { + if dbc.debouncers[sh23.CarIdx] == nil { + dbc.debouncers[sh23.CarIdx] = NewPacketDebouncer(dbc.ch, dbc.interval) + } + dbc.debouncers[sh23.CarIdx].Write(m) + return + } } diff --git a/pkg/streamdb/bolt.go b/pkg/streamdb/bolt.go index c9f36d3..085668f 100644 --- a/pkg/streamdb/bolt.go +++ b/pkg/streamdb/bolt.go @@ -151,7 +151,7 @@ func (i *stream) close() error { } func (i *stream) rotate() { - rotateWithPriviledges := func(fn func()) { + rotateWithPrivileges := func(fn func()) { i.mx.Lock() go func() { defer i.mx.Unlock() @@ -169,7 +169,7 @@ func (i *stream) rotate() { fn() }() } - rotateWithPriviledges(func() { + rotateWithPrivileges(func() { backup := &bytes.Buffer{} err := i.handleDb.Update(func(tx *bolt.Tx) error { log.Printf("snapshotting %s of data", bytesize.New(float64(tx.Size()))) @@ -191,6 +191,8 @@ func (i *stream) rotate() { constants.PacketLobbyInfo, constants.PacketCarDamage, constants.PacketSessionHistory, + constants.PacketTyreSets, + constants.PacketMotionEx, } { err = tx.DeleteBucket([]byte{bucketName}) if err != nil && !errors.Is(err, bolt.ErrBucketNotFound) {