From e38bff4f2f3d03a61b5c724140ef600ec8c7da51 Mon Sep 17 00:00:00 2001 From: "duck." Date: Fri, 5 Apr 2024 12:38:06 +0100 Subject: [PATCH 1/2] Remove Shares from admin packet format For OpenTTD 14.0 support - maybe we should add support for detecting game version and decoding as such? This isn't backwards compatible. Signed-off-by: duck. --- pkg/admin/events.go | 9 +++++---- pkg/admin/packets/packets_server.go | 8 ++++---- pkg/admin/state.go | 10 ++++++---- pkg/admin/structs.go | 18 +++++++++--------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/pkg/admin/events.go b/pkg/admin/events.go index 9aa67fd..0710f74 100644 --- a/pkg/admin/events.go +++ b/pkg/admin/events.go @@ -122,10 +122,11 @@ type CompanyUpdate struct { // Type 115 Colour uint8 // Main company colour. Password bool // Company is password protected. BankruptcyQuarters uint8 // Quarters of Bankruptcy. - Share1 uint8 // Owner of Share 1. - Share2 uint8 // Owner of Share 2. - Share3 uint8 // Owner of Share 3. - Share4 uint8 // Owner of Share 4. + // Removed in OpenTTD 14.0 + // Share1 uint8 // Owner of Share 1. + // Share2 uint8 // Owner of Share 2. + // Share3 uint8 // Owner of Share 3. + // Share4 uint8 // Owner of Share 4. } // CompanyRemove fires when a company is removed from the game. diff --git a/pkg/admin/packets/packets_server.go b/pkg/admin/packets/packets_server.go index 63a4ed2..045f2a8 100644 --- a/pkg/admin/packets/packets_server.go +++ b/pkg/admin/packets/packets_server.go @@ -272,10 +272,10 @@ type ServerCompanyUpdate struct { // Type 115 Colour uint8 // Main company colour. Password bool // Company is password protected. BankruptcyQuarters uint8 // Quarters of Bankruptcy. - Share1 uint8 // Owner of Share 1. - Share2 uint8 // Owner of Share 2. - Share3 uint8 // Owner of Share 3. - Share4 uint8 // Owner of Share 4. + // Share1 uint8 // Owner of Share 1. + // Share2 uint8 // Owner of Share 2. + // Share3 uint8 // Owner of Share 3. + // Share4 uint8 // Owner of Share 4. } func (p ServerCompanyUpdate) String() string { diff --git a/pkg/admin/state.go b/pkg/admin/state.go index bdce734..82627c8 100644 --- a/pkg/admin/state.go +++ b/pkg/admin/state.go @@ -306,10 +306,12 @@ func (s *State) onCompanyUpdate(se *Session, r *CompanyUpdate) (err error) { com.Colour = helpers.OpenttdColour(r.Colour) com.Passworded = r.Password com.Bankruptcy = r.BankruptcyQuarters - com.Share1 = r.Share1 - com.Share2 = r.Share2 - com.Share3 = r.Share3 - com.Share4 = r.Share4 + + // Shares removed in OpenTTD 14.0 + // com.Share1 = r.Share1 + // com.Share2 = r.Share2 + // com.Share3 = r.Share3 + // com.Share4 = r.Share4 s.Companies[r.ID] = com diff --git a/pkg/admin/structs.go b/pkg/admin/structs.go index 8013669..f27e047 100644 --- a/pkg/admin/structs.go +++ b/pkg/admin/structs.go @@ -106,15 +106,15 @@ type Company struct { // The number of quarters this company has been in a bankruptcy state (this may mean they're about to be dissolved!) Bankruptcy uint8 `json:"bankruptcy"` - // Share data - // Share 1 is owned by... - Share1 uint8 `json:"share1"` - // Share 2 is owned by... - Share2 uint8 `json:"share2"` - // Share 3 is owned by... - Share3 uint8 `json:"share3"` - // Share 4 is owned by... - Share4 uint8 `json:"share4"` + // Share data - removed in OpenTTD 14 + // // Share 1 is owned by... + // Share1 uint8 `json:"share1"` + // // Share 2 is owned by... + // Share2 uint8 `json:"share2"` + // // Share 3 is owned by... + // Share3 uint8 `json:"share3"` + // // Share 4 is owned by... + // Share4 uint8 `json:"share4"` // Cargo delivered in the current quarter. CargoThisQuarter uint16 `json:"cargo_current"` From a037387bc77f641a0f9043c85fd9293a0bc638e4 Mon Sep 17 00:00:00 2001 From: "duck." Date: Tue, 9 Apr 2024 14:21:59 +0100 Subject: [PATCH 2/2] Support shares on versions older than OpenTTD 14 / Protocol 3 Do this by performing buffer length checking when unmarshalling data. Signed-off-by: duck. --- cmd/openttd_adm_query/openttd_scrape.go | 6 +- pkg/admin/events.go | 9 ++- pkg/admin/packets/packets_server.go | 79 +++++++++++++++-------- pkg/admin/proto_unmarshal.go | 83 +++++++++++++++---------- pkg/admin/state.go | 12 ++-- pkg/admin/structs.go | 18 +++--- 6 files changed, 129 insertions(+), 78 deletions(-) diff --git a/cmd/openttd_adm_query/openttd_scrape.go b/cmd/openttd_adm_query/openttd_scrape.go index 5798589..2f3ad69 100644 --- a/cmd/openttd_adm_query/openttd_scrape.go +++ b/cmd/openttd_adm_query/openttd_scrape.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" gopenttd "github.com/ropenttd/gopenttd/pkg/admin" + "github.com/ropenttd/gopenttd/pkg/admin/enum" log "github.com/sirupsen/logrus" "time" ) @@ -39,8 +40,11 @@ func main() { } defer s.Close() + s.RequestUpdates(enum.UpdateTypeCompanyInfo, enum.UpdateFrequencyAutomatically) + s.RequestUpdates(enum.UpdateTypeCompanyEconomy, enum.UpdateFrequencyAutomatically) + // stupid delay to make sure things settle into state (? find a better way to do this) - time.Sleep(3 * time.Second) + time.Sleep(10 * time.Second) state := s.State var b []byte diff --git a/pkg/admin/events.go b/pkg/admin/events.go index 0710f74..9aa67fd 100644 --- a/pkg/admin/events.go +++ b/pkg/admin/events.go @@ -122,11 +122,10 @@ type CompanyUpdate struct { // Type 115 Colour uint8 // Main company colour. Password bool // Company is password protected. BankruptcyQuarters uint8 // Quarters of Bankruptcy. - // Removed in OpenTTD 14.0 - // Share1 uint8 // Owner of Share 1. - // Share2 uint8 // Owner of Share 2. - // Share3 uint8 // Owner of Share 3. - // Share4 uint8 // Owner of Share 4. + Share1 uint8 // Owner of Share 1. + Share2 uint8 // Owner of Share 2. + Share3 uint8 // Owner of Share 3. + Share4 uint8 // Owner of Share 4. } // CompanyRemove fires when a company is removed from the game. diff --git a/pkg/admin/packets/packets_server.go b/pkg/admin/packets/packets_server.go index 045f2a8..158cba2 100644 --- a/pkg/admin/packets/packets_server.go +++ b/pkg/admin/packets/packets_server.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "github.com/ropenttd/gopenttd/pkg/admin/enum" + "io" "reflect" ) @@ -27,33 +28,61 @@ func genericUnmarshal(p AdminResponsePacket, buffer *bytes.Buffer) (err error) { // binary.Read() doesn't appear to work here (always returns 0?) so do things the long way // i.e binary.Read(buffer, binary.LittleEndian, nv) case reflect.Bool: - var nv bool - nv = uint8(buffer.Next(1)[0]) != 0 - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + var nv bool + nv = uint8(buffer.Next(1)[0]) != 0 + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.Uint8: - var nv uint8 - nv = uint8(buffer.Next(1)[0]) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + var nv uint8 + nv = uint8(buffer.Next(1)[0]) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.Uint16: - var nv uint16 - nv = binary.LittleEndian.Uint16(buffer.Next(2)) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 2 { + var nv uint16 + nv = binary.LittleEndian.Uint16(buffer.Next(2)) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.Uint32: - var nv uint32 - nv = binary.LittleEndian.Uint32(buffer.Next(4)) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 4 { + var nv uint32 + nv = binary.LittleEndian.Uint32(buffer.Next(4)) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.Int64: - var nv int64 - binary.Read(buffer, binary.LittleEndian, &nv) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 8 { + var nv int64 + binary.Read(buffer, binary.LittleEndian, &nv) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.Uint64: - var nv uint64 - nv = binary.LittleEndian.Uint64(buffer.Next(8)) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 8 { + var nv uint64 + nv = binary.LittleEndian.Uint64(buffer.Next(8)) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } case reflect.String: - nvBytes, _ := buffer.ReadBytes(byte(0)) - nv := string(bytes.Trim(nvBytes, "\x00")) - f.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + nvBytes, _ := buffer.ReadBytes(byte(0)) + nv := string(bytes.Trim(nvBytes, "\x00")) + f.Set(reflect.ValueOf(nv)) + } else { + return io.ErrUnexpectedEOF + } } } return err @@ -272,10 +301,10 @@ type ServerCompanyUpdate struct { // Type 115 Colour uint8 // Main company colour. Password bool // Company is password protected. BankruptcyQuarters uint8 // Quarters of Bankruptcy. - // Share1 uint8 // Owner of Share 1. - // Share2 uint8 // Owner of Share 2. - // Share3 uint8 // Owner of Share 3. - // Share4 uint8 // Owner of Share 4. + Share1 uint8 // Owner of Share 1. + Share2 uint8 // Owner of Share 2. + Share3 uint8 // Owner of Share 3. + Share4 uint8 // Owner of Share 4. } func (p ServerCompanyUpdate) String() string { diff --git a/pkg/admin/proto_unmarshal.go b/pkg/admin/proto_unmarshal.go index b75c3f4..2695c8f 100644 --- a/pkg/admin/proto_unmarshal.go +++ b/pkg/admin/proto_unmarshal.go @@ -34,71 +34,88 @@ func ottdUnmarshalData(buffer *bytes.Buffer, val reflect.Value, set bool) (gen i // i.e binary.Read(buffer, binary.LittleEndian, nv) case reflect.Bool: var nv bool - nv = uint8(buffer.Next(1)[0]) != 0 - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + nv = uint8(buffer.Next(1)[0]) != 0 + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Uint8: var nv uint8 - nv = uint8(buffer.Next(1)[0]) - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + nv = uint8(buffer.Next(1)[0]) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Uint16: var nv uint16 - nv = binary.LittleEndian.Uint16(buffer.Next(2)) - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 2 { + nv = binary.LittleEndian.Uint16(buffer.Next(2)) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Uint32: var nv uint32 - nv = binary.LittleEndian.Uint32(buffer.Next(4)) - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 1 { + nv = binary.LittleEndian.Uint32(buffer.Next(4)) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Int64: var nv int64 - binary.Read(buffer, binary.LittleEndian, &nv) - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 64 { + binary.Read(buffer, binary.LittleEndian, &nv) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Uint64: var nv uint64 - nv = binary.LittleEndian.Uint64(buffer.Next(8)) - if set { - val.Set(reflect.ValueOf(nv)) + if buffer.Len() >= 64 { + nv = binary.LittleEndian.Uint64(buffer.Next(8)) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.String: - nvBytes, _ := buffer.ReadBytes(byte(0)) - nv := string(bytes.Trim(nvBytes, "\x00")) - if set { - val.Set(reflect.ValueOf(nv)) + var nv string + if buffer.Len() >= 1 { + nvBytes, _ := buffer.ReadBytes(byte(0)) + nv = string(bytes.Trim(nvBytes, "\x00")) + if set { + val.Set(reflect.ValueOf(nv)) + } } return nv case reflect.Map: // This is handled in a special, openttd way val.Set(reflect.MakeMap(val.Type())) var next bool - next = uint8(buffer.Next(1)[0]) != 0 - for next { - // there are settings to read - // read the key data - k := reflect.Zero(val.Type().Key()) - k = reflect.ValueOf(ottdUnmarshalData(buffer, k, false)) + if buffer.Len() >= 1 { + next = uint8(buffer.Next(1)[0]) != 0 + for next { + // there are settings to read + // read the key data + k := reflect.Zero(val.Type().Key()) + k = reflect.ValueOf(ottdUnmarshalData(buffer, k, false)) - // then read the value data - v := reflect.Zero(val.Type().Elem()) - v = reflect.ValueOf(ottdUnmarshalData(buffer, v, false)) + // then read the value data + v := reflect.Zero(val.Type().Elem()) + v = reflect.ValueOf(ottdUnmarshalData(buffer, v, false)) - val.SetMapIndex(k, v) + val.SetMapIndex(k, v) - next = uint8(buffer.Next(1)[0]) != 0 + next = uint8(buffer.Next(1)[0]) != 0 + } } return val } diff --git a/pkg/admin/state.go b/pkg/admin/state.go index 82627c8..411426e 100644 --- a/pkg/admin/state.go +++ b/pkg/admin/state.go @@ -307,11 +307,13 @@ func (s *State) onCompanyUpdate(se *Session, r *CompanyUpdate) (err error) { com.Passworded = r.Password com.Bankruptcy = r.BankruptcyQuarters - // Shares removed in OpenTTD 14.0 - // com.Share1 = r.Share1 - // com.Share2 = r.Share2 - // com.Share3 = r.Share3 - // com.Share4 = r.Share4 + if se.State.ProtocolVersion <= 2 { + // Company shares were removed in OpenTTD 14.0. + com.Share1 = r.Share1 + com.Share2 = r.Share2 + com.Share3 = r.Share3 + com.Share4 = r.Share4 + } s.Companies[r.ID] = com diff --git a/pkg/admin/structs.go b/pkg/admin/structs.go index f27e047..8013669 100644 --- a/pkg/admin/structs.go +++ b/pkg/admin/structs.go @@ -106,15 +106,15 @@ type Company struct { // The number of quarters this company has been in a bankruptcy state (this may mean they're about to be dissolved!) Bankruptcy uint8 `json:"bankruptcy"` - // Share data - removed in OpenTTD 14 - // // Share 1 is owned by... - // Share1 uint8 `json:"share1"` - // // Share 2 is owned by... - // Share2 uint8 `json:"share2"` - // // Share 3 is owned by... - // Share3 uint8 `json:"share3"` - // // Share 4 is owned by... - // Share4 uint8 `json:"share4"` + // Share data + // Share 1 is owned by... + Share1 uint8 `json:"share1"` + // Share 2 is owned by... + Share2 uint8 `json:"share2"` + // Share 3 is owned by... + Share3 uint8 `json:"share3"` + // Share 4 is owned by... + Share4 uint8 `json:"share4"` // Cargo delivered in the current quarter. CargoThisQuarter uint16 `json:"cargo_current"`