gamedig / rust-gamedig

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

Add programatic way to access list of supported games #36

Closed Douile closed 1 year ago

Douile commented 1 year ago

Currently the list of supported games is defined in the games module, they must be accessed using games::<game_name>. As far as I know there is no way to programmatically enumerate the items in the module, I cannot access a list of all the games from code. It would be useful if the library exposed a collection (for example a hashmap) containing all the games query functions and some metadata about them.

I imagine this could be used like this:

let list_of_games: Vec<&str> = gamedig::games::games().keys().collect();
// Output a list of the game names
println("{:?}", list_of_games);

if let Some(game) = gamedig::games::games().get("csgo") {
  // Output some metadata about a game
  println("{:?}", game.name);

  // Query a game
  game.query("...", None);
}
Douile commented 1 year ago

Looking into this we would need a way to generalize the return types of the query functions, there are three ways I think this could be done:

I'm leaning more towards traits as it makes the returned data more easily accessible generically: the caller doesn't need to implement a match to handle all the types they want.

In terms of metadata that should be stored about this game (taking inspiration from node-gamedig) I think we could start with:

For creating a hashmap of the supported games, we know this at compile time so it can be static and constant, according to this stack overflow answer phf is a good library for this. However it would up the MSRV to 1.60. Also it doesn't seem to have a way to add line-by line so we would need a big list of all the games and their metadata somewhere. In my opinion it would be nice if this was generated automatically, either using a build script or a proc macro however that might be over-complicating things for an initial implementation.

Here's an initial drafts for what a trait could look like

pub trait GenericServerInfo {
  fn server_name(&self) -> String;
  fn map_name(&self) -> Option<String>;
  fn players_online(&self) -> u64;
  fn players_maximum(&self) -> u64;
  fn players_bots(&self) -> u64;
  fn has_password(&self) -> bool;
  // ...
  // Possibly add every possible variant here but wrap the responses in Optionals so that they can be opted out of
}

If anyone has comments or suggestions please let me know, I'm not sure the best way to do this but would like to implement it in a way that is actually useful.

EDIT: It might also be possible to use serde to mangle the return type into an already re-usable datatype e.g. hashmap.