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 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 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 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 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 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}