From 9c3aaafc7d8f6c79f1678841e2580c99b5d78c51 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 16 Mar 2021 19:45:03 +0000 Subject: [PATCH] clock: Much wip code, compiles but doesn't do anything yet Signed-off-by: Ian Jackson --- specs/penultima.game.toml | 1 + src/clock.rs | 201 ++++++++++++++++++++++++++++++++++++-- src/prelude.rs | 2 +- src/spec.rs | 1 + 4 files changed, 198 insertions(+), 7 deletions(-) diff --git a/specs/penultima.game.toml b/specs/penultima.game.toml index d55e4f92..082cb033 100644 --- a/specs/penultima.game.toml +++ b/specs/penultima.game.toml @@ -196,3 +196,4 @@ shape.xy = [20,10] [[pieces]] pos = [240, 100] type = "ChessClock" +time = 900 diff --git a/src/clock.rs b/src/clock.rs index 8d859e67..eb0d2640 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -2,27 +2,208 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // There is NO WARRANTY. +#![allow(dead_code)] + use crate::prelude::*; use shapelib::Rectangle; -const W: Coord = 50; -const H: Coord = 20; -const OUTLINE: Rectangle = Rectangle { xy: PosC([W as f64, H as f64]) }; +// ==================== users ==================== + +struct UserInfo { + idchar: char, +} + +const N: usize = 2; + +type Time = i32; + +#[derive(Copy,Clone,Serialize,Deserialize)] +#[derive(Eq,Ord,PartialEq,PartialOrd,Hash)] +#[serde(try_from="u8", into="u8")] +struct User(bool); + +impl Index for [T;2] { + type Output = T; + fn index(&self, index: User) -> &T { &self[index.0 as usize] } +} +impl IndexMut for [T;2] { + fn index_mut(&mut self, index: User) -> &mut T { &mut self[index.0 as usize] } +} + +const USERINFOS: [UserInfo; N] = [ + UserInfo { idchar: 'x' }, + UserInfo { idchar: 'y' }, +]; + +const USERS: [User; N] = [ User(false), User(true) ]; + +impl fmt::Debug for User { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_char(USERINFOS[*self].idchar) + } +} + +#[derive(Debug,Clone,Copy,Error,Serialize,Deserialize)] +struct UserOutOfRangeError; +display_as_debug!{UserOutOfRangeError} + +impl TryFrom for User { + type Error = UserOutOfRangeError; + #[throws(UserOutOfRangeError)] + fn try_from(u: u8) -> User { User(match u { + 0 => false, + 1 => true, + _ => throw!(UserOutOfRangeError), + }) } +} + +impl From for u8 { + fn from(user: User) -> u8 { user.0 as u8 } +} + +// ==================== state ==================== #[derive(Debug,Clone,Serialize,Deserialize)] pub struct ChessClock { // spec + time: Time, + #[serde(default)] per_move: usize, } #[derive(Debug,Serialize,Deserialize)] struct Clock { // state + spec: ChessClock, +} + +#[derive(Debug,Serialize,Deserialize,Default)] +struct State { + users: [UState; 2], + #[serde(skip)] running: Option, } +#[typetag::serde(name="Hand")] +impl PieceXData for State { + fn dummy() -> Self { default() } +} + + +#[derive(Debug,Default,Serialize,Deserialize)] +struct UState { + player: PlayerId, + remaining: Time, // -ve means flag +} + +#[derive(Debug,Serialize,Deserialize)] +struct Running { + user: User, + #[serde(with="timespec_serde")] next_wakeup: TimeSpec, +} + +mod timespec_serde { + use super::*; + + #[derive(Serialize, Deserialize)] + struct Timespec(i64, u32); + + #[throws(S::Error)] + pub fn serialize(v: &TimeSpec, s: S) -> S::Ok { + let v = Timespec(v.tv_sec().into(), v.tv_nsec().try_into().unwrap()); + Serialize::serialize(&v, s)? + } + #[throws(D::Error)] + pub fn deserialize<'de, D:Deserializer<'de>>(d: D) -> TimeSpec { + let Timespec(sec, nsec) = Deserialize::deserialize(d)?; + libc::timespec { tv_sec: sec.into(), tv_nsec: nsec.into() }.into() + } +} + +// ==================== rendering, abstract ==================== + +#[derive(Debug)] +struct URender<'r> { + st: URenderState, + remaining: Time, // always >=0 + nick: &'r str, +} + +#[derive(Debug,Copy,Clone)] +#[derive(Eq,Ord,PartialEq,PartialOrd,Hash)] +enum URenderState { + Running, + ActiveHeld, + Inactive, + Stopped, + Reset, + Flag, + PMissing, +} +use URenderState as URS; + +impl Clock { + fn urender<'r>(&self, state: &State, gplayers: &'r GPlayers, gpc: &GPiece) + -> [URender<'r>; N] + { + let mut r: [URender;N] = izip!( + USERS.iter(), + state.users.iter() + ).map(|(&user, ustate)| { + let nick = gplayers.get(ustate.player).map(|gpl| gpl.nick.as_str()); + let (st, remaining, nick) = + if ustate.remaining < 0 { + (URS::Flag, 0, nick.unwrap_or("")) + } else if let Some(nick) = nick { + ( + if let Some(running) = &state.running { + if running.user != user { + URS::Inactive + } else if gpc.held.is_some() { + URS::ActiveHeld + } else { + URS::Running + } + } else if ustate.remaining == self.spec.time { + URS::Reset + } else { + URS::Stopped + }, + ustate.remaining, + nick + ) + } else { + (URS::PMissing, ustate.remaining, "") + }; + + URender { st, remaining, nick } + }) + .collect::>() + .into_inner().unwrap(); + + if r.iter().filter(|ur| ur.st == URS::Reset).count() == 1 { + for ur in &mut r { + if ur.st == URS::Reset { ur.st = URS::Stopped } + } + } + + r + } +} + +// ==================== rendering ==================== + +const W: Coord = 50; +const H: Coord = 20; +const OUTLINE: Rectangle = Rectangle { xy: PosC([W as f64, H as f64]) }; + + +// ==================== piece management, loading, etc. ==================== + #[typetag::serde] impl PieceSpec for ChessClock { #[throws(SpecError)] fn load(&self, _: usize, _gpc: &mut GPiece) -> PieceSpecLoaded { - let clock = Clock { + if self.time <= 0 { throw!(SpecError::NegativeTimeout) } + let clock = Clock { + spec: self.clone(), }; PieceSpecLoaded { p: Box::new(clock), @@ -50,8 +231,16 @@ impl PieceTrait for Clock { fn svg_piece(&self, f: &mut Html, _gpc: &GPiece, _gs: &GameState, id: VisiblePieceId) { dbgc!("rendering", id); write!( &mut f.0, r##" - - "##)?; + + "##, W, H + )?; +/* + let urs = + + dbg + for (i,u) in USERS.iter().enumerate() { + + }*/ } #[throws(IE)] diff --git a/src/prelude.rs b/src/prelude.rs index 1cdfe54c..2108ff23 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -32,7 +32,7 @@ pub use std::iter::repeat_with; pub use std::marker::PhantomData; pub use std::mem; pub use std::num::{NonZeroUsize, TryFromIntError, Wrapping}; -pub use std::ops::{Deref, DerefMut, Index}; +pub use std::ops::{Deref, DerefMut, Index, IndexMut}; pub use std::os::linux::fs::MetadataExt; // todo why linux for st_mode?? pub use std::os::unix; pub use std::os::unix::ffi::OsStrExt; diff --git a/src/spec.rs b/src/spec.rs index 9d31a824..bb963c56 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -83,6 +83,7 @@ pub enum SpecError { InconsistentFacesEdgecoloursCount, SpecifiedWidthOfNoEdges, UnsupportedShape, + NegativeTimeout, } display_as_debug!{SpecError} -- 2.30.2