gamedig / rust-gamedig

Game Server Query Library.
https://crates.io/crates/gamedig
MIT License
39 stars 12 forks source link

Unreal2: Add extra server info parsing #145

Open Douile opened 11 months ago

Douile commented 11 months ago

What is this feature about? Some games that use the unreal2 protocol include extra data at the end of the ServerInfo response. The node version detects this based on the rules/mutators response and goes back to parse the extra data.

We could do the same, or specify which game we are querying in GatheringSettings and decide whether to try to gather this data based on that.

Additional context/references Node-gamedig implementation

CosminPerRam commented 11 months ago

As per node-gamedig's implementation: If it's an unreal2 response it will get the following fields: ping If it's extended ("These fields were added in later revisions [...]": flags, skill If the response is from Killing Floor, it has the following fields: ping, flags, skill (these are already present in unreal extended) and wavecurrent, wavetotal.

I think [...] specify which game we are querying in GatheringSettings and decide whether to try to gather this data based on that. could be avoided, with the following solution proposals:

For the simplicity's sake (as I do not think we will have another game that will also report more fields here), we could just add them to the response (extra info) as Option's (ping is always present, so without that one). Advantages:

Disadvantages:

Another solution would be to always parse the ping (and the extended fields) and add a new implementation that parses them correctly afterwards, sort of what JC2M does, but just for this extra info. Advantages:

Disadvantages:

Thoughts about this? Got any other ideas going?

Douile commented 10 months ago

Wasting 2 options worth of memory on every query that isn't killing floor.

2 Option<u32> is not a lot, especially since allocation will likely be rounded.

Another solution would be to always parse the ping (and the extended fields) and add a new implementation that parses them correctly afterwards, sort of what JC2M does, but just for this extra info.

I'm not sure how well this would work because we don't know how many u32 are before the string and the string is indistinguishable from u32s. I think the extended/killingfloor parsing works best given the remaining &[u8] after parsing standard headers.

I do like the idea the idea of the game-specific impl of killing floor though: maybe get_server_info() could return the remaining bytes ((ServerInfo, &[u8]) or (ServerInfo, Buffer)). Then the game-specific impl could handle that.

I'm not sure the extended impl should be in src/games though as it applies to multiple games, maybe Unreal2 could have a standard_query and a extended_query, or pass extended: bool to the query function. If doing this though I kinda feel like maybe killing floor shouldn't be a game specific impl as it is 1/4 of the unreal2 protocol.

The problem with game-specific impls here is that by doing so we restrict our ability to do detection of what kind of packet we get at parse time: the caller must know what kind of packet the server will return so they can decide which impl to call.

Simpler implementation, like the node one.

I do think that specifying what kind of query we want in GatherSettings is simpler than the node-implementation as it means we don't have to implement the server kind detection logic. However it does make our parser more strict than the node one: it would fail for servers that node would work for.


I think a good way to do this would be:

  1. Return the remaining buffer from get_server_info() -> (ServerInfo, Buffer)
  2. Create an enum for extra info response
    enum Unreal2ExtraServerInfo {
    Extended {
    /* ... */
    },
    KillingFloor {
    /* ... */
    },
    None,
    }
  3. After fetching rules do the same detection as node, then attempt to parse the remaining buffer from get_server_info() into this enum accordingly
  4. Add enum to the response:
    pub struct Response {
    pub server_info: ServerInfo,
    pub mutators_and_rules: MutatorsAndRules,
    pub players: Players,
    pub extra_server_info: Unreal2ExtraServerInfo,
    }

Advantages:

Disadvantages:


Got any other ideas going?

I was thinking of doing a trait based impl of this in #148 but that has the same issue of game detection as here, so detection logic would be the same.