Ver Fonte

starting to work

Nicolas Winkler há 4 anos atrás
pai
commit
76e481c932
8 ficheiros alterados com 242 adições e 42 exclusões
  1. 7 2
      Cargo.toml
  2. 106 0
      questions.txt
  3. 92 22
      src/datasource.rs
  4. 6 1
      src/main.rs
  5. 11 9
      src/server/gamelobby.rs
  6. 3 1
      src/server/messages.rs
  7. 7 3
      src/server/server.rs
  8. 10 4
      static/comm.js

+ 7 - 2
Cargo.toml

@@ -18,5 +18,10 @@ bytes = "0.5.3"
 serde = "1.0"
 serde_json = "1.0"
 rand = "0.7.3"
-rusqlite = "0.24.2"
-lazy_static = "1.4.0"
+lazy_static = "1.4.0"
+r2d2_sqlite = "0.17.0"
+r2d2 = "0.8.9"
+
+[dependencies.rusqlite]
+version = "0.24.2"
+features = ["bundled"]

+ 106 - 0
questions.txt

@@ -0,0 +1,106 @@
+Sportart für Rentner
+Schnelles Auto, mit dem man angeben kann
+ursprünglich als 13.ter Monat angedacht
+Name einer Geheimdienstoperation
+Gerät zum Elche häuten
+Produkt aus dem Brockenhaus
+Marke für Windeln
+Studiengang über Salamiproduktion
+Russische Oper
+Neustes Trendgemüse (Superfood)
+Sexspielzeug
+Harry Potter und der/die/das ...
+Brasilianischer Tanzstil
+Kurzwort für Rasenmähen
+Vietnamesischer Feiertag
+Unwetterwolke
+dänisches Wort für Heiterkeit
+U-Boot aus dem Kalten Krieg
+neuartiger Virus
+Schlechter Käse
+deutsche Kleinstadt
+süchtigmachendes Handyspiel
+Zauberspruch aus Harry Potter
+Frauenname von Höhlenmenschen
+E-Zigarrettenmarke
+Ausgeschriebenes Verkehrsschild
+Wort aus dem polnischen Militärjargon
+Firma aus dem Silicon Valley
+Imperium der Antike
+Injektionsmittel bei Schönheitsoperationen
+Epoche aus der Architektur
+Physiker, der nie etwas nach sich benennen konnte
+Dating-App für Katzen
+Sektenguru aus dem Regenwald
+Horrorkomödie auf Netflix
+Scheidungsgrund
+Japanische Krankheit
+NASAs geheimer Klebstoff
+Blumiger Kerzenduft
+Name der neusten Trend-Sekte
+Piratenbucht
+Experimenteller Cocktail
+Lampe aus IKEA
+Slang für Geld
+Der Pummligste von den 7 Zwergen
+Firmenname vom Klempner
+Gericht eines 5-Sterne-Kochs
+Gescheitertes Wirtschaftsmodell
+Korrupte Waschmaschinenfirma
+Balkanische Süssspeise
+Schweizer Autor
+Synonym für Homophobie
+unbekannte Figur der griechischen Mythologie
+Putins Assistentin
+Veganes Kochmagazin
+Wasserglace für Kinder
+Teesorte aus exotischen Kräutern
+Grönländische Beleidigung
+Mittel gegen Haarausfall
+Zahnpastamarke
+Nachwuchstalent aus Brasilien
+Böser Alien
+Ausgestorbene Tierart
+Videokonferenztool
+lateinischer Pflanzenname
+Maler aus dem 18. Jahrhundert
+Benutzername für MSN
+niederländische Velomarke 
+Drachenname
+Figur aus einem Kindermärchen
+Von Red Bull gesponserter Extremsport
+Insektenspray
+Beliebtes Spiel im Casino
+hippe Sockenmarke
+Raumschiff, das es nie auf den Mond schaffte
+Coiffeurname
+Name der neuen ZVV-Trams
+Abgelehnter Buchtitel einer Autobiografie
+Fussballteam in der dritten Liga
+Name des ungeliebtesten Kindes
+Schachweltmeister
+Tödlicher Fusspilz
+trendiger Mädchenvorname aus den USA
+Erfolgloser Stuntman
+Mittelalterliches Werkzeug
+Neues Handy von Apple
+Amerikanischer Hurrikan
+Juristisches Fachmagazin
+Bergdorf im Kanton Graubünden
+Isländische Moossorte
+Trenddroge in den 80er Jahren
+Unbeliebtes Instrument, das alle Kinder lernen müssen
+Instagram-Name eines Hundes
+Neu entdeckte Farbe
+Staubsaugermarke
+Billiges Parfüm
+Südseefisch
+Schmerzmittel
+Chemisches Element, das nicht reagiert
+Spotifyplaylist
+italienischer Vulkan
+Fluss in Uganda
+Schneckenart mit einem Auge
+Tätigkeit gegen Corona
+Pferd von Napoleon
+"Danke" in einer afrikanischen Stammessprache

+ 92 - 22
src/datasource.rs

@@ -1,16 +1,43 @@
-use rusqlite::Connection;
+use r2d2_sqlite::SqliteConnectionManager;
+use r2d2::Pool;
+#[macro_use]
+use rusqlite::params;
+use std::sync::Arc;
+use std::fs::File;
+use std::io::{self, BufRead};
+use rand::thread_rng;
+use rand::seq::SliceRandom;
+
+
+pub fn create_array_source(filename: &str) -> Arc<dyn DataSource<String>> {
+    let file = File::open(filename).unwrap();
+    let lines = io::BufReader::new(file).lines();
+    let mut questions: Vec<String> = Vec::new();
+    for line in lines {
+        if let Ok(l) = line {
+            questions.push(l);
+        }
+    }
+
+    Arc::new(ArraySource::create(questions))
+}
 
 pub trait DataSource<T> {
-    fn get_ith<'a>(&'a self, i: usize) -> Option<&'a T>;
+    fn get_ith(&self, i: usize) -> Option<T>;
+    fn len(&self) -> usize;
 }
 
 pub struct ArraySource<T> {
     words: Vec<T>
 }
 
-impl<T> DataSource<T> for ArraySource<T> {
-    fn get_ith<'a>(&'a self, i: usize) -> Option<&'a T> {
-        self.words.get(i)
+impl<T: Clone> DataSource<T> for ArraySource<T> {
+    fn get_ith(&self, i: usize) -> Option<T> {
+        self.words.get(i).cloned()
+    }
+
+    fn len(&self) -> usize {
+        self.words.len()
     }
 }
 
@@ -23,33 +50,76 @@ impl<T> ArraySource<T> {
 }
 
 pub struct SqliteSource {
-    conn: Connection
+    pool: Pool<SqliteConnectionManager>
 }
 
 impl DataSource<String> for SqliteSource {
-    fn get_ith<'a>(&'a self, i: usize) -> Option<&'a String> {
-        let word: rusqlite::Result<String> = self.conn.query_row(
-            &format!(
-                "SELECT question
-                 FROM questions
-                 WHERE id={};", i),
-            rusqlite::NO_PARAMS,
-            |row| row.get(0));
-        
-        //word.ok().as_ref()
-        None
+    fn get_ith(& self, i: usize) -> Option<String> {
+        match self.pool.get() {
+            Ok(p) => {
+                let r: rusqlite::Result<String> = p.query_row(
+                    "SELECT question
+                     FROM questions
+                     WHERE id=?1;",
+                     params![i as i32],
+                    |row| row.get(0));
+
+                r.ok()
+            },
+            Err(_) => None
+        }
+    }
+
+    fn len(&self) -> usize {
+        match self.pool.get() {
+            Ok(p) => {
+                let r: rusqlite::Result<i32> = p.query_row(
+                    "SELECT COUNT(*)
+                     FROM questions;",
+                     rusqlite::NO_PARAMS,
+                    |row| row.get(0));
+
+                r.unwrap_or(0) as usize
+            },
+            Err(_) => 0
+        }
     }
 }
 
 impl SqliteSource {
-    pub fn open(db_name: &str) -> Result<Self, rusqlite::Error> {
+    pub fn open(db_name: &str) -> Result<Self, r2d2::Error> {
         Ok(SqliteSource {
-            conn: Connection::open(db_name)?
+            pool: Pool::new(SqliteConnectionManager::file(db_name))?
         })
     }
 }
 
-lazy_static! {
-    //static ref default_source: Box<dyn DataSource<String>> =
-    //    Box::new(SqliteSource::open("questions.db").unwrap_or(ArraySource::create(vec!["".to_owned()])))
+
+pub struct Shuffler<T> {
+    source: Arc<dyn DataSource<T>>,
+    permutation: Vec<usize>,
+    index: usize
+}
+
+
+impl<T> Shuffler<T> {
+    pub fn create(source: Arc<dyn DataSource<T>>) -> Self {
+        let mut permutation = (0..source.len()).collect::<Vec<_>>();
+        permutation.shuffle(&mut thread_rng());
+        Self {
+            source,
+            permutation,
+            index: 0
+        }
+    }
+
+    pub fn get(&mut self) -> T {
+        let old_index = self.index;
+        self.index += 1;
+        if self.index >= self.permutation.len() {
+            self.index = 0;
+        }
+
+        self.source.get_ith(self.permutation[old_index]).unwrap()
+    }
 }

+ 6 - 1
src/main.rs

@@ -1,4 +1,5 @@
 use std::collections::BTreeMap;
+use std::sync::Arc;
 use actix::prelude::*;
 use actix_files as fs;
 use actix_web::{middleware, web, App, HttpServer};
@@ -16,13 +17,17 @@ mod server {
 }
 
 use crate::server::server::{Server, ws_initiate};
+use crate::datasource::SqliteSource;
 
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
     std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
     env_logger::init();
 
-    let server = Server { lobbies: BTreeMap::new() };
+    let server = Server {
+        lobbies: BTreeMap::new(),
+        default_data: datasource::create_array_source("questions.txt")
+    };
     let server_addr = server.start();
 
     HttpServer::new(move || {

+ 11 - 9
src/server/gamelobby.rs

@@ -1,4 +1,6 @@
 use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::borrow::Borrow;
 
 use rand::thread_rng;
 use rand::seq::SliceRandom;
@@ -28,7 +30,9 @@ pub struct GameLobby {
     waiting_players: BTreeMap<String, Addr<GameConnection>>,
     ready_players: Vec<String>,
     lobby_state: LobbyState,
-    data_source: Box<dyn datasource::DataSource<String>>,
+    
+    //data_source: Arc<dyn datasource::DataSource<String>>,
+    shuffler: datasource::Shuffler<String>
 }
 
 impl Actor for GameLobby {
@@ -96,7 +100,7 @@ impl Handler<SubmitGuessMsg> for GameLobby {
 }
 
 impl GameLobby {
-    pub fn new(gi: String) -> Self {
+    pub fn new(gi: String, data_source: Arc<dyn datasource::DataSource<String>>) -> Self {
         GameLobby {
             connected_players: BTreeMap::new(),
             game_id: gi,
@@ -104,12 +108,13 @@ impl GameLobby {
             waiting_players: BTreeMap::new(),
             ready_players: Vec::new(),
             lobby_state: LobbyState::Starting,
-            data_source: Box::new(datasource::ArraySource::create(vec![
+            shuffler: datasource::Shuffler::create(data_source)
+            /*Box::new(datasource::ArraySource::create(vec![
                 "Delikatessfutter für Hunde".to_owned(),
                 "Ein Hotel für die ganze Familie".to_owned(),
                 "Brasilianischer Superstar".to_owned(),
                 "Buchstabe des griechischen Alphabets".to_owned(),
-            ]))
+            ]))*/
         }
     }
 
@@ -124,12 +129,9 @@ impl GameLobby {
                 self.ready_players.clear();
             },
             LobbyState::Creating => {
-                let ds = &self.data_source;
+                let s = &mut self.shuffler;
                 let mut index = 0;
-                self.game.start_round(|| {
-                    index += 1;
-                    ds.get_ith(index - 1).unwrap().clone()
-                });
+                self.game.start_round(|| s.get());
             },
             _ => {}
         }

+ 3 - 1
src/server/messages.rs

@@ -3,6 +3,7 @@ use actix::prelude::*;
 use crate::server::server::GameConnection;
 use crate::server::gamelobby::GameLobby;
 use crate::websocket::*;
+use crate::datasource::DataSource;
 
 #[derive(Message)]
 #[rtype(result = "Answer")]
@@ -15,7 +16,8 @@ pub struct JoinRequest {
 #[derive(Message)]
 #[rtype(result = "Answer")]
 pub struct CreateLobbyRequest {
-    pub lobby_id: String, pub p: Addr<GameConnection>
+    pub lobby_id: String,
+    pub p: Addr<GameConnection>
 }
 
 #[derive(Message)]

+ 7 - 3
src/server/server.rs

@@ -20,13 +20,14 @@ pub async fn ws_initiate(server: web::Data<Addr<Server>>,
                          r: HttpRequest,
                          stream: web::Payload) -> Result<HttpResponse, Error> {
     println!("{:?}", r);
-    let (addr, res) = ws::start_with_addr(GameConnection::new(server.as_ref().clone()), &r, stream)?;
+    let res = ws::start(GameConnection::new(server.as_ref().clone()), &r, stream)?;
     println!("{:?}", res);
     Ok(res)
 }
 
 pub struct Server {
     pub lobbies: BTreeMap<String, Addr<GameLobby>>,
+    pub default_data: Arc<dyn datasource::DataSource<String>>
 }
 
 impl Actor for Server {
@@ -59,7 +60,7 @@ impl Handler<CreateLobbyRequest> for Server {
         match existing_lobby {
             Some(_) => Answer::LobbyAlreadyExists,
             None => {
-                let lobby = GameLobby::new(clr.lobby_id.clone());
+                let lobby = GameLobby::new(clr.lobby_id.clone(), self.default_data.clone());
                 let lobby_addr = lobby.start();
                 self.lobbies.insert(clr.lobby_id.clone(), lobby_addr.clone());
                 Answer::LobbyCreated(lobby_addr)
@@ -178,7 +179,10 @@ impl GameConnection {
                 ClientMessage::CreateGame{game_id, nick} => {
                     self.game_id = Some(game_id.clone());
                     self.nick = Some(nick);
-                    self.server.do_send(CreateLobbyRequest{ lobby_id: game_id.clone(), p: ctx.address() });
+                    self.server.do_send(CreateLobbyRequest{
+                        lobby_id: game_id.clone(),
+                        p: ctx.address()
+                    });
                 },
                 ClientMessage::Join{game_id, nick} => {
                     self.server.do_send(JoinRequest{ lobby_id: game_id.clone(), nick: nick.clone(), p: ctx.address() });

+ 10 - 4
static/comm.js

@@ -122,6 +122,11 @@ $(function() {
                 var chars = creating.available_chars;
                 $('#question').val(creating.question);
                 $('#letters').val(chars.join());
+
+
+                $('#guessing').hide();
+                $('#startingform').hide();
+                $('#results').hide();
                 $('#createform').show();
             }
             else if (gs.state_data.guessing != null) {
@@ -143,7 +148,9 @@ $(function() {
 
                 questionlist = questions;
                 wordlist = sub_words.map(pair => pair[0]);
-                
+
+                $('#startingform').hide();
+                $('#results').hide();
                 $('#createform').hide();
                 $('#guessing').show();
             }
@@ -186,12 +193,11 @@ $(function() {
             $table.append(wordline);
         }
 
-        $('#results').append($table);
-
+        $('#results').html($table);
 
+        $('#startingform').hide();
         $('#createform').hide();
         $('#guessing').hide();
-        $('#startingform').hide();
         $('#results').show();
     }
 });