diff --git a/server/Cargo.toml b/server/Cargo.toml index 45f6075..16b5ba4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -22,10 +22,11 @@ server_client = {git = "https://github.com/Mr-Llama-s-Wonderful-Soundboard/serve fallible-iterator = "0.2" regex = "1" lazy_static = "1" -anyhow = "1.0" +anyhow = "1" +tar = "0.4" # Game loading -rhai = {version = "1.0", features = ["serde", "sync"]} +rhai = {version = "1", features = ["serde", "sync"]} serde_json = "1" serde = "1" schemars = "0.8" diff --git a/server/src/server.rs b/server/src/server.rs index 493eb93..1e9c41f 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -2,6 +2,7 @@ use anyhow::Result; use log::{debug, warn}; +use prost_types::Timestamp; use tokio::{ io::{AsyncReadExt, BufReader}, net::TcpListener, @@ -11,18 +12,9 @@ use uuid::Uuid; use std::{collections::HashMap, io::ErrorKind, net::SocketAddr, sync::Arc}; -use crate::{ - db, - games::RunningGame, - server::{ - connection::{ +use crate::{db, games::RunningGame, server::{connection::{ connect, create_lobby, disconnect, get_public_lobbies, join_lobby_with_code, name, - }, - game::{get_card_image, on_click, query_status}, - lobby::{leave, ready, users, vote}, - }, - server_properties::ServerProperties, -}; + }, game::{get_card_image, get_cards_images, on_click, query_status}, lobby::{leave, ready, users, vote}, protos::game::{Cards, cards::Card}}, server_properties::ServerProperties}; use prost::Message; @@ -34,6 +26,7 @@ mod lobby; mod protos; mod socket_manager; mod votes; +mod time; // pub fn decode(bytes: &[u8]) { // let p = protos::protocol::ServerClientPacket::decode(bytes).expect("AAAAH"); @@ -252,10 +245,12 @@ pub async fn serve( // GAME Data::QueryCardImage(card_kind) => { + // get_cards_images(&mut service_data, &socket_manager, Cards { cards: vec![Card { kind: card_kind.kind.clone(), time: Some(Timestamp {seconds: 0, nanos: 0}) }] }).await.unwrap(); get_card_image(&mut service_data, &socket_manager, card_kind) .await .expect("Error handling card image query") } + Data::QueryCardImages(cards) => {} Data::CallOnClick(card_id) => { debug!("{:?}", card_id); on_click(&mut service_data, &socket_manager, card_id) diff --git a/server/src/server/game.rs b/server/src/server/game.rs index c723afa..4185f3a 100644 --- a/server/src/server/game.rs +++ b/server/src/server/game.rs @@ -1,16 +1,24 @@ +use std::{borrow::BorrowMut, fs::File, io::Write, path::Path}; + use super::{ - protos::game::{game_status::Piles, CardKind, GameStatus, Image}, + protos::game::{game_status::Piles, CardKind, Cards, GameStatus, Image}, socket_manager::SocketManager, ServiceData, }; use crate::{ games::{CardId, CardIdx, PileKind, RunningPile}, - server::protos::{ - game::game_status::CustomInfoMessage, - protocol::server_client_packet::{self, Data}, + server::{ + protos::{ + game::{cards::Card, game_status::CustomInfoMessage}, + protocol::server_client_packet::{self, Data}, + }, + time::EpochTime, }, }; use anyhow::{anyhow, Result}; +use bytes::BytesMut; +use prost_types::Timestamp; +use tar::{Archive, Builder}; pub(super) async fn get_status( data: &mut ServiceData, @@ -172,6 +180,79 @@ pub(super) async fn get_card_image( Ok(()) } +pub(super) async fn get_cards_images( + data: &mut ServiceData, + socket_mgr: &SocketManager, + cards: Cards, +) -> Result<()> { + log::info!("Getting images location"); + let time = std::time::Instant::now(); + let uuid = data.user_id.get()?; + let lobby: u32 = match data.db.get_lobby_for_user(uuid).await { + Some(l) => l, + None => return Err(anyhow!("User isn't in a lobby")), + }; + let game_id = match data.running_games.read().await.get(&lobby) { + Some(x) => x.read().await.0, + None => return Err(anyhow!("User isn't in a lobby with a running game",)), + }; + let game = &data.games[game_id as usize]; + let mut b = Vec::new(); + let mut ar = Builder::new(b); + for card_kind in cards.cards { + let (face, back) = game.get_card_paths(&card_kind.kind); + // log::info!("Loading face image [{:?}]", time.elapsed()); + + while !face.exists() || !back.exists() { + tokio::task::yield_now().await; + } + fn is_newer>(c: &Card, p: &P) -> bool { + c.time + .clone() + .and_then(|x| { + p.as_ref() + .metadata() + .and_then(|x| x.modified()) + .ok() + .map(|y| (y.into(), x.into())) + }) + .map_or(false, |(x, y): (EpochTime, EpochTime)| x > y) + } + + if is_newer(&card_kind, &face) { + ar.append_file( + format!("{}/face.png", card_kind.kind), + &mut File::open(face).unwrap(), + ) + .unwrap(); + } + + if is_newer(&card_kind, &back) { + ar.append_file( + format!("{}/back.png", card_kind.kind), + &mut File::open(back).unwrap(), + ) + .unwrap(); + } + + } + + File::create("a.tar").unwrap().write_all(&ar.into_inner().unwrap()).unwrap(); + // TODO Apply lz4 compression + // log::info!("Loaded images {} [{:?}]", card_kind.kind, time.elapsed()); + // socket_mgr + // .write( + // data.user_id.get_ref()?, + // Data::ReturnCardImage(Image { + // face: face_buf, + // back: back_buf, + // kind: card_kind.kind, + // }), + // ) + // .await?; + Ok(()) +} + pub(super) async fn on_click( data: &mut ServiceData, socket_mgr: &SocketManager, diff --git a/server/src/server/protos/game.rs b/server/src/server/protos/game.rs index d8d735f..1e48d3c 100644 --- a/server/src/server/protos/game.rs +++ b/server/src/server/protos/game.rs @@ -12,6 +12,48 @@ pub struct CardKind { pub kind: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] +pub struct Cards { + #[prost(message, repeated, tag="1")] + pub cards: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Cards`. +pub mod cards { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Card { + #[prost(string, tag="1")] + pub kind: ::prost::alloc::string::String, + #[prost(message, optional, tag="2")] + pub time: ::core::option::Option<::prost_types::Timestamp>, + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Images { + #[prost(oneof="images::Data", tags="1, 2")] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `Images`. +pub mod images { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct SetUp { + #[prost(uint32, tag="1")] + pub number: u32, + } + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct DataPacket { + #[prost(uint32, tag="1")] + pub id: u32, + #[prost(bytes="vec", tag="2")] + pub data: ::prost::alloc::vec::Vec, + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + #[prost(message, tag="1")] + Setup(SetUp), + #[prost(message, tag="2")] + DataPacket(DataPacket), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Image { #[prost(bytes="vec", tag="1")] pub face: ::prost::alloc::vec::Vec, diff --git a/server/src/server/protos/protocol.rs b/server/src/server/protos/protocol.rs index dfbefd3..4549972 100644 --- a/server/src/server/protos/protocol.rs +++ b/server/src/server/protos/protocol.rs @@ -1,6 +1,6 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct ClientServerPacket { - #[prost(oneof="client_server_packet::Data", tags="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14")] + #[prost(oneof="client_server_packet::Data", tags="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15")] pub data: ::core::option::Option, } /// Nested message and enum types in `ClientServerPacket`. @@ -38,11 +38,13 @@ pub mod client_server_packet { CallOnClick(super::super::game::CardId), #[prost(message, tag="14")] QueryGameStatus(()), + #[prost(message, tag="15")] + QueryCardImages(super::super::game::Cards), } } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ServerClientPacket { - #[prost(oneof="server_client_packet::Data", tags="1, 2, 3, 4, 5, 6, 7, 8, 9")] + #[prost(oneof="server_client_packet::Data", tags="1, 2, 3, 4, 5, 6, 7, 8, 10, 9")] pub data: ::core::option::Option, } /// Nested message and enum types in `ServerClientPacket`. @@ -68,6 +70,8 @@ pub mod server_client_packet { /// GAME #[prost(message, tag="8")] ReturnCardImage(super::super::game::Image), + #[prost(message, tag="10")] + ReturnCardsImages(super::super::game::Images), #[prost(message, tag="9")] GameStatus(super::super::game::GameStatus), } diff --git a/server/src/server/time.rs b/server/src/server/time.rs new file mode 100644 index 0000000..46cac29 --- /dev/null +++ b/server/src/server/time.rs @@ -0,0 +1,27 @@ +use std::time::SystemTime; +use prost_types::Timestamp; + +#[derive(PartialEq)] +pub struct EpochTime(Timestamp); + +impl From for EpochTime { + fn from(a: SystemTime) -> Self { + Self(a.into()) + } +} + +impl From for EpochTime { + fn from(a: Timestamp) -> Self { + Self(a) + } +} + +impl PartialOrd for EpochTime { + fn partial_cmp(&self, other: &Self) -> Option { + if other.0.seconds == self.0.seconds { + self.0.seconds.partial_cmp(&other.0.seconds) + }else{ + self.0.nanos.partial_cmp(&other.0.nanos) + } + } +} \ No newline at end of file