screeps_async/lib.rs
1#![warn(missing_docs)]
2//! A tick-aware async runtime for Screeps
3//!
4//! # Getting Started
5//!
6//! Add `screeps-async` to your `Cargo.toml`
7//! ```toml
8//! [dependencies]
9//! screeps-async = "0.3.1"
10//! ```
11//!
12//! # The [`#[screeps_async::main]`](main) macro
13//! ```
14//! #[screeps_async::main]
15//! pub fn game_loop() {
16//! // Tick logic that spawns some tasks
17//! screeps_async::spawn(async {
18//! println!("Hello!");
19//! });
20//! }
21//! ```
22//!
23//! This expands to roughly the following:
24//!
25//! ```
26//! pub fn game_loop() {
27//! // Tick logic that spawns some tasks
28//! screeps_async::spawn(async {
29//! println!("Hello!");
30//! });
31//!
32//! screeps_async::run().unwrap();
33//! }
34//! ```
35
36pub mod macros;
37pub use macros::*;
38
39use std::cell::RefCell;
40pub mod error;
41pub mod job;
42pub mod runtime;
43pub mod sync;
44pub mod time;
45
46use crate::error::RuntimeError;
47use crate::job::JobHandle;
48use crate::runtime::{Builder, ScreepsRuntime};
49use std::future::Future;
50
51thread_local! {
52 /// The current runtime
53 pub static CURRENT: RefCell<Option<ScreepsRuntime>> =
54 const { RefCell::new(None) };
55}
56
57/// Configures the runtime with default settings. Must be called only once
58///
59/// To use custom settings, create a [Builder] with [Builder::new], customize as needed,
60/// then call [Builder::apply]
61///
62/// # Panics
63///
64/// This function panics if there is already a runtime initialized
65pub fn initialize() {
66 Builder::new().apply()
67}
68
69/// Run the task executor for one tick
70///
71/// This is just shorthand for:
72/// ```no_run
73/// # let _ = // avoid warnings from not capturing returned value
74/// screeps_async::with_runtime(|runtime| {
75/// runtime.run()
76/// });
77/// ```
78///
79/// # Panics
80///
81/// This function panics if the current runtime is not set
82pub fn run() -> Result<(), RuntimeError> {
83 with_runtime(|runtime| runtime.run())
84}
85
86/// The main entrypoint for the async runtime. Runs a future to completion.
87///
88/// See [ScreepsRuntime::block_on] for details
89pub fn block_on<F>(future: F) -> Result<F::Output, RuntimeError>
90where
91 F: Future + 'static,
92{
93 with_runtime(|runtime| runtime.block_on(future))
94}
95
96/// Spawn a new async task
97///
98/// # Panics
99///
100/// This function panics if the current runtime is not set
101pub fn spawn<F>(future: F) -> JobHandle<F::Output>
102where
103 F: Future + 'static,
104{
105 with_runtime(|runtime| runtime.spawn(future))
106}
107
108/// Acquire a reference to the [ScreepsRuntime].
109///
110/// # Panics
111///
112/// This function panics if the current runtime is not set
113pub fn with_runtime<F, R>(f: F) -> R
114where
115 F: FnOnce(&ScreepsRuntime) -> R,
116{
117 CURRENT.with_borrow(|runtime| {
118 let runtime = runtime
119 .as_ref()
120 .expect("No screeps_async runtime configured");
121 f(runtime)
122 })
123}
124
125#[cfg(not(test))]
126mod utils {
127 use screeps::game;
128
129 pub(super) fn game_time() -> u32 {
130 game::time()
131 }
132
133 /// Returns the percentage of tick time used so far
134 pub(super) fn time_used() -> f64 {
135 game::cpu::get_used() / game::cpu::tick_limit()
136 }
137}
138
139#[cfg(test)]
140mod utils {
141 pub(super) use super::tests::*;
142}
143
144#[cfg(test)]
145mod tests {
146 use crate::error::RuntimeError;
147 use crate::runtime::Builder;
148 use std::cell::RefCell;
149
150 thread_local! {
151 pub(crate) static GAME_TIME: RefCell<u32> = const { RefCell::new(0) };
152 pub(crate) static TIME_USED: RefCell<f64> = const { RefCell::new(0.0) };
153 }
154
155 pub(super) fn game_time() -> u32 {
156 GAME_TIME.with_borrow(|t| *t)
157 }
158
159 pub(super) fn time_used() -> f64 {
160 TIME_USED.with_borrow(|t| *t)
161 }
162
163 pub(crate) fn init_test() {
164 GAME_TIME.with_borrow_mut(|t| *t = 0);
165 TIME_USED.with_borrow_mut(|t| *t = 0.0);
166
167 Builder::new().apply()
168 }
169
170 /// Calls [crate::run] and increments [GAME_TIME] if [crate::run] succeeded
171 pub(crate) fn tick() -> Result<(), RuntimeError> {
172 crate::run()?;
173 GAME_TIME.with_borrow_mut(|t| *t += 1);
174 Ok(())
175 }
176}