--- /dev/null
+
+#[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,
+}
-
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,
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()),
}
--- /dev/null
+
+#[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)
+ }
+}
+
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;
--- /dev/null
+
+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();
+ }
+}
type RE = E;
+pub type InstanceName = String;
+
#[derive(Serialize,Debug)]
struct TestRenderContext { }
}
}
-#[get("/updates")]
+#[get("/<instance>")]
fn updates() -> impl response::Responder<'static> {
let tc = TestCounterInner { next : 0 };
let tc = BufReader::new(tc);
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))
}
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()
+ }
}
-