diff --git a/lib/svrquery/protocol/titanfall/query.go b/lib/svrquery/protocol/titanfall/query.go index c31a981..f96eb99 100644 --- a/lib/svrquery/protocol/titanfall/query.go +++ b/lib/svrquery/protocol/titanfall/query.go @@ -166,6 +166,7 @@ func (q *queryer) Query() (resp protocol.Responser, err error) { if err = r.Read(&i.PerformanceInfoV9); err != nil { return nil, err } + i.PerformanceInfo = i.PerformanceInfoV9.PerformanceInfo } else if i.Version > 4 { // PerformanceInfo. if err = r.Read(&i.PerformanceInfo); err != nil { @@ -178,16 +179,19 @@ func (q *queryer) Query() (resp protocol.Responser, err error) { if err = r.Read(&i.MatchStateV10); err != nil { return nil, err } + i.MatchStateV9 = i.MatchStateV10.MatchStateV9 + i.MatchStateV6 = i.MatchStateV10.toV6() } else if i.Version >= 9 { if err = r.Read(&i.MatchStateV9); err != nil { return nil, err } + i.MatchStateV6 = i.MatchStateV9.toV6() } else if i.Version > 5 { - // MatchState and Teams. - if err = r.Read(&i.MatchState); err != nil { + // MatchStateV6 and Teams. + if err = r.Read(&i.MatchStateV6); err != nil { return nil, err } - } else if err = r.Read(&i.MatchState.MatchStateV2); err != nil { + } else if err = r.Read(&i.MatchStateV6.MatchStateV2); err != nil { return nil, err } @@ -210,6 +214,7 @@ func (q *queryer) instanceInfo(r *common.BinaryReader, i *Info) (err error) { if err = r.Read(&i.InstanceInfoV8); err != nil { return err } + i.InstanceInfo = i.InstanceInfoV8.toV1() } else { if err = r.Read(&i.InstanceInfo); err != nil { return err diff --git a/lib/svrquery/protocol/titanfall/query_test.go b/lib/svrquery/protocol/titanfall/query_test.go index c4fc793..fc878b4 100644 --- a/lib/svrquery/protocol/titanfall/query_test.go +++ b/lib/svrquery/protocol/titanfall/query_test.go @@ -41,7 +41,7 @@ var ( Map: "mp_rr_desertlands_64k_x_64k", }, PerformanceInfo: PerformanceInfo{}, - MatchState: MatchState{ + MatchStateV6: MatchStateV6{ MatchStateV2: MatchStateV2{ Phase: 2, MaxRounds: 1, @@ -51,6 +51,7 @@ var ( TimePassed: 0, MaxScore: 50, }, + TeamsLeftWithPlayersNum: 0, }, } ) @@ -75,7 +76,7 @@ func TestQuery(t *testing.T) { AverageUserCommandTime: 3, MaxUserCommandTime: 4, } - v7.MatchState.TeamsLeftWithPlayersNum = 6 + v7.MatchStateV6.TeamsLeftWithPlayersNum = 6 v8 := v7 v8.Version = 8 @@ -87,7 +88,6 @@ func TestQuery(t *testing.T) { HealthFlags: 0, RandomServerID: 0, } - v8.InstanceInfo = InstanceInfo{} v9 := v8 v9.Version = 9 @@ -98,13 +98,20 @@ func TestQuery(t *testing.T) { CommitMemory: 8472, ResidentMemory: 3901, } - v9.PerformanceInfo = PerformanceInfo{} + // Newer version of the match state are dramatically different to the older ones. So wipe with a new copy that + // looks like what will be retained by the compatability code. v9.MatchStateV9 = MatchStateV9{ Phase: 3, TimePassed: 0, TeamsLeftWithPlayersNum: 0, } - v9.MatchState = MatchState{} + v9.MatchStateV6 = MatchStateV6{ + MatchStateV2: MatchStateV2{ + Phase: 3, + TimePassed: 0, + }, + TeamsLeftWithPlayersNum: 0, + } v10 := v9 v10.Version = 10 @@ -117,7 +124,6 @@ func TestQuery(t *testing.T) { CurrentEntityPropertyCount: 2, MaxEntityPropertyCount: 5, } - v10.MatchStateV9 = MatchStateV9{} cases := []struct { name string diff --git a/lib/svrquery/protocol/titanfall/types.go b/lib/svrquery/protocol/titanfall/types.go index 903a189..2c04ca5 100644 --- a/lib/svrquery/protocol/titanfall/types.go +++ b/lib/svrquery/protocol/titanfall/types.go @@ -25,7 +25,7 @@ type Info struct { // Version 9+ PerformanceInfoV9 // Version 2+ - MatchState + MatchStateV6 // Version 9+ MatchStateV9 // Version 10+ @@ -77,6 +77,7 @@ type InstanceInfo struct { } // InstanceInfoV8 represents instance information contained in a query response. +// We use a separate structure as the order is important for parsing. type InstanceInfoV8 struct { Retail byte InstanceType byte @@ -86,6 +87,17 @@ type InstanceInfoV8 struct { RandomServerID uint32 } +// toV1 converts the V8 instance info to the V1 format. +func (i InstanceInfoV8) toV1() InstanceInfo { + return InstanceInfo{ + Retail: i.Retail, + InstanceType: i.InstanceType, + ClientCRC: i.ClientCRC, + NetProtocol: i.NetProtocol, + RandomServerID: uint64(i.RandomServerID), + } +} + // HealthFlags allows us to read the health bits and output them // in an easy to consume json format. type HealthFlags uint32 @@ -200,6 +212,20 @@ type MatchStateV9 struct { TeamsLeftWithPlayersNum uint16 } +// toV6 converts the V9 match state to the V6 format. Preserving fields where possible. +func (m MatchStateV9) toV6() MatchStateV6 { + // The older version of the match state is dramatically different to the new version so not + // all information is available through the backwards compatibility interface. + return MatchStateV6{ + // The v2 stricture + MatchStateV2: MatchStateV2{ + Phase: m.Phase, + TimePassed: m.TimePassed, + }, + TeamsLeftWithPlayersNum: m.TeamsLeftWithPlayersNum, + } +} + // MatchStateV2 represents match state contained in a query response. // This contains a legacy v2 version of matchstate type MatchStateV2 struct { @@ -212,8 +238,8 @@ type MatchStateV2 struct { MaxScore uint16 } -// MatchState represents match state contained in a query response. -type MatchState struct { +// MatchStateV6 represents match state contained in a query response. +type MatchStateV6 struct { MatchStateV2 TeamsLeftWithPlayersNum uint16 }