M api/src/actions/game/all_forming.rs => api/src/actions/game/all_forming.rs +7 -2
@@ 13,7 13,7 @@ use laurelin_shared::{
types::game::{Game, GAMESTATE_FORMING},
};
-pub(crate) fn all_forming(conn: &mut PgConnection) -> Result<Vec<Game>, APIError> {
+pub(crate) fn all_forming(conn: &mut PgConnection) -> Result<Vec<String>, APIError> {
let games_res = games::table
.filter(games::state.eq(GAMESTATE_FORMING))
.load::<Game>(conn);
@@ 23,5 23,10 @@ pub(crate) fn all_forming(conn: &mut PgConnection) -> Result<Vec<Game>, APIError
Ok(games) => games,
};
- Ok(games)
+ let mut ids = Vec::with_capacity(games.len());
+ for game in &games {
+ ids.push(game.id.to_string());
+ }
+
+ Ok(ids)
}
A api/src/actions/game/info.rs => api/src/actions/game/info.rs +26 -0
@@ 0,0 1,26 @@
+/*
+ * This file is part of laurelin/api
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
+ *
+ * Licensed under GPL-3.0-only.
+ * See LICENSE for licensing information.
+ */
+
+use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
+use laurelin_schema::schema::games;
+use laurelin_shared::{error::api::APIError, types::game::Game};
+use uuid::Uuid;
+
+pub(crate) fn info(conn: &mut PgConnection, game_id: &str) -> Result<Game, APIError> {
+ let game = match games::table
+ .filter(games::id.eq(&Uuid::try_parse(game_id).unwrap())) // TODO: handle
+ .first::<Game>(conn)
+ {
+ Err(_) => {
+ return Err(APIError::UserNotFound);
+ }
+ Ok(game) => game,
+ };
+
+ Ok(game)
+}
M api/src/actions/game/mod.rs => api/src/actions/game/mod.rs +3 -0
@@ 17,3 17,6 @@ pub(crate) use my_games::my_games;
mod patch;
pub(crate) use patch::patch;
+
+mod info;
+pub(crate) use info::info;
M api/src/actions/game/my_games.rs => api/src/actions/game/my_games.rs +7 -2
@@ 11,7 11,7 @@ use laurelin_schema::schema::games;
use laurelin_shared::{error::api::APIError, types::game::Game};
use uuid::Uuid;
-pub(crate) fn my_games(conn: &mut PgConnection, user_id: &str) -> Result<Vec<Game>, APIError> {
+pub(crate) fn my_games(conn: &mut PgConnection, user_id: &str) -> Result<Vec<String>, APIError> {
let games_res = games::table
.filter(
games::host_id
@@ 25,5 25,10 @@ pub(crate) fn my_games(conn: &mut PgConnection, user_id: &str) -> Result<Vec<Gam
Ok(games) => games,
};
- Ok(games)
+ let mut ids = Vec::with_capacity(games.len());
+ for game in &games {
+ ids.push(game.id.to_string());
+ }
+
+ Ok(ids)
}
M api/src/handlers/game/mod.rs => api/src/handlers/game/mod.rs +26 -0
@@ 90,6 90,32 @@ async fn my_games(session: Session, pool: web::Data<PgPool>) -> impl Responder {
}
}
+#[get("/api/game/{id}")]
+async fn info(session: Session, pool: web::Data<PgPool>, id: web::Path<String>) -> impl Responder {
+ let session_validation = session::validate_session(&session);
+
+ match session_validation {
+ Err(err) => err,
+ Ok(_user_id) => {
+ let game = web::block(move || {
+ let mut conn = match pool.get() {
+ Err(_) => return Err(APIError::DatabasePoolGetFailed),
+ Ok(conn) => conn,
+ };
+ actions::game::info(&mut conn, &id)
+ })
+ .await;
+ match game {
+ Err(_err) => HttpResponse::InternalServerError().json(APIError::Undefined),
+ Ok(game_res) => match game_res {
+ Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
+ Ok(game) => HttpResponse::Ok().json(game),
+ },
+ }
+ }
+ }
+}
+
#[patch("/api/game/{id}")]
async fn patch(
pool: web::Data<PgPool>,
M api/src/main.rs => api/src/main.rs +1 -0
@@ 103,6 103,7 @@ async fn main() -> std::io::Result<()> {
.service(handlers::game::create)
.service(handlers::game::all_forming)
.service(handlers::game::my_games)
+ .service(handlers::game::info)
.service(handlers::game::patch)
})
.bind(("0.0.0.0", 8080))?
M client/src/main.rs => client/src/main.rs +9 -1
@@ 11,6 11,7 @@
use bevy::{
app::AppExit,
prelude::*,
+ utils::HashMap,
window::{
CompositeAlphaMode, Cursor, PresentMode, WindowLevel, WindowMode, WindowResizeConstraints,
WindowResolution,
@@ 21,7 22,10 @@ use naia_bevy_client::{
Client, ClientConfig as NaiaClientConfig, Plugin as NaiaClientPlugin, ReceiveEvents,
};
-use laurelin_shared::{server::protocol::protocol, types::user::UserPub};
+use laurelin_shared::{
+ server::protocol::protocol,
+ types::{game::GamePub, user::UserPub},
+};
mod cfg;
mod constants;
@@ 51,6 55,8 @@ pub struct Global {
pub users_cache: Vec<UserPub>,
/// stores ids of users currently in queue to be gotten
pub users_cache_queue: Vec<String>,
+ pub games_cache: HashMap<String, GamePub>,
+ pub games_cache_queue: Vec<String>,
}
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@@ 105,6 111,8 @@ fn main() {
app.insert_resource(Global {
users_cache: vec![],
users_cache_queue: vec![],
+ games_cache: HashMap::new(),
+ games_cache_queue: vec![],
})
.insert_resource(cfg::CfgDirs(
directories::ProjectDirs::from("com", "liljamo", "deckbuilder")
M => +47 -17
@@ 9,7 9,7 @@
use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};
use laurelin_shared::types::{
game::{GAMESTATE_FINISHED, GAMESTATE_INPROGRESS},
game::{GamePub, GAMESTATE_FINISHED, GAMESTATE_FORMING, GAMESTATE_INPROGRESS},
user::UserPub,
};
@@ 110,10 110,10 @@ pub fn ui(
browse_forming(ui, &mut data, &cfg_user, &global);
}
PlayScreenBrowseState::InProgress => {
browse_inprogress(ui, &mut data, &global);
browse_inprogress(ui, &mut data, &global, &cfg_user);
}
PlayScreenBrowseState::Finished => {
browse_finished(ui, &mut data, &global);
browse_finished(ui, &mut data, &global, &cfg_user);
}
});
});
@@ 169,10 169,16 @@ fn browse_forming(
return;
}
if data.all_forming.is_empty() {
let all_forming: Vec<&GamePub> = global
.games_cache
.values()
.filter(|g| g.state == GAMESTATE_FORMING)
.collect();
if all_forming.is_empty() {
ui.label("No forming games found.");
} else {
for game in data.all_forming.clone() {
for game in all_forming {
egui::Frame::none()
.fill(egui::Color32::BLACK)
.rounding(4.)
@@ 220,7 226,12 @@ fn browse_forming(
}
}
fn browse_inprogress(ui: &mut egui::Ui, data: &mut PlayScreenData, global: &Global) {
fn browse_inprogress(
ui: &mut egui::Ui,
data: &mut PlayScreenData,
global: &Global,
cfg_user: &CfgUser,
) {
if data.waiting_for_my_games {
ui.horizontal(|ui| {
ui.spinner();
@@ 229,13 240,20 @@ fn browse_inprogress(ui: &mut egui::Ui, data: &mut PlayScreenData, global: &Glob
return;
}
if data.my_games.is_empty() {
let my_games_inprogress: Vec<&GamePub> = global
.games_cache
.values()
.filter(|g| {
g.state == GAMESTATE_INPROGRESS
&& (g.host_id == cfg_user.id
|| g.guest_id.clone().unwrap_or("".to_string()) == cfg_user.id)
})
.collect();
if my_games_inprogress.is_empty() {
ui.label("No games found.");
} else {
let mut games = data.my_games.clone();
games.retain(|g| g.state == GAMESTATE_INPROGRESS);
for game in games {
for game in my_games_inprogress {
egui::Frame::none()
.fill(egui::Color32::BLACK)
.rounding(4.)
@@ 271,7 289,12 @@ fn browse_inprogress(ui: &mut egui::Ui, data: &mut PlayScreenData, global: &Glob
}
}
fn browse_finished(ui: &mut egui::Ui, data: &mut PlayScreenData, global: &Global) {
fn browse_finished(
ui: &mut egui::Ui,
data: &mut PlayScreenData,
global: &Global,
cfg_user: &CfgUser,
) {
if data.waiting_for_my_games {
ui.horizontal(|ui| {
ui.spinner();
@@ 280,13 303,20 @@ fn browse_finished(ui: &mut egui::Ui, data: &mut PlayScreenData, global: &Global
return;
}
if data.my_games.is_empty() {
let my_games_finished: Vec<&GamePub> = global
.games_cache
.values()
.filter(|g| {
g.state == GAMESTATE_FINISHED
&& (g.host_id == cfg_user.id
|| g.guest_id.clone().unwrap_or("".to_string()) == cfg_user.id)
})
.collect();
if my_games_finished.is_empty() {
ui.label("No games found.");
} else {
let mut games = data.my_games.clone();
games.retain(|g| g.state == GAMESTATE_FINISHED);
for game in games {
for game in my_games_finished {
egui::Frame::none()
.fill(egui::Color32::BLACK)
.rounding(4.)
M client/src/plugins/networking/mod.rs => client/src/plugins/networking/mod.rs +2 -0
@@ 19,11 19,13 @@ impl Plugin for NetworkingPlugin {
app.add_event::<send::game::GameCreateEvent>()
.add_event::<send::game::GameAllFormingEvent>()
.add_event::<send::game::GameMyGamesEvent>()
+ .add_event::<send::game::GameInfoEvent>()
.add_event::<send::user::PubUserDetailsEvent>()
.add_systems((
send::game::create_event,
send::game::all_forming_event,
send::game::my_games_event,
+ send::game::game_info_event,
send::user::pub_user_details_event,
))
.add_systems(
M client/src/plugins/networking/systems/events/receive/mod.rs => client/src/plugins/networking/systems/events/receive/mod.rs +42 -24
@@ 7,9 7,12 @@
*/
use bevy::prelude::*;
-use laurelin_shared::server::{
- channels::{AfterAuthChannel, CookieRefreshChannel, DataRequestChannel},
- messages::{AfterAuth, CookieRefresh, DataRequestResponse, DataRequestType},
+use laurelin_shared::{
+ server::{
+ channels::{AfterAuthChannel, CookieRefreshChannel, DataRequestChannel},
+ messages::{AfterAuth, CookieRefresh, DataRequestResponse, DataRequestType},
+ },
+ types::{game::GamePub, user::UserPub},
};
use naia_bevy_client::{
events::{ClientTickEvent, ConnectEvent, DisconnectEvent, MessageEvents, RejectEvent},
@@ 28,7 31,7 @@ use crate::{
Global,
};
-use super::send::user::PubUserDetailsEvent;
+use super::send::{game::GameInfoEvent, user::PubUserDetailsEvent};
pub fn connect_events(mut ev: EventReader<ConnectEvent>, client: Client) {
for _ in ev.iter() {
@@ 61,6 64,7 @@ pub fn message_events(
mut commands: Commands,
mut ev: EventReader<MessageEvents>,
mut pud_ev_w: EventWriter<PubUserDetailsEvent>,
+ mut gi_ev_w: EventWriter<GameInfoEvent>,
mut global: ResMut<Global>,
mut cfg_user: ResMut<CfgUser>,
mut connect_data: ResMut<ConnectScreenData>,
@@ 89,45 93,59 @@ pub fn message_events(
for response in events.read::<DataRequestChannel, DataRequestResponse>() {
match DataRequestType::from_u8(&response.r#type) {
DataRequestType::GameCreate => {
- // TODO: handle possible error (unwrap,
+ // TODO: handle possible errors (unwrap,
// and if the response data is an error)
- play_data.cur_game = response.games.unwrap().get(0).cloned();
+ let game =
+ serde_json::from_str(response.data.unwrap().get(0).unwrap()).unwrap();
+ play_data.cur_game = Some(game);
play_data.state = PlayScreenState::InLobbyHost;
}
DataRequestType::GameAllForming => {
- // TODO: handle possible error
- let all_forming = response.games.unwrap();
+ // TODO: handle
+ let all_forming_ids = response.data.unwrap();
- for game in &all_forming {
- pud_ev_w.send(PubUserDetailsEvent {
- id: game.host_id.clone(),
+ for game_id in &all_forming_ids {
+ gi_ev_w.send(GameInfoEvent {
+ id: game_id.clone(),
});
}
- play_data.all_forming = all_forming;
play_data.waiting_for_all_forming = false;
}
DataRequestType::GameMyGames => {
- // TODO: handle possible error
- let my_games = response.games.unwrap();
+ // TODO: handle
+ let my_games_ids = response.data.unwrap();
- for game in &my_games {
- pud_ev_w.send(PubUserDetailsEvent {
- id: game.host_id.clone(),
+ for game_id in &my_games_ids {
+ gi_ev_w.send(GameInfoEvent {
+ id: game_id.clone(),
});
- if let Some(guest_id) = &game.guest_id {
- pud_ev_w.send(PubUserDetailsEvent {
- id: guest_id.clone(),
- });
- }
}
- play_data.my_games = my_games;
play_data.waiting_for_my_games = false;
}
+ DataRequestType::GameInfo => {
+ // TODO: handle
+ let game: GamePub =
+ serde_json::from_str(response.data.unwrap().get(0).unwrap()).unwrap();
+
+ // cache players of the game
+ pud_ev_w.send(PubUserDetailsEvent {
+ id: game.host_id.clone(),
+ });
+ if let Some(guest_id) = &game.guest_id {
+ pud_ev_w.send(PubUserDetailsEvent {
+ id: guest_id.clone(),
+ });
+ }
+
+ global.games_cache_queue.retain(|id| id != &game.id);
+ global.games_cache.insert(game.id.clone(), game);
+ }
DataRequestType::PubUserDetails => {
// TODO: handle possible error
- let user = response.users.unwrap().get(0).unwrap().clone();
+ let user: UserPub =
+ serde_json::from_str(response.data.unwrap().get(0).unwrap()).unwrap();
global.users_cache_queue.retain(|id| id != &user.id);
global.users_cache.push(user);
}
M client/src/plugins/networking/systems/events/send/game.rs => client/src/plugins/networking/systems/events/send/game.rs +40 -1
@@ 6,13 6,15 @@
* See LICENSE for licensing information.
*/
-use bevy::prelude::EventReader;
+use bevy::prelude::{EventReader, ResMut};
use laurelin_shared::server::{
channels::DataRequestChannel,
messages::{DataRequest, DataRequestType},
};
use naia_bevy_client::Client;
+use crate::Global;
+
use super::data_request;
pub struct GameCreateEvent;
@@ 38,3 40,40 @@ data_request!(
DataRequestType::GameMyGames,
|ev| None
);
+
+pub struct GameInfoEvent {
+ pub id: String,
+}
+
+pub fn game_info_event(
+ mut ev: EventReader<GameInfoEvent>,
+ mut client: Client,
+ mut global: ResMut<Global>,
+) {
+ for ev in ev.iter() {
+ // check if already in cache OR in cache queue
+ if global.games_cache.contains_key(&ev.id) {
+ return;
+ }
+
+ if !global
+ .games_cache_queue
+ .iter()
+ .filter(|&id| id == &ev.id)
+ .cloned()
+ .collect::<Vec<String>>()
+ .is_empty()
+ {
+ return;
+ }
+
+ // add to cache queue
+ global.games_cache_queue.push(ev.id.clone());
+
+ // send request
+ client.send_message::<DataRequestChannel, DataRequest>(&DataRequest::new(
+ DataRequestType::GameInfo as u8,
+ Some(ev.id.clone()),
+ ));
+ }
+}
M server/src/systems/event/message/mod.rs => server/src/systems/event/message/mod.rs +49 -21
@@ 6,16 6,16 @@
* See LICENSE for licensing information.
*/
-use std::vec;
-
use bevy_ecs::{
event::EventReader,
system::{Res, ResMut},
};
use laurelin_shared::{
api::{
+ self,
game::{
- all_forming, create, my_games, ResponseAllForming, ResponseCreateGame, ResponseMyGames,
+ all_forming, create, my_games, ResponseAllForming, ResponseCreateGame,
+ ResponseGameInfo, ResponseMyGames,
},
user::{self, ResponseInfo},
},
@@ 46,13 46,15 @@ pub(crate) fn message_events(
ResponseCreateGame::Error(_err) => None, // TODO: handle
ResponseCreateGame::Valid(result) => Some(result),
};
- let mut games_pub = vec![];
- if let Some(game) = game {
- games_pub.push(GamePub::from_game(&game));
- }
+ let game_pub_json = match game {
+ None => {
+ panic!("I can't be bothered to handle this right now.")
+ }
+ Some(game) => serde_json::to_string(&GamePub::from_game(&game)).unwrap(),
+ };
server.send_message::<DataRequestChannel, DataRequestResponse>(
&user_key,
- &DataRequestResponse::new(request.r#type, None, Some(games_pub)),
+ &DataRequestResponse::new(request.r#type, Some(vec![game_pub_json])),
);
// update cookie
@@ 68,17 70,13 @@ pub(crate) fn message_events(
// TODO: handle
let cookie = global.user_to_session_map.get(&user_key).unwrap();
let wrapped = all_forming(&config.api_address, cookie);
- let games = match wrapped.response {
+ let game_ids = match wrapped.response {
ResponseAllForming::Error(_err) => vec![], // TODO: handle
ResponseAllForming::Valid(result) => result,
};
- let mut games_pub = vec![];
- for game in games {
- games_pub.push(GamePub::from_game(&game));
- }
server.send_message::<DataRequestChannel, DataRequestResponse>(
&user_key,
- &DataRequestResponse::new(request.r#type, None, Some(games_pub)),
+ &DataRequestResponse::new(request.r#type, Some(game_ids)),
);
// update cookie
@@ 94,17 92,45 @@ pub(crate) fn message_events(
// TODO: handle
let cookie = global.user_to_session_map.get(&user_key).unwrap();
let wrapped = my_games(&config.api_address, cookie);
- let games = match wrapped.response {
+ let game_ids = match wrapped.response {
ResponseMyGames::Error(_err) => vec![], // TODO: handle
ResponseMyGames::Valid(result) => result,
};
- let mut games_pub = vec![];
- for game in games {
- games_pub.push(GamePub::from_game(&game));
- }
server.send_message::<DataRequestChannel, DataRequestResponse>(
&user_key,
- &DataRequestResponse::new(request.r#type, None, Some(games_pub)),
+ &DataRequestResponse::new(request.r#type, Some(game_ids)),
+ );
+
+ // update cookie
+ global
+ .user_to_session_map
+ .insert(user_key, wrapped.cookie.clone());
+ server.send_message::<CookieRefreshChannel, CookieRefresh>(
+ &user_key,
+ &CookieRefresh::new(&wrapped.cookie),
+ );
+ }
+ DataRequestType::GameInfo => {
+ // TODO: handle
+ let cookie = global.user_to_session_map.get(&user_key).unwrap();
+ let wrapped = api::game::info(
+ &config.api_address,
+ cookie,
+ &request.data.unwrap_or("".to_string()),
+ );
+ let game = match wrapped.response {
+ ResponseGameInfo::Error(_err) => None, // TODO: handle
+ ResponseGameInfo::Valid(result) => Some(result),
+ };
+ let game_pub_json = match game {
+ None => {
+ panic!("I can't be bothered to handle this right now.")
+ }
+ Some(game) => serde_json::to_string(&GamePub::from_game(&game)).unwrap(),
+ };
+ server.send_message::<DataRequestChannel, DataRequestResponse>(
+ &user_key,
+ &DataRequestResponse::new(request.r#type, Some(vec![game_pub_json])),
);
// update cookie
@@ 130,9 156,11 @@ pub(crate) fn message_events(
}
ResponseInfo::Ok(result) => result,
};
+ // TODO: handle
+ let user_details_json = serde_json::to_string(&user_details).unwrap();
server.send_message::<DataRequestChannel, DataRequestResponse>(
&user_key,
- &DataRequestResponse::new(request.r#type, Some(vec![user_details]), None),
+ &DataRequestResponse::new(request.r#type, Some(vec![user_details_json])),
);
// update cookie
M shared/src/api/game/all_forming.rs => shared/src/api/game/all_forming.rs +2 -2
@@ 9,9 9,9 @@
use reqwest::{self, header::COOKIE};
use serde::{Deserialize, Serialize};
-use crate::{api::macros::extract_cookie, error::api::APIError, types::game::Game};
+use crate::{api::macros::extract_cookie, error::api::APIError};
-pub type ResultAllForming = Vec<Game>;
+pub type ResultAllForming = Vec<String>;
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
M shared/src/api/game/info.rs => shared/src/api/game/info.rs +19 -10
@@ 6,26 6,35 @@
* See LICENSE for licensing information.
*/
-use reqwest;
+use reqwest::{self, header::COOKIE};
use serde::{Deserialize, Serialize};
-use super::{types, APIErrorWrapper};
+use crate::{api::macros::extract_cookie, error::api::APIError, types::game::Game};
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Serialize, Deserialize)]
#[serde(untagged)]
-pub enum ResponseInfo {
- Error(APIErrorWrapper),
- Valid(types::Game),
+pub enum ResponseGameInfo {
+ Error(APIError),
+ Valid(Game),
}
-pub fn info(api_address: String, token: String, game_id: String) -> ResponseInfo {
+#[derive(Deserialize)]
+pub struct ResponseGameInfoWrapper {
+ pub response: ResponseGameInfo,
+ pub cookie: String,
+}
+
+pub fn info(api_address: &String, cookie: &String, game_id: &String) -> ResponseGameInfoWrapper {
let client = reqwest::blocking::Client::new();
let resp = client
- .get(&format!("{}/game/{}", api_address, game_id))
- .header("Authorization", token)
+ .get(format!("{}/game/{}", api_address, game_id))
+ .header(COOKIE, &format!("id={}", cookie))
.send()
.unwrap();
- resp.json().unwrap()
+ ResponseGameInfoWrapper {
+ cookie: extract_cookie!(resp),
+ response: resp.json().unwrap(),
+ }
}
M shared/src/api/game/mod.rs => shared/src/api/game/mod.rs +2 -2
@@ 12,8 12,8 @@ pub use create::*;
mod all_forming;
pub use all_forming::*;
-//mod info;
-//pub use info::*;
+mod info;
+pub use info::*;
//mod join;
//pub use join::*;
M shared/src/api/game/mygames.rs => shared/src/api/game/mygames.rs +2 -2
@@ 9,9 9,9 @@
use reqwest::{self, header::COOKIE};
use serde::{Deserialize, Serialize};
-use crate::{api::macros::extract_cookie, error::api::APIError, types::game::Game};
+use crate::{api::macros::extract_cookie, error::api::APIError};
-pub type ResultMyGames = Vec<Game>;
+pub type ResultMyGames = Vec<String>;
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
M shared/src/server/messages/datarequest.rs => shared/src/server/messages/datarequest.rs +5 -10
@@ 8,8 8,6 @@
use naia_bevy_shared::Message;
-use crate::types::{game::GamePub, user::UserPub};
-
#[derive(Message)]
pub struct DataRequest {
pub r#type: u8,
@@ 29,17 27,12 @@ impl DataRequest {
#[derive(Message)]
pub struct DataRequestResponse {
pub r#type: u8,
- pub users: Option<Vec<UserPub>>,
- pub games: Option<Vec<GamePub>>,
+ pub data: Option<Vec<String>>,
}
impl DataRequestResponse {
- pub fn new(r#type: u8, users: Option<Vec<UserPub>>, games: Option<Vec<GamePub>>) -> Self {
- Self {
- r#type,
- users,
- games,
- }
+ pub fn new(r#type: u8, data: Option<Vec<String>>) -> Self {
+ Self { r#type, data }
}
}
@@ 48,6 41,7 @@ pub enum DataRequestType {
GameAllForming = 100,
GameMyGames = 101,
GameCreate = 102,
+ GameInfo = 103,
PubUserDetails = 150,
}
@@ 58,6 52,7 @@ impl DataRequestType {
100 => Self::GameAllForming,
101 => Self::GameMyGames,
102 => Self::GameCreate,
+ 103 => Self::GameInfo,
150 => Self::PubUserDetails,
_ => {
// NOTE/TODO/FIXME: bad, veeeery bad.