chiark / gitweb /
creategame command etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 11:59:45 +0000 (12:59 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 26 Jul 2020 11:59:45 +0000 (12:59 +0100)
src/cmdlistener.rs
src/commands.rs
src/global.rs
src/imports.rs

index bd83b506ce9403bf63c1f76ddfc9f45fb2ff2c9a..3ee23199eb9171b41df479c090613f6ff18e3f50 100644 (file)
@@ -31,8 +31,9 @@ struct CommandStream<'d> {
   euid : Result<u32, ConnectionEuidDiscoverEerror>,
   read : io::Lines<BufReader<UnixStream>>,
   write : CSWrite,
-  scope : Option<ManagementScope>,
   desc : &'d str,
+  scope : Option<ManagementScope>,
+  amu : Option<InstanceRef>,
 }
 
 type CSE = anyhow::Error;
@@ -195,10 +196,25 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse {
       Fine { }
     },
 
-/*
-    CreateGame(game) => {
-      
+    CreateGame(name) => {
+      let gs = GameState {
+        pieces : Default::default(),
+        players : Default::default(),
+        log : Default::default(),
+        gen : Generation(0),
+      };
+
+      let name = InstanceName {
+        scope : cs.scope.as_ref().ok_or(NoScope)?.clone(),
+        scoped_name : name,
+      };
+
+      cs.amu = Some(Instance::new(name, gs)?);
+
+      Fine { }
     },
+
+    /*
     AddPiece(game, { pos,count,name,info }) => {
       let game = cs.lookup_game(&game)?;
       let count = spec.count.unwrap_or(1);
@@ -271,7 +287,7 @@ impl CommandListener {
         let write = BufWriter::new(write);
 
         let cs = CommandStream {
-          scope: None, desc: &desc,
+          scope: None, amu: None, desc: &desc,
           read, write, euid,
         };
         cs.mainloop()?;
index 1436516b03a34cd77200ac4662b31e61f9021565..516ca2e3b862ec5131e0dbf77f9129b87b19c366 100644 (file)
@@ -5,6 +5,7 @@ use crate::imports::*;
 pub enum MgmtCommand {
   Noop { },
   SetScope(ManagementScope),
+  CreateGame(String),
 //  AddPiece(Box<dyn PieceSpec>),
 }
 
@@ -18,6 +19,8 @@ pub enum MgmtResponse {
 pub enum MgmtError {
   ParseFailed(String),
   AuthorisationError,
+  NoScope,
+  AlreadyExists,
   XXXU(&'static str),
 }
 display_as_debug!{MgmtError}
index d4512f00fe120e334523cc1f3a9cc33d252dc4ec..b5a72cf7ee59102590efab1736c95adb881110d8 100644 (file)
@@ -15,10 +15,17 @@ pub struct RawToken (pub String);
 
 // ---------- data structure ----------
 
+#[derive(Debug,Serialize,Deserialize)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
+pub struct InstanceName {
+  pub scope: ManagementScope,
+  pub scoped_name: String,
+}
+pub type InstanceRef = Arc<Mutex<Instance>>;
+
 #[derive(Debug)]
 pub struct Instance {
-  pub scope : ManagementScope,
-  pub scoped_name : String,
+  pub name : Arc<InstanceName>,
   pub gs : GameState,
   pub clients : DenseSlotMap<ClientId,Client>,
   pub updates : SecondarySlotMap<PlayerId, PlayerUpdates>,
@@ -26,7 +33,8 @@ pub struct Instance {
   pub tokens_clients : TokenRegistry<ClientId>,
 }
 
-#[derive(Debug,Deserialize,Serialize)]
+#[derive(Debug,Clone,Deserialize,Serialize)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
 pub enum ManagementScope {
   XXX,
   Unix { user : String /* username, so filename-safe */ },
@@ -46,6 +54,7 @@ pub struct InstanceGuard<'g> {
 #[derive(Default)]
 struct Global {
   // lock hierarchy: this is the innermost lock
+  games   : RwLock<HashMap<Arc<InstanceName>,InstanceRef>>,
   players : RwLock<TokenTable<PlayerId>>,
   clients : RwLock<TokenTable<ClientId>>,
   // xxx delete instances at some point!
@@ -107,17 +116,32 @@ lazy_static! {
 // ---------- Player and instance API ----------
 
 impl Instance {
-  #[throws(OE)]
-  pub fn new(gs: GameState,
-             scope : ManagementScope,
-             scoped_name: String) -> Instance {
-    Instance {
-      scope, scoped_name, gs,
+  /// Returns `None` if a game with this name already exists
+  #[throws(MgmtError)]
+  pub fn new(name: InstanceName, gs: GameState) -> InstanceRef {
+    let name = Arc::new(name);
+
+    let inst = Instance {
+      name : name.clone(),
+      gs,
       clients : Default::default(),
       updates : Default::default(),
       tokens_players : Default::default(),
       tokens_clients : Default::default(),
-    }
+    };
+
+    let mut games = GLOBAL.games.write().unwrap();
+    let entry = games.entry(name);
+
+    use hash_map::Entry::*;
+    let entry = match entry {
+      Vacant(ve) => ve,
+      Occupied(_) => throw!(MgmtError::AlreadyExists),
+    };
+
+    let ami = Arc::new(Mutex::new(inst));
+    entry.insert(ami.clone());
+    ami
   }
 
   #[throws(OE)]
@@ -170,15 +194,14 @@ impl InstanceGuard<'_> {
     for t in tokens.tr.drain() { global.remove(&t); }
   }
 
-  fn savefile(scope: &ManagementScope, scoped_name: &str,
-              prefix: &str, suffix: &str) -> String {
-    let scope_prefix = { use ManagementScope::*; match scope {
-        XXX => format!(""),
-        Unix{user} => { format!("{}:", user) },
+  fn savefile(name: &InstanceName, prefix: &str, suffix: &str) -> String {
+    let scope_prefix = { use ManagementScope::*; match &name.scope {
+      XXX => format!(""),
+      Unix{user} => { format!("{}:", user) },
     } };
     iter::once(prefix)
       .chain( iter::once(scope_prefix.as_ref()) )
-      .chain( utf8_percent_encode(scoped_name,
+      .chain( utf8_percent_encode(&name.scoped_name,
                                   &percent_encoding::NON_ALPHANUMERIC) )
       .chain( iter::once(suffix) )
       .collect()
@@ -189,12 +212,12 @@ impl InstanceGuard<'_> {
     w: fn(s: &Self, w: &mut BufWriter<fs::File>)
           -> Result<(),rmp_serde::encode::Error>
   ) {
-    let tmp = Self::savefile(&self.scope, &self.scoped_name, prefix,".tmp");
+    let tmp = Self::savefile(&self.name, prefix,".tmp");
     let mut f = BufWriter::new(fs::File::create(&tmp)?);
     w(self, &mut f)?;
     f.flush()?;
     drop( f.into_inner().map_err(|e| { let e : io::Error = e.into(); e })? );
-    let out = Self::savefile(&self.scope, &self.scoped_name, prefix,"");
+    let out = Self::savefile(&self.name, prefix,"");
     fs::rename(&tmp, &out)?;
     eprintln!("saved to {}", &out);
   }
@@ -224,33 +247,33 @@ impl InstanceGuard<'_> {
   }
 
   #[throws(OE)]
-  fn load_something<T:DeserializeOwned>(
-    scope: &ManagementScope,
-    scoped_name: &str,
-    prefix: &str
-  ) -> T {
-    let inp = Self::savefile(scope, scoped_name, prefix, "");
+  fn load_something<T:DeserializeOwned>(name: &InstanceName, prefix: &str) -> T {
+    let inp = Self::savefile(name, prefix, "");
     let mut f = BufReader::new(fs::File::open(&inp)?);
     // xxx handle ENOENT specially, own OE variant
     rmp_serde::decode::from_read(&mut f)?
   }
 
   #[throws(OE)]
-  pub fn load(scope: ManagementScope, scoped_name: String)
-              -> Arc<Mutex<Instance>> {
-    let gs : GameState = Self::load_something(&scope, &scoped_name, "g-")?;
+  pub fn load(name: InstanceName) -> Arc<Mutex<Instance>> {
+    // xxx scan on startup, rather than asking caller to specify names
+    // xxx should take a file lock on save area
+    let gs : GameState = Self::load_something(&name, "g-")?;
     let mut al : InstanceSaveAccesses<String>
-                       = Self::load_something(&scope, &scoped_name, "a-")?;
+                       = Self::load_something(&name, "a-")?;
     let mut updates : SecondarySlotMap<_,_> = Default::default();
     for player in gs.players.keys() {
       updates.insert(player, Default::default());
     }
+    let name = Arc::new(name);
+
     let inst = Instance {
-      scope, scoped_name, gs, updates,
+      name, gs, updates,
       clients : Default::default(),
       tokens_clients : Default::default(),
       tokens_players : Default::default(),
     };
+    // xxx record in GLOBAL.games
     let amu = Arc::new(Mutex::new(inst));
     let mut ig = amu.lock().unwrap();
     for (token, _) in &al.tokens_players {
@@ -344,9 +367,11 @@ const XXX_PLAYERS_TOKENS : &[(&str, &str)] = &[
 #[throws(OE)]
 pub fn xxx_global_setup() {
   let gs = xxx_gamestate_init();
-  let gi = Instance::new(gs, ManagementScope::XXX, "dummy".to_string())?;
-  let amu = Arc::new(Mutex::new(gi));
-  let mut ig = Instance::lock(&amu)?;
+  let ami = Instance::new(InstanceName {
+    scope: ManagementScope::XXX,
+    scoped_name: "dummy".to_string()
+  }, gs).expect("xxx create dummy");
+  let mut ig = Instance::lock(&ami)?;
   for (token, nick) in XXX_PLAYERS_TOKENS {
     let player = ig.player_new(PlayerState {
       nick : nick.to_string(),
index 5507ff2353f823d1dd32562efd47e119f4e9fcf4..5d810839006b53e6e9083a6fddb73e0faa349fa9 100644 (file)
@@ -8,7 +8,7 @@ pub use std::fmt::{self,Display,Debug};
 pub use std::thread;
 pub use std::time::Duration;
 pub use std::sync::{Arc,Mutex,MutexGuard,RwLock,Condvar};
-pub use std::collections::{HashMap,HashSet};
+pub use std::collections::{HashMap,hash_map,HashSet};
 pub use std::borrow::Borrow;
 pub use std::convert::{TryFrom,TryInto};
 pub use std::str;