Skip to main content

roblox_api/api/games/
v1.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{DateTime, Paging, endpoint};
4
5pub const URL: &str = "https://games.roblox.com/v1";
6
7#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9pub struct PlaceDetails {
10    #[serde(rename = "placeId")]
11    pub id: u64,
12    pub name: String,
13    pub description: String,
14
15    pub source_name: String,
16    pub source_description: String,
17
18    pub universe_id: u64,
19    pub universe_root_place_id: u64,
20
21    pub url: String,
22    pub image_token: String,
23    pub reason_prohibited: String,
24
25    // cast to Creator
26    pub builder: String,
27    pub builder_id: u64,
28
29    pub price: u64,
30
31    pub is_playable: bool,
32    #[serde(rename = "hasVerifiedBadge")]
33    pub is_verified: bool,
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
37pub struct PrivateServerInfoGameRootPlace {
38    pub id: u64,
39    pub name: String,
40}
41
42#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
43#[serde(rename_all = "camelCase")]
44pub struct PrivateServerInfoGame {
45    pub id: u64,
46    pub name: String,
47    pub root_place: PrivateServerInfoGameRootPlace,
48}
49
50#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
51#[serde(rename_all = "camelCase")]
52pub struct PrivateServerInfoSubscription {
53    pub price: u64,
54
55    pub active: bool,
56    pub expired: bool,
57    pub can_renew: bool,
58    pub has_price_changed: bool,
59    pub has_recurring_profile: bool,
60    pub has_insufficient_funds: bool,
61
62    pub expiration_date: DateTime,
63}
64
65#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
66#[serde(rename_all = "camelCase")]
67pub struct PrivateServerInfoPermissions {
68    // TODO: find out what it holds
69    //pub users: Vec<>
70
71    // Assuming u64, might be String
72    pub enemy_clan_id: Option<u64>,
73
74    pub clan_allowed: bool,
75    pub friends_allowed: bool,
76}
77
78#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
79pub struct PrivateServerInfoVoiceSettings {
80    pub enabled: bool,
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
84#[serde(rename_all = "camelCase")]
85pub struct PrivateServerInfo {
86    pub id: u64,
87    pub name: String,
88
89    pub active: bool,
90
91    #[serde(rename = "link")]
92    pub link_url: String,
93    pub join_code: String,
94
95    pub game: PrivateServerInfoGame,
96    pub subscription: PrivateServerInfoSubscription,
97    pub permissions: PrivateServerInfoPermissions,
98    pub voice_settings: PrivateServerInfoVoiceSettings,
99}
100
101#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
102#[serde(rename_all = "camelCase")]
103pub struct PrivateServerOwner {
104    pub id: u64,
105    pub name: String,
106    pub display_name: String,
107    #[serde(rename = "hasVerifiedBadge")]
108    pub is_verified: bool,
109}
110
111#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
112#[serde(rename_all = "camelCase")]
113pub struct PrivateServer {
114    pub name: String,
115    #[serde(rename = "vipServerId")]
116    pub id: u64,
117    pub max_players: u16,
118
119    pub owner: PrivateServerOwner,
120
121    //pub players: Vec<()>, // Seems to be an empty vec?
122    pub player_tokens: Vec<String>,
123}
124
125#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
126pub struct PrivateServersResponse {
127    #[serde(rename = "data")]
128    pub servers: Vec<PrivateServer>,
129    #[serde(rename = "gameJoinRestricted")]
130    pub join_restricted: bool,
131    #[serde(rename = "nextPageCursor")]
132    pub next_cursor: Option<String>,
133    #[serde(rename = "previousPageCursor")]
134    pub previous_cursor: Option<String>,
135}
136
137#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
138#[serde(rename_all = "camelCase")]
139pub struct Server {
140    #[serde(rename = "id")]
141    pub job_id: String,
142
143    pub playing: u16,
144    pub max_players: u16,
145    pub fps: f32,
146    pub ping: Option<u16>,
147
148    //pub players: Vec<()>, // Seems to be an empty vec?
149    pub player_tokens: Vec<String>,
150}
151
152#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
153pub struct ServersResponse {
154    #[serde(rename = "data")]
155    pub servers: Vec<Server>,
156    #[serde(rename = "nextPageCursor")]
157    pub next_cursor: Option<String>,
158    #[serde(rename = "previousPageCursor")]
159    pub previous_cursor: Option<String>,
160}
161
162#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
163pub struct UniverseVotes {
164    pub id: u64,
165    #[serde(rename = "upVotes")]
166    pub likes: u32,
167    #[serde(rename = "downVotes")]
168    pub dislikes: u32,
169}
170
171#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
172#[serde(rename_all = "camelCase")]
173pub struct UniverseGamepass {
174    pub id: u64,
175    pub name: String,
176    pub display_name: String,
177
178    pub price: Option<u64>,
179    pub product_id: Option<u64>,
180
181    #[serde(rename = "isOwned")]
182    pub owned: bool,
183
184    pub seller_id: Option<u64>,
185    pub seller_name: String,
186}
187
188#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
189pub struct UniverseGamepassesResponse {
190    #[serde(rename = "data")]
191    pub gamepasses: Vec<UniverseGamepass>,
192    #[serde(rename = "nextPageCursor")]
193    pub next_cursor: Option<String>,
194    #[serde(rename = "previousPageCursor")]
195    pub previous_cursor: Option<String>,
196}
197
198endpoint! {
199    batch_place_details(ids: &[u64]) -> Vec<PlaceDetails> {
200        GET "{URL}/games/multiget-place-details";
201        prelude {
202            let joined_ids = ids
203                .iter()
204                .map(|x| x.to_string())
205                .collect::<Vec<String>>()
206                .join(",");
207        }
208        query {
209            "placeIds" => &joined_ids,
210        }
211    }
212
213    /// Set server_kind to 0, if you want a valid response
214    servers(id: u64, server_kind: u8, exclude_full_games: bool, paging: Paging<'_>) -> ServersResponse {
215        GET "{URL}/games/{id}/servers/{server_kind}";
216        paging_query { paging, limit = 10 }
217        prelude {
218            let exclude_full_games = exclude_full_games.to_string();
219        }
220        query {
221            "excludeFullGames" => &exclude_full_games,
222        }
223    }
224
225    private_servers(id: u64, exclude_friend_servers: bool, paging: Paging<'_>) -> PrivateServersResponse {
226        GET "{URL}/games/{id}/private-servers";
227        paging_query { paging, limit = 10 }
228        prelude {
229            let exclude_friend_servers = exclude_friend_servers.to_string();
230        }
231        query {
232            "excludeFriendServers" => &exclude_friend_servers,
233        }
234    }
235
236    private_server_info(id: u64) -> PrivateServerInfo {
237        GET "{URL}/vip-servers/{id}";
238    }
239
240    universe_favorite_count(id: u64) -> u64 {
241        GET "{URL}/games/{id}/favorites/count";
242        types {
243            Response {
244                favorites("favoritesCount"): u64,
245            }
246        }
247        map |r: Response| r.favorites
248    }
249
250    universe_votes(ids: &[u64]) -> Vec<UniverseVotes> {
251        GET "{URL}/games/votes";
252        types {
253            Response {
254                votes("data"): Vec<UniverseVotes>,
255            }
256        }
257        prelude {
258            let joined_ids = ids
259                .iter()
260                .map(|x| x.to_string())
261                .collect::<Vec<String>>()
262                .join(",");
263        }
264        query {
265            "universeIds" => &joined_ids,
266        }
267        map |r: Response| r.votes
268    }
269
270    universe_gamepasses(id: u64, paging: Paging<'_>) -> UniverseGamepassesResponse {
271        GET "{URL}/games/{id}/game-passes";
272        paging_query { paging, limit = 10 }
273    }
274}