players : Default::default(),
log : Default::default(),
gen : Generation(0),
+ max_z: ZCoord(0.),
};
let name = InstanceName {
Fine { }
},
-
+// let game = cs.lookup_game(&game)?;
- /*
- AddPiece(game, { pos,count,name,info }) => {
- let game = cs.lookup_game(&game)?;
- let count = spec.count.unwrap_or(1);
- let pos = spec.ok_or(XXU("missing piece pos"))?;
- let _xxx_name = spec.name;
- let pc = info.load()?;
-
- }
- }, // xxx*/
}
}
response
}
+const XXX_START_POS : Pos = [20,20];
+const XXX_DEFAULT_POSD : Pos = [5,5];
+
+const CREATE_PIECES_MAX : u32 = 300;
+
#[throws(ME)]
-fn execute_game_insn(_gs: &mut GameState, update: MgmtGameInstruction)
+fn execute_game_insn(gs: &mut GameState, update: MgmtGameInstruction)
-> (Vec<(PieceId,PieceUpdateOp<()>)>, Vec<LogEntry>) {
use MgmtGameInstruction::*;
match update {
Noop { } => (vec![], vec![]),
+
+ AddPiece(PiecesSpec{ pos,posd,count,face,info }) => {
+ let count = count.unwrap_or(1);
+ if count > CREATE_PIECES_MAX { throw!(LimitExceeded) }
+ let posd = posd.unwrap_or(XXX_DEFAULT_POSD);
+ let face = info.resolve_spec_face(face)?;
+
+ let mut updates = Vec::with_capacity(count as usize);
+ let mut pos = pos.unwrap_or(XXX_START_POS);
+ for _ in 0..count {
+ let p = info.load()?;
+ let z = ZCoord(gs.max_z.0 + (count + 1) as f64);
+ let pc = PieceState {
+ held: None,
+ zlevel: ZLevel { z, zg: gs.gen },
+ lastclient: Default::default(),
+ gen_before_lastclient: Generation(0),
+ gen: gs.gen,
+ pos, p, face,
+ };
+ let piece = gs.pieces.insert(pc);
+ updates.push((piece, PieceUpdateOp::Insert(())));
+ pos[0] += posd[0];
+ pos[1] += posd[1];
+ }
+
+ (updates, vec![ LogEntry {
+ html: format!("The facilitaror added {} pieces", count),
+ }])
+ },
}
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtGameInstruction {
Noop { },
- // AddPiece(Box<dyn PieceSpec>),
+ AddPiece(PiecesSpec),
}
#[derive(Debug,Serialize,Deserialize)]
AlreadyExists,
GameBeingDestroyed,
GameCorrupted,
+ LimitExceeded,
SVGProcessingFailed(#[from] SVGProcessingError),
+ GameError(#[from] GameError),
}
display_as_debug!{MgmtError}
use crate::imports::*;
-#[derive(Error,Debug)]
+#[derive(Error,Debug,Serialize,Deserialize)]
#[error("operation error {:?}",self)]
pub enum GameError {
Conflict,
PieceGone,
PieceHeld,
+ FaceNotFound,
InternalErrorSVG(#[from] SVGProcessingError),
}
}
define_index_type! {
+ #[derive(Default)]
pub struct FaceId = u8;
}
#[derive(Serialize,Deserialize)]
#[serde(into="f64")]
#[serde(try_from="f64")]
-pub struct ZCoord(f64);
+pub struct ZCoord(pub f64);
// ---------- general data types ----------
pub players : DenseSlotMap<PlayerId,PlayerState>,
pub gen : Generation,
pub log : Vec<(Generation,Arc<LogEntry>)>,
+ pub max_z : ZCoord,
}
#[derive(Debug,Serialize,Deserialize)]
#[typetag::serde(tag="type")]
pub trait PieceSpec : Debug {
- fn load(self) -> Result<Box<dyn Piece>,SE>;
+ fn load(&self) -> Result<Box<dyn Piece>,SE>;
+ fn resolve_spec_face(&self, face : Option<FaceId>)
+ -> Result<FaceId,GameError>;
}
// ========== implementations ==========
pieces.insert(pr);
}
GameState { pieces, gen, players : Default::default(),
+ max_z: ZCoord(0.),
log : Default::default(), }
}
visible_slotmap_key!{ ClientId('C') }
-#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash,Deserialize)]
+#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash,Serialize,Deserialize)]
#[serde(transparent)]
pub struct RawToken (pub String);
pub use slotmap::dense::DenseSlotMap;
pub type SecondarySlotMap<K,V> = slotmap::secondary::SecondaryMap<K,V>;
-pub use index_vec::{define_index_type,index_vec,IndexVec};
+pub use index_vec::{define_index_type,index_vec,IndexVec,IndexSlice};
pub use vecdeque_stableix::StableIndexVecDeque;
pub use crate::commands::*;
pub use crate::slotmap_slot_idx::*;
pub use crate::cmdlistener::*;
+pub use crate::spec::*;
pub use crate::api::{Lens,TransparentLens};
+pub use crate::utils::OrdExt;
pub use libc::uid_t;
pub mod spec;
pub mod cmdlistener;
pub mod commands;
+pub mod utils;
#[path="slotmap-slot-idx.rs"] pub mod slotmap_slot_idx;
impl SimpleShape {
fn new_from_path(desc: String, path: String, approx_dia: Coord,
- mut faces: Vec<ColourSpec>) -> Result<Box<dyn Piece>,SE> {
+ faces: &IndexVec<FaceId,ColourSpec>)
+ -> Result<Box<dyn Piece>,SE> {
let scaled_path = svg_rescale_path(&path, SELECT_SCALE)?;
let colours = faces
- .iter_mut()
- .map(|s| mem::take(s).try_into())
+ .iter()
+ .map(|s| s.try_into())
.collect::<Result<_,SE>>()?;
Ok(Box::new(SimpleShape {
scaled_path, desc, approx_dia, path, colours,
#[repr(transparent)]
struct ColourSpec(String);
-impl TryFrom<ColourSpec> for Colour {
+impl TryFrom<&ColourSpec> for Colour {
type Error = SE;
#[throws(SE)]
- fn try_from(spec: ColourSpec) -> Colour {
+ fn try_from(spec: &ColourSpec) -> Colour {
// xxx check syntax
- spec.0
+ spec.0.clone()
}
}
#[derive(Debug,Serialize,Deserialize)]
struct Disc {
diam : Coord,
- faces : Vec<ColourSpec>,
+ faces : IndexVec<FaceId,ColourSpec>,
}
#[derive(Debug,Serialize,Deserialize)]
struct Square {
size : Vec<Coord>,
- faces : Vec<ColourSpec>,
+ faces : IndexVec<FaceId,ColourSpec>,
+}
+
+#[throws(GameError)]
+fn simple_resolve_spec_face(faces: &IndexSlice<FaceId,[ColourSpec]>,
+ face: Option<FaceId>)
+ -> FaceId {
+ let face = face.unwrap_or_default();
+ faces.get(face).ok_or(GameError::FaceNotFound)?;
+ face
}
#[typetag::serde]
impl PieceSpec for Disc {
#[throws(SE)]
- fn load(self) -> Box<dyn Piece> {
+ fn load(&self) -> Box<dyn Piece> {
let unit_path =
"M 0 1 a 1 1 0 1 0 0 -2 \
a 1 1 0 1 0 0 2 z";
let scale = (self.diam as f64) * 0.5;
let path = svg_rescale_path(&unit_path, scale)?;
SimpleShape::new_from_path("circle".to_owned(), path, self.diam,
- self.faces)?
+ &self.faces)?
+ }
+ #[throws(GameError)]
+ fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
+ simple_resolve_spec_face(&self.faces, face)?
}
}
#[typetag::serde]
impl PieceSpec for Square {
#[throws(SE)]
- fn load(self) -> Box<dyn Piece> {
+ fn load(&self) -> Box<dyn Piece> {
let (x, y) = match self.size.as_slice() {
&[s,] => (s,s),
&[x, y] => (x,y),
};
let path = format!("M {} {} h {} v {} h {} z",
-(x as f64)*0.5, -(y as f64)*0.5, x, y, -x);
- SimpleShape::new_from_path("square".to_owned(), path, (x+y+1)/2, self.faces)?
+ SimpleShape::new_from_path("square".to_owned(), path, (x+y+1)/2,
+ &self.faces)?
+ }
+ #[throws(GameError)]
+ fn resolve_spec_face(&self, face: Option<FaceId>) -> FaceId {
+ simple_resolve_spec_face(&self.faces, face)?
}
}
([ 90, 80 ],
Disc {
diam : 20,
- faces : vec![ ColourSpec("red".to_string()), ColourSpec("grey".to_string()) ],
+ faces : index_vec![ ColourSpec("red".to_string()), ColourSpec("grey".to_string()) ],
}.load()?),
([ 90, 60 ],
Square {
size : vec![20],
- faces : vec![ ColourSpec("blue".to_string()), ColourSpec("grey".to_string()) ],
+ faces : index_vec![ ColourSpec("blue".to_string()), ColourSpec("grey".to_string()) ],
}.load()?),
])
}
use crate::imports::*;
-#[derive(Debug,Deserialize)]
-struct GameSpec {
- table : Pos,
- players : Vec<PlayerSpec>,
- pieces : Vec<PiecesSpec>,
+#[derive(Debug,Serialize,Deserialize)]
+pub struct GameSpec {
+ pub table : Pos,
+ pub players : Vec<PlayerSpec>,
+ pub pieces : Vec<PiecesSpec>,
}
-#[derive(Debug,Deserialize)]
-struct PlayerSpec {
- nick: String,
+#[derive(Debug,Serialize,Deserialize)]
+pub struct PlayerSpec {
+ pub nick: String,
#[serde(flatten)]
- access: Box<dyn PlayerAccessSpec>,
+ pub access: Box<dyn PlayerAccessSpec>,
}
-#[derive(Debug,Deserialize)]
-struct PiecesSpec {
- pos : Option<Pos>,
- count : Option<u32>,
- name : Option<String>,
+#[derive(Debug,Serialize,Deserialize)]
+pub struct PiecesSpec {
+ pub pos : Option<Pos>,
+ pub posd : Option<Pos>,
+ pub count : Option<u32>,
+ pub face : Option<FaceId>,
#[serde(flatten)]
- info : Box<dyn PieceSpec>,
+ pub info : Box<dyn PieceSpec>,
}
-#[typetag::deserialize(tag="access")]
-trait PlayerAccessSpec : Debug {
+#[typetag::serde(tag="access")]
+pub trait PlayerAccessSpec : Debug {
#[throws(OE)]
fn make_token(&self) -> RawToken { RawToken::new_random()? }
fn deliver_token(&mut self) -> Result<(),OE>;
}
-#[typetag::deserialize]
+#[typetag::serde]
impl PlayerAccessSpec for RawToken {
#[throws(OE)]
fn make_token(&self) -> RawToken { self.clone() }
if next_len > buf.len() { break }
let tu = next.for_transmit(self.client);
+ // xxx handle overflow by allocating
write!(buf, "data: ")?;
serde_json::to_writer(&mut buf, &tu)?;
write!(buf, "\n\
lens: &dyn Lens) {
let gs = &mut self.g.gs;
+ // xxx check pos is within range, everywhere
+
let (update, piece) = match gs.pieces.byid_mut(piece) {
Ok(pc) => {
+ gs.max_z.update_max(pc.zlevel.z);
+
if self.by_client != pc.lastclient {
pc.gen_before_lastclient = pc.gen;
pc.lastclient = self.by_client;
--- /dev/null
+
+pub trait OrdExt : Ord + Sized {
+ fn update_max(&mut self, new: Self) {
+ if new > *self { *self = new }
+ }
+}
+impl<T> OrdExt for T where T : Ord + Sized {
+}