Needs a little work still, and not used.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
config().game_rng.set_fake(ents, superuser)?;
Fine
}
+
+ MC::SetFakeTime(fspec) => {
+ let superuser = cs.superuser()
+ .ok_or(ME::SuperuserAuthorisationRequired)?;
+ config().global_clock.set_fake(fspec, superuser)?;
+ Fine
+ }
}))();
let resp = match resp {
player_info_pane: Html,
bundles_info_pane: Html,
fake_rng: bool,
+ fake_time: bool,
}
#[derive(Debug,Serialize)]
ptoken: form.ptoken.clone(),
links: (&*ig.links).into(),
fake_rng: config().game_rng.is_fake(),
+ fake_time: config().global_clock.is_fake(),
load: serde_json::to_string(&DataLoad {
movehist,
players: load_players,
SshReinstallKeys, // managment only
LoadFakeRng(Vec<String>),
+ SetFakeTime(FakeTimeSpec),
}
//---------- Accounts file ----------
#[error("TOML syntax error: {0}")] TomlSyntaxError(String),
#[error("TOML structure error: {0}")] TomlStructureError(String),
#[error("RNG is real, command not supported")] RngIsReal,
+ #[error("Time is real, command not supported")] TimeIsReal,
#[error("upload truncated")] UploadTruncated,
#[error("upload corrupted")] UploadCorrupted,
#[error("too many bundles")] TooManyBundles,
pub authorized_keys_include: Option<String>,
pub debug_js_inject_file: Option<String>,
#[serde(default)] pub fake_rng: FakeRngSpec,
+ #[serde(default)] pub fake_time: FakeTimeConfig,
/// Disable this for local testing only. See LICENCE.
pub check_bundled_sources: Option<bool>,
}
pub debug_js_inject: Arc<String>,
pub check_bundled_sources: bool,
pub game_rng: RngWrap,
+ pub global_clock: GlobalClock,
pub prctx: PathResolveContext,
}
http_port, listen, public_url, sse_wildcard_url,
template_dir, specs_dir, nwtemplate_dir, wasm_dir, libexec_dir, usvg_bin,
log, bundled_sources, shapelibs, sendmail,
- debug_js_inject_file, check_bundled_sources, fake_rng,
+ debug_js_inject_file, check_bundled_sources, fake_rng, fake_time,
ssh_proxy_command, ssh_proxy_user, ssh_restrictions, authorized_keys,
authorized_keys_include,
} = self;
let game_rng = fake_rng.make_game_rng();
+ let global_clock = fake_time.make_global_clock();
let home = || env::var("HOME").context("HOME");
let prctx = if let Some(ref cd) = change_directory {
listen, public_url, sse_wildcard_url,
template_dir, specs_dir, nwtemplate_dir, wasm_dir, libexec_dir,
bundled_sources, shapelibs, sendmail, usvg_bin,
- debug_js_inject, check_bundled_sources, game_rng, prctx,
+ debug_js_inject, check_bundled_sources, game_rng, global_clock, prctx,
ssh_proxy_bin, ssh_proxy_uid, ssh_restrictions,
authorized_keys, authorized_keys_include,
};
--- /dev/null
+// Copyright 2020-2021 Ian Jackson and contributors to Otter
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// There is NO WARRANTY.
+
+use crate::prelude::*;
+
+use parking_lot::Mutex;
+
+type Millis = u32;
+type Micros = u64;
+
+#[derive(Deserialize,Debug,Clone,Default)]
+#[serde(transparent)]
+pub struct FakeTimeConfig(Option<FakeTimeSpec>);
+
+#[derive(Deserialize,Serialize,Debug,Clone,Default)]
+#[serde(transparent)]
+pub struct FakeTimeSpec(Option<Millis>);
+
+#[derive(Debug)]
+pub struct GlobalClock {
+ fakeable: Option<Mutex<Option<FakeClock>>>,
+}
+
+#[derive(Debug)]
+struct FakeClock {
+ start: Instant,
+ current: Micros,
+}
+
+impl FakeClock {
+ fn from_millis(ms: Millis) -> FakeClock { FakeClock {
+ start: Instant::now(),
+ current: (ms as Micros) * 1000,
+ } }
+
+ fn from_spec(FakeTimeSpec(fspec): FakeTimeSpec) -> Option<FakeClock> {
+ fspec.map(FakeClock::from_millis)
+ }
+}
+
+impl FakeTimeConfig {
+ pub fn make_global_clock(self) -> GlobalClock {
+ let fakeable = self.0.map(|fspec| FakeClock::from_spec(fspec).into());
+ GlobalClock { fakeable }
+ }
+}
+
+impl GlobalClock {
+ pub fn now(&self) -> Instant {
+ self.now_fake().unwrap_or_else(|| Instant::now())
+ }
+
+ #[throws(as Option)]
+ fn now_fake(&self) -> Instant {
+ let mut guard = self.fakeable.as_ref()?.lock();
+ let fake = guard.as_mut()?;
+ fake.current += 1;
+ fake.start + Duration::from_micros(fake.current)
+ }
+
+ #[throws(MgmtError)]
+ pub fn set_fake(&self, fspec: FakeTimeSpec, _: AuthorisationSuperuser) {
+ let mut guard = self.fakeable.as_ref().ok_or(ME::TimeIsReal)?.lock();
+ *guard = FakeClock::from_spec(fspec)
+ }
+
+ pub fn is_fake(&self) -> bool {
+ self.fakeable.is_some()
+ }
+}
#[path = "slotmap-slot-idx.rs"] pub mod slotmap_slot_idx;
#[path = "toml-de.rs"] pub mod toml_de;
#[path = "fake-rng.rs"] pub mod fake_rng;
+#[path = "fake-time.rs"] pub mod fake_time;
pub use crate::debugreader::DebugReader;
pub use crate::error::*;
pub use crate::fake_rng::*;
+pub use crate::fake_time::*;
pub use crate::gamestate::*;
pub use crate::global::*;
pub use crate::hidden::*;
{% macro status() %}
{%- if fake_rng %}<strong>FAKING RANDOMNESS!</strong>{% endif %}
+{%- if fake_time %}<strong>FAKING TIME!</strong>{% endif %}
<span id="status">nothing</span>
{% endmacro status %}