chiark / gitweb /
before redo for user based guards etc.
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 20 May 2020 21:35:03 +0000 (22:35 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 20 May 2020 21:35:03 +0000 (22:35 +0100)
src/error.rs [new file with mode: 0644]
src/gamestate.rs
src/global.rs [new file with mode: 0644]
src/imports.rs
src/instance.rs [new file with mode: 0644]
src/main.rs
src/pieces.rs

diff --git a/src/error.rs b/src/error.rs
new file mode 100644 (file)
index 0000000..61b26b1
--- /dev/null
@@ -0,0 +1,8 @@
+
+#[derive(Error)]
+enum Error {
+  [error("attempt to create instance with same name as existing instance")]
+  InstanceAlreadyExists,
+  [error("attempt to create instance with same name as existing instance")]
+  UnknownInstance,
+}
index 994f25dedfe506828883d257ad2ac193d1eaed8d..761e14fd390681e0a8b9f2ffb85dd66586a5a93a 100644 (file)
@@ -1,53 +1,30 @@
-
 pub trait Piece {
-  type Msg : Serialize;
-  fn msg(&self) -> Msg;
+  fn svg(&self, pr : &PiecedRecord) -> SvgData;
 }
 
 #[derive(Debug)]
 pub struct PieceRecord {
-  x : Coord,
-  y : Coord,
+  pos : Pos,
   p : Box<dyn Piece>,
   held : Option<PlayerRef>,
 }
 
-const RECENT_BUFFER : usize = 10;
 
 #[derive(Debug)]
 pub struct GameState {
-  gen : Counter,
-  data : GameStateData,
-  recent : VecDeque<MsgUpdate>,
-  notify : Condvar,
-}
-
-#[derive(Debug)]
-pub struct GameStateData {
   pub pieces : Vec<PieceRecord>,
   pub players : Vec<PlayerRecord>,
 }
 
-impl Deref for GameState {
-  type Output = GamStateData;
-  fn deref(&self) -> &GamStateData { &self.data }
-}
-
-impl GameState {
-  fn as_ref(&self) -> (&usize, &GameStateData) { (&self.gen, &self.data) }
-  fn gen(&self) -> usize { self.gen }
+type MsgPiece = SvgData;
 
-  fn<F> update(&mut self, f : F)
-  where F : FnOnce(&mut GameStateData) -> MsgUpdate {
-    let msg = f(&mut self.data),
-    if let MsgNoUpdate = msg { return }
-    self.gen += 1,
-    if self.recent.len() >= RECENT_BUFFER { self.pop_front() }
-    self.recent.push_back(msg);
-    self.notify.notify_all();
-  }
+pub struct GameRef (InstanceGuard);
+impl Deref for GameRef {
+  type Output = GamState;
+  fn deref(&self) -> &GameState { self.0.read() }
 }
 
+
 #[derive(Serialize)]
 enum MsgUpdate {
   MsgNoUpdate,
@@ -56,31 +33,25 @@ enum MsgUpdate {
   MsgPieceUpdate(usize, MsgPiece),
 }
 
-struct MsgPiece {
-  
-}
-
 impl PieceRecord {
-  fn msg(&self) -> MsgPiece {
-    
-  }
+  fn msg(&self) -> MsgPiece { self.p.svg(self) }
 }
 
-impl GameState {
+impl GameRef {
   fn piece_insert(&mut self, i : usize, p : PieceRecord) {
-    self.update(|d| {
+    self.0.update(|d| {
       d.pieces.insert(i, p);
       MsgPieceInsert(i, p.msg())
     );
   }
   fn piece_delete(&mut self, i : usize) {
-    self.update(|d| {
+    self.0.update(|d| {
       d.pieces.remove(i, p);
       MsgPieceDelete(i)
     }
   }
   fn piece_update(&mut self, i : usize, p : PieceRecord) {
-    self.update(|d| {
+    self.0.update(|d| {
       d.pieces[i] = p,
       MsgPieceUpdate(i, p.msg()),
     }
diff --git a/src/global.rs b/src/global.rs
new file mode 100644 (file)
index 0000000..33f3796
--- /dev/null
@@ -0,0 +1,37 @@
+
+#[derive(Default)]
+struct Global {
+  instances : RwLock<HashMap<InstanceName, Rc<Instance>>>,
+  // xxx delete instances at some point!
+}
+
+lazy_static! {
+  static ref GLOBAL : Global = Default::default();
+}
+
+fn lookup_instance(name : &str) -> Option<Rc<Instance>> {
+  GLOBAL.instances().read().get(name)
+}
+
+#[throws(TE)]
+fn create_instance(name : &str, i : Rc<Instance>) {
+  let w = GLOBAL.instances().write();
+  match w.entry(name) {
+    Occupied(oe) => throw!(TE::InstanceAlreadyExists);
+    Vacant(ve) => ve.insert(i);
+  }
+}
+
+impl<'r> FromParam<'r> for InstanceGuard<'r> {
+  // xxx any additional auth should go here
+  type Error = AE;
+  #[throws(E)]
+  fn from_param(param: &'r RawStr) -> Self {
+    let g = GLOBAL.instances().read();
+    let iname = param.as_str();
+    let i = g.get(iname);
+    let i = i.ok_or(anyhow!("unnown instance"))?;
+    i.lock(iname)
+  }
+}
+
index 7582a078d9edc1571827c076eb35c02bf8bbcbb7..dc4c222b810f9a3c715c932a03b7d21e51232dcd 100644 (file)
@@ -21,3 +21,8 @@ pub use rocket::response::NamedFile;
 pub use rocket::response;
 
 pub type E = anyhow::Error;
+
+pub type SvgData = Vec<u8>;
+pub type Coord = isize;
+pub type Pos = [Coord; 2];
+pub type Colour = String;
diff --git a/src/instance.rs b/src/instance.rs
new file mode 100644 (file)
index 0000000..720cb1c
--- /dev/null
@@ -0,0 +1,56 @@
+
+const RECENT_BUFFER : usize = 10;
+#[derive(Debug)]
+pub struct Instance {
+  g : RwLock<Game>,
+  g_notify : Condvar,
+}
+
+#[derive(Debug)]
+struct Game {
+  gen : Counter,
+  gs : GameState,
+  recent : VecDeque<MsgUpdate>,
+}
+
+impl Instance {
+  fn new(gs : GameState) -> Instance {
+    Instance {
+      g_notify : Condvar::new(),
+      g : RwLock::new(Game {
+        gen : 0,
+        gs,
+        recent : VecDequeue::with_capacity(RECENT_BUFFER),
+      }),
+    }
+  }
+}
+
+pub struct InstanceGuard<'r> {
+  iname : &'r str;
+  g : RwLockWriteGuard<Game>,
+  g_notify : &'r Condvar,
+}
+
+impl Instance {
+  fn lock(&'r self, iname : &'r str) -> InstanceGuard<'r> {
+    let g = self.g.lock();
+    InstanceGuard { g, iname, g_notify : &self.g_notify }
+  }
+}
+
+impl InstanceGuard {
+  fn read(&self) -> &GameState { &self.g.deref().gs }
+  fn iname(&self) -> &str { self.iname }
+
+  fn<F> update(&mut self, f : F)
+  where F : FnOnce(&mut GameState) -> MsgUpdate {
+    let msg = f(&mut self.gs.g),
+    if let MsgNoUpdate = msg { return }
+    self.gs.gen += 1,
+    if self.gw.recent.len() >= RECENT_BUFFER { self.pop_front() }
+    self.g.recent.push_back(msg);
+    self.g_notify.notify_all();
+  }
+}
index d3d3a7dc54334490c81553b7388bc113a673683d..5bfbde915605bf7a13673f29b90340459c6a5505 100644 (file)
@@ -13,6 +13,8 @@ use imports::*;
 
 type RE = E;
 
+pub type InstanceName = String;
+
 #[derive(Serialize,Debug)]
 struct TestRenderContext { }
 
@@ -53,7 +55,7 @@ impl Read for TestCounterInner {
   }
 }
 
-#[get("/updates")]
+#[get("/<instance>")]
 fn updates() -> impl response::Responder<'static> {
   let tc = TestCounterInner { next : 0 };
   let tc = BufReader::new(tc);
@@ -63,7 +65,7 @@ fn updates() -> impl response::Responder<'static> {
   response::content::Content(ct,ch)
 }  
 
-#[get("/<leaf>")]
+#[get("/_/<leaf>")]
 fn resource(leaf : CheckedResourceLeaf) -> io::Result<NamedFile> {
   let template_dir = "templates"; // xxx
   NamedFile::open(format!("{}/{}", template_dir, leaf.safe))
index 6454ae1a877dcea3058ba5606eac1e9fa5e43eba..0f6fa366e6769b514afee17dbad9a1bdace7ae61 100644 (file)
@@ -5,5 +5,10 @@ struct Disc {
 }
 
 impl Piece for Disc {
+  fn svg(&self, pr : &PiecedRecord) -> SvgData {
+    format!(
+      r#"<circle cs="{}" cy="{}" r="{}" style="fill: {};"/>"#,
+      pr.pos[0], pr.pos[1], swlf.size, self.colour,
+    ).into_bytes()
+  }
 }
-