Closed 715kg closed 2 years ago
CS 1.6 runs on the Gold Src Engine, no? In the readme, it says only Source Engine is supported.
C's 1.6 servers run on the hl1 engine. requests for bytes and headers are the same as for the source engine. the answer may be slightly different. So why not make a universal library for all types of games. ?)
here it works for both cs 1.6 and cs source. also cs go, hlaf life 1 .... and so on. all on the Source engine. Even those cs 1.6 servers that do not work on a2s work.
but I don't have enough information that the hldsinfo package outputs.
I'm not strong enough in programming to work with bytes, it's a little difficult for me to understand what and how the packet receives. tell me how to make it work not only for the source engine
The documentation says 2 engines. and there is a list of packages at the bottom of the page. so why not make a working package, according to the full description of the documentation. so that it works as described. I and other users will be grateful if you update your a2s package
When I change
data, immediate, err := c.getChallenge(builder.Bytes(), A2S_INFO_RESPONSE)
On
data, immediate, err := c.getChallenge(builder.Bytes(), 0x6D)
Which corresponds to the answer (0x6D) GoldSource
And I also change the check
if header != A2S_INFO_RESPONSE {
return nil, ErrUnsupportedHeader
}
on
if header != 0x6D{
return nil, ErrUnsupportedHeader
}
I get a response from GoldSource. But the data in the structure is not output correctly, since the GoldSource response is slightly different. I do not know how to fix it((
Quote from the documentation https://developer .valvesoftware.com/wiki/Server_queries (GoldSource servers reply using the same response as Source servers. Older and pre-Steam servers still reply with the response format below. GoldSource can be distinguished from Source via AppIDs.)
Please add the verification conditions on GoldSource, with the output of correct information in the existing structure. I will be grateful to you. I could implement the check myself, but I don't know much about bytes and how they work
I made changes, now it works more or less correctly. As an experienced programmer, I ask you to correct what I wrote, because I am not completely sure that it will work correctly with many servers.
Added
A2S_INFO_RESPONSE_GS = 0x6D // GoldSource
Check
header := reader.ReadUint8()
if header != A2S_INFO_RESPONSE {
if header != A2S_INFO_RESPONSE_GS {
return nil, ErrUnsupportedHeader
}
}
Made a condition
// Source & Up
if header == A2S_INFO_RESPONSE {
info.Protocol = reader.ReadUint8()
info.Name = reader.ReadString()
info.Map = reader.ReadString()
info.Folder = reader.ReadString()
info.Game = reader.ReadString()
info.ID = reader.ReadUint16()
info.Players = reader.ReadUint8()
info.MaxPlayers = reader.ReadUint8()
info.Bots = reader.ReadUint8()
// Rag Doll Kung Fu servers always return 0 for "Server type."
info.ServerType = ParseServerType(reader.ReadUint8())
info.ServerOS = ParseServerOS(reader.ReadUint8())
info.Visibility = reader.ReadUint8() == 1
info.VAC = reader.ReadUint8() == 1
if AppID(info.ID) == App_TheShip {
info.TheShip = &TheShipInfo{}
info.TheShip.Mode = ParseTheShipMode(reader.ReadUint8())
info.TheShip.Witnesses = reader.ReadUint8()
info.TheShip.Duration = reader.ReadUint8()
}
info.Version = reader.ReadString()
// Start of EDF
if !reader.More() {
return info, nil
}
info.ExtendedServerInfo = &ExtendedServerInfo{}
info.EDF = reader.ReadUint8()
if (info.EDF & 0x80) != 0 {
info.ExtendedServerInfo.Port = reader.ReadUint16()
}
if (info.EDF & 0x10) != 0 {
info.ExtendedServerInfo.SteamID = reader.ReadUint64()
}
if (info.EDF & 0x40) != 0 {
info.SourceTV = &SourceTVInfo{}
info.SourceTV.Port = reader.ReadUint16()
info.SourceTV.Name = reader.ReadString()
}
if (info.EDF & 0x20) != 0 {
info.ExtendedServerInfo.Keywords = reader.ReadString()
}
if (info.EDF & 0x01) != 0 {
info.ExtendedServerInfo.GameID = reader.ReadUint64()
}
}
// GoldSource
if header == A2S_INFO_RESPONSE_GS {
info.Adresss = reader.ReadString()
info.Name = reader.ReadString()
info.Map = reader.ReadString()
info.Folder = reader.ReadString()
info.Game = reader.ReadString()
info.Players = reader.ReadUint8()
info.MaxPlayers = reader.ReadUint8()
info.Protocol = reader.ReadUint8()
// Rag Doll Kung Fu servers always return 0 for "Server type."
info.ServerType = ParseServerType(reader.ReadUint8())
info.ServerOS = ParseServerOS(reader.ReadUint8())
info.Visibility = reader.ReadUint8() == 1
info.Mod = reader.ReadUint8()
if int8(info.Mod) == 1 {
info.Mods = &InfoMod{}
info.Mods.Link = reader.ReadString()
info.Mods.DownLink = reader.ReadString()
info.Mods.NULL = reader.ReadUint8()
if info.Mods.NULL != 0 {
info.Mods.Version = reader.ReadUint64()
info.Mods.Size = reader.ReadUint64()
}
info.Mods.Type = reader.ReadUint8()
info.Mods.DLL = reader.ReadUint8()
}
info.VAC = reader.ReadUint8() == 1
info.Bots = reader.ReadUint8()
}
Added structure
type InfoMod struct {
Link string `json:"Mode"`
DownLink string `json:"DownLink"`
NULL uint8 `json:"NULL"`
Version uint64 `json:"Version"`
Size uint64 `json:"Size"`
Type uint8 `json:"Type"`
DLL uint8 `json:"DLL"`
}
Added variables to the ServerInfo structure
// Adresss server.
Adresss string `json:"Adresss"`
// Mod of game.
Mod uint8 `json:"Mod"`
// Mod info
Mods *InfoMod `json:"Mods,omitempty"`
The final result info.go
package a2s
import (
"errors"
"fmt"
)
const (
A2S_INFO_REQUEST = 0x54
A2S_INFO_RESPONSE = 0x49 // Source & up
A2S_INFO_RESPONSE_GS = 0x6D // GoldSource
)
var (
ErrBadPacketHeader = errors.New("Packet header mismatch")
ErrUnsupportedHeader = errors.New("Unsupported protocol header")
)
type ServerInfo struct {
// Adresss server.
Adresss string `json:"Adresss"`
// Protocol version used by the server.
Protocol uint8 `json:"Protocol"`
// Name of the server.
Name string `json:"Name"`
// Map the server has currently loaded.
Map string `json:"Map"`
// Name of the folder containing the game files.
Folder string `json:"Folder"`
// Full name of the game.
Game string `json:"Game"`
// Steam Application ID of game.
ID uint16 `json:"AppID"`
// Mod of game.
Mod uint8 `json:"Mod"`
// Mod info
Mods *InfoMod `json:"Mods,omitempty"`
// Number of players on the server.
Players uint8 `json:"Players"`
// Maximum number of players the server reports it can hold.
MaxPlayers uint8 `json:"MaxPlayers"`
// Number of bots on the server.
Bots uint8 `json:"Bots"`
// Indicates the type of server
// Rag Doll Kung Fu servers always return 0 for "Server type."
ServerType ServerType `json:"ServerType"`
// Indicates the operating system of the server
ServerOS ServerOS `json:"ServerOS"`
// Indicates whether the server requires a password
Visibility bool `json:"Visibility"`
// Specifies whether the server uses VAC
VAC bool `json:"VAC"`
// These fields only exist in a response if the server is running The Ship
TheShip *TheShipInfo `json:"TheShip,omitempty"`
// Version of the game installed on the server.
Version string `json:"Version"`
// If present, this specifies which additional data fields will be included.
EDF uint8 `json:"EDF,omitempty"`
ExtendedServerInfo *ExtendedServerInfo `json:"ExtendedServerInfo,omitempty"`
SourceTV *SourceTVInfo `json:"SourceTV,omitempty"`
}
type TheShipInfo struct {
Mode TheShipMode `json:"Mode"`
Witnesses uint8 `json:"Witnesses"`
Duration uint8 `json:"Duration"`
}
type InfoMod struct {
Link string `json:"Mode"`
DownLink string `json:"DownLink"`
NULL uint8 `json:"NULL"`
Version uint64 `json:"Version"`
Size uint64 `json:"Size"`
Type uint8 `json:"Type"`
DLL uint8 `json:"DLL"`
}
type ExtendedServerInfo struct {
// The server's game port number.
Port uint16 `json:"Port"`
// Server's SteamID.
SteamID uint64 `json:"SteamID"`
// Tags that describe the game according to the server (for future use.)
Keywords string `json:"Keywords"`
// The server's 64-bit GameID. If this is present, a more accurate AppID is present in the low 24 bits. The earlier AppID could have been truncated as it was forced into 16-bit storage.
GameID uint64 `json:"GameID"`
}
type SourceTVInfo struct {
// Spectator port number for SourceTV.
Port uint16 `json:"Port"`
// Name of the spectator server for SourceTV.
Name string `json:"Name"`
}
func (c *Client) QueryInfo() (*ServerInfo, error) {
var builder PacketBuilder
/*
(FF FF FF FF) 54 53 6F 75 72 63 65 20 45 6E 67 69 ÿÿÿÿTSource Engi
6E 65 20 51 75 65 72 79 00 ne Query.
*/
builder.WriteBytes([]byte{
0xFF, 0xFF, 0xFF, 0xFF, A2S_INFO_REQUEST,
})
builder.WriteCString("Source Engine Query")
data, immediate, err := c.getChallenge(builder.Bytes(), 0x6D)
if err != nil {
fmt.Println("Ошибка тут 1", err)
return nil, err
}
if !immediate {
builder.WriteBytes(data)
if err := c.send(builder.Bytes()); err != nil {
fmt.Println("Ошибка тут 2")
return nil, err
}
data, err = c.receive()
if err != nil {
fmt.Println("Ошибка тут 3")
return nil, err
}
}
/*
Header long Always equal to -1 (0xFFFFFFFF). Means it isn't split.
Payload
*/
reader := NewPacketReader(data)
if reader.ReadInt32() != -1 {
return nil, ErrBadPacketHeader
}
info := &ServerInfo{}
header := reader.ReadUint8()
if header != A2S_INFO_RESPONSE {
if header != A2S_INFO_RESPONSE_GS {
return nil, ErrUnsupportedHeader
}
}
// Source & Up
if header == A2S_INFO_RESPONSE {
info.Protocol = reader.ReadUint8()
info.Name = reader.ReadString()
info.Map = reader.ReadString()
info.Folder = reader.ReadString()
info.Game = reader.ReadString()
info.ID = reader.ReadUint16()
info.Players = reader.ReadUint8()
info.MaxPlayers = reader.ReadUint8()
info.Bots = reader.ReadUint8()
// Rag Doll Kung Fu servers always return 0 for "Server type."
info.ServerType = ParseServerType(reader.ReadUint8())
info.ServerOS = ParseServerOS(reader.ReadUint8())
info.Visibility = reader.ReadUint8() == 1
info.VAC = reader.ReadUint8() == 1
if AppID(info.ID) == App_TheShip {
info.TheShip = &TheShipInfo{}
info.TheShip.Mode = ParseTheShipMode(reader.ReadUint8())
info.TheShip.Witnesses = reader.ReadUint8()
info.TheShip.Duration = reader.ReadUint8()
}
info.Version = reader.ReadString()
// Start of EDF
if !reader.More() {
return info, nil
}
info.ExtendedServerInfo = &ExtendedServerInfo{}
info.EDF = reader.ReadUint8()
if (info.EDF & 0x80) != 0 {
info.ExtendedServerInfo.Port = reader.ReadUint16()
}
if (info.EDF & 0x10) != 0 {
info.ExtendedServerInfo.SteamID = reader.ReadUint64()
}
if (info.EDF & 0x40) != 0 {
info.SourceTV = &SourceTVInfo{}
info.SourceTV.Port = reader.ReadUint16()
info.SourceTV.Name = reader.ReadString()
}
if (info.EDF & 0x20) != 0 {
info.ExtendedServerInfo.Keywords = reader.ReadString()
}
if (info.EDF & 0x01) != 0 {
info.ExtendedServerInfo.GameID = reader.ReadUint64()
}
}
// GoldSource
if header == A2S_INFO_RESPONSE_GS {
info.Adresss = reader.ReadString()
info.Name = reader.ReadString()
info.Map = reader.ReadString()
info.Folder = reader.ReadString()
info.Game = reader.ReadString()
info.Players = reader.ReadUint8()
info.MaxPlayers = reader.ReadUint8()
info.Protocol = reader.ReadUint8()
// Rag Doll Kung Fu servers always return 0 for "Server type."
info.ServerType = ParseServerType(reader.ReadUint8())
info.ServerOS = ParseServerOS(reader.ReadUint8())
info.Visibility = reader.ReadUint8() == 1
info.Mod = reader.ReadUint8()
if int8(info.Mod) == 1 {
info.Mods = &InfoMod{}
info.Mods.Link = reader.ReadString()
info.Mods.DownLink = reader.ReadString()
info.Mods.NULL = reader.ReadUint8()
if info.Mods.NULL != 0 {
info.Mods.Version = reader.ReadUint64()
info.Mods.Size = reader.ReadUint64()
}
info.Mods.Type = reader.ReadUint8()
info.Mods.DLL = reader.ReadUint8()
}
info.VAC = reader.ReadUint8() == 1
info.Bots = reader.ReadUint8()
}
return info, nil
}
But here's what to do with A2S_PLAYER and A2S_RULES I don't know. They don't work on GoldSource. Although they should.
This library could support Gold Src, I made it initially to suit my needs, PR is welcome.
Hello. I have a problem when I enter the IP address from cs 1.6 servers, the error ErrBadChallengeResponse = errors.New("Bad challenge response") is triggered
90% of cs 1.6 servers receive this error in response