quill_prototype/
lib.rs

1//! A prototype API for `quill`, Feather's plugin API.
2//!
3//! # Concepts
4//! * Feather is based on [the ECS architecture], an alternative
5//! to classic object-oriented game architecture which is generally
6//! more flexible and slightly more performant.
7//! * Feather plugins compile to WebAssembly and are run in a sandboxed
8//! environment.
9//!
10//! # Getting started
11//! Install `cargo-quill`, a command line tool to help build
12//! and test Feather plugins:
13//! ```bash
14//! cargo install cargo-quill
15//! ```
16//!
17//! To start on a new plugin, run `cargo quill init myplugin`.
18//! `cargo-quill` creates a new directory called `myplugin` and
19//! fills it with a starter template.
20//!
21//! Let's take a look at the directory structure:
22//! ```
23//! myplugin
24//! ├── Cargo.toml
25//! └── src
26//!    └── main.rs
27//! ```
28
29use std::{marker::PhantomData, ops::Deref, ops::DerefMut};
30use uuid::Uuid;
31
32/// (Would be reexported from `feather-blocks` in the final product)
33pub struct BlockId;
34
35/// Result type returned by most functions.
36pub type SysResult<T = ()> = anyhow::Result<T>;
37
38/// A type that can be associated with an entity as a component.
39///
40/// Components are a critical part of the ECS architecture;
41/// refer to [this guide] for mroe information.
42///
43/// This trait is automatically implemented for most types.
44///
45/// # Persistence
46/// Custom components are removed from entities when your
47/// plugin is unloaded. We will offer a component persistence
48/// API in the future.
49pub trait Component: Send + Sync + 'static {}
50impl<T> Component for T where T: Send + Sync + 'static {}
51
52/// Error returned when attempting to get a component from an entity.
53#[derive(Debug, thiserror::Error)]
54pub enum ComponentError {
55    /// The entity no longer exists, e.g. because it was despawned
56    /// or killed.
57    #[error("entity has been despawned")]
58    EntityDead,
59    /// The entity does not have the component requested.
60    #[error("entity does not have a component of type '{0}0")]
61    MissingComponent(&'static str),
62}
63
64/// An RAII guard to a component reference.
65pub struct ComponentRef<T> {
66    _todo: PhantomData<T>,
67    _not_send_sync: PhantomData<*mut ()>,
68}
69
70impl<T> Deref for ComponentRef<T> {
71    type Target = T;
72
73    fn deref(&self) -> &Self::Target {
74        todo!()
75    }
76}
77
78/// An RAII guard to a mutable reference to a component.
79pub struct ComponentRefMut<T> {
80    _todo: PhantomData<T>,
81    _not_send_sync: PhantomData<*mut ()>,
82}
83
84impl<T> Deref for ComponentRefMut<T> {
85    type Target = T;
86
87    fn deref(&self) -> &Self::Target {
88        todo!()
89    }
90}
91
92impl<T> DerefMut for ComponentRefMut<T> {
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        todo!()
95    }
96}
97
98/// A handle to an entity. Provides access to components.
99///
100/// This handle cannot be sent to another thread, as it requires
101/// access to the main server state. To persist entity handles across
102/// thread boundaries, you may use [`EntityId`] instead.
103#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
104pub struct EntityRef {
105    _not_send_sync: PhantomData<*mut ()>,
106}
107
108impl EntityRef {
109    /// Gets a component of this entity.
110    ///
111    /// Returns an error if the entity does not have this component
112    /// or if the entity no longer exists.
113    pub fn get<T: Component>(self) -> Result<ComponentRef<T>, ComponentError> {
114        todo!()
115    }
116
117    /// Gets a mutable reference to a component of this entity.
118    ///
119    /// Returns an error if the entity does not have this component
120    /// or if the entity no longer exists.
121    pub fn get_mut<T: Component>(self) -> Result<ComponentRefMut<T>, ComponentError> {
122        todo!()
123    }
124
125    /// Adds a component to this entity.
126    ///
127    /// If the entity already has a component of type `T`,
128    /// then the component is overwritten.
129    pub fn add<T: Component>(self, _component: T) -> Result<(), EntityDead> {
130        todo!()
131    }
132
133    /// Sends a message to this entity.
134    ///
135    /// Returns an error if this entity cannot receive a message.
136    /// (Usually, only players and the console are capable of receiving messages.)
137    pub fn send_message(self, _message: &str) -> Result<(), ComponentError> {
138        todo!()
139    }
140
141    /// Returns the ID of this entity. This ID can be stored
142    /// for later use, usually through [`State::entity`].
143    pub fn id(self) -> EntityId {
144        todo!()
145    }
146
147    /// Returns this entity's `Position` component.
148    pub fn position(self) -> Result<Position, ComponentError> {
149        todo!()
150    }
151
152    /// Returns this entity's `Name` component.
153    pub fn name(self) -> Result<Name, ComponentError> {
154        todo!()
155    }
156
157    /// Returns this entity's `Uuid` component.
158    pub fn uuid(self) -> Result<Uuid, ComponentError> {
159        todo!()
160    }
161}
162
163/// An opaque, unique ID of an entity. Can be used with [`State::entity`] to get
164/// an [`EntityRef`] which allows access to an entity's components.
165#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
166pub struct EntityId(u32);
167
168/// A position in world space.
169#[derive(Copy, Clone, Debug)]
170pub struct Position {
171    pub x: f64,
172    pub y: f64,
173    pub z: f64,
174    pub pitch: f32,
175    pub yaw: f32,
176    pub on_ground: bool,
177}
178
179/// Position of a block in world space.
180///
181/// Like [`Position`], but coordinates are always integers.
182#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
183pub struct BlockPosition {
184    pub x: i32,
185    pub y: i32,
186    pub z: i32,
187}
188
189/// The name of an entity.
190#[derive(Clone, Debug, PartialEq, Eq, Hash)]
191pub struct Name(pub String); // TODO: evaluate heap allocation
192
193/// Error returned when an entity no longer exists.
194#[derive(Debug, thiserror::Error)]
195#[error("entity has been despawned")]
196pub struct EntityDead;
197
198/// Error returned when a block cannot be accessed because
199/// its chunk is not loaded.
200#[derive(Debug, thiserror::Error)]
201#[error("block chunk is unloaded")]
202pub struct ChunkNotLoaded;
203
204/// Error returned when a resource of some type does not exist.
205#[derive(Debug, thiserror::Error)]
206#[error("missing resource of type '{0}'")]
207pub struct MissingResource(&'static str);
208
209/// A type that can be stored in [`State`] as a "resource."
210///
211/// Resources are useful for plugin state that is not directly
212/// related to a specific entity. For example, you might store
213/// your plugin's configuration in a `Resource`.
214///
215/// Use [`State::resource`] and [`State::resource_mut`] to access resources.
216/// Similarly, use [`Setup::resource`] to create a resource when your plugin
217/// is loaded.
218///
219/// This trait is automatically implemented for most types.
220pub trait Resource: Send + Sync + 'static {}
221impl<T> Resource for T where T: Send + Sync + 'static {}
222
223/// The main server state.
224///
225/// This struct is passed to systems and event handlers. It
226/// provides access to entities, components, blocks, resources,
227/// etc.
228#[derive(Debug)]
229pub struct State;
230
231impl State {
232    /// Gets an [`EntityRef`] from an [`EntityId`].
233    ///
234    /// Returns an error if the entity no longer exists.
235    pub fn entity(&self, _id: EntityId) -> Result<EntityRef, EntityDead> {
236        todo!()
237    }
238
239    /// Gets the block at the given position.
240    ///
241    /// Returns an error if the block's chunk is not loaded.
242    pub fn block(&self, _pos: BlockPosition) -> Result<BlockId, ChunkNotLoaded> {
243        todo!()
244    }
245
246    /// Sets the block at the given position.
247    ///
248    /// Returns an error if the block's chunk is not loaded.
249    pub fn set_block(&self, _pos: BlockPosition, _block: BlockId) -> Result<(), ChunkNotLoaded> {
250        todo!()
251    }
252
253    /// Gets a reference to a resource.
254    ///
255    /// Returns an error if the resource does not exist.
256    pub fn resource<T: Resource>(&self) -> Result<&T, MissingResource> {
257        todo!()
258    }
259
260    /// Gets a mutable reference to a resource.
261    ///
262    /// Returns an error if the resource does not exist.
263    pub fn resource_mut<T: Resource>(&self) -> Result<&mut T, MissingResource> {
264        todo!()
265    }
266}
267
268impl State {
269    /// Gets an online player by their username.
270    pub fn player_by_name(&self, _name: &str) -> Option<EntityRef> {
271        todo!()
272    }
273
274    /// Gets an online entity by their UUID.
275    ///
276    /// This function works for all entities, not just players.
277    pub fn entity_by_uuid(&self, _uuid: Uuid) -> Option<EntityRef> {
278        todo!()
279    }
280}
281
282/// A function that can be used as a command executor.
283///
284/// This is a temporary Bukkit-like command API. We'll figure
285/// out a more structured approach to commands (like Brigadier)
286/// in the future.
287pub type CommandExecutor = fn(state: &mut State, sender: EntityRef, args: &[&str]) -> SysResult;
288
289/// A builder for a command.
290///
291/// # Example
292/// A basic `/msg` command:
293/// ```no_run
294/// # fn main() -> anyhow::Result<()> {
295/// use quill_prototype::{CommandBuilder, State, EntityRef, SysResult};
296///
297/// # let mut setup = quill_prototype::Setup;
298///
299/// CommandBuilder::new("msg")
300///     .alias("m")
301///     .build(on_msg, &mut setup);
302///
303/// fn on_msg(state: &mut State, sender: EntityRef, args: &[&str]) -> SysResult {
304///     if args.len() < 1 {
305///         sender.send_message("Usage: /msg <player> [message...]")?;
306///         // Note that a command executor should only return an `Err`
307///         // when an internal error occurs, not when user input is incorrect.
308///         // Returning an error will
309///         // send the player a "an internal error occurred while trying to execute
310///         // this command" message and print the error to the console.
311///         // Returning an `Err` is like throwing
312///         // an exception in Bukkit.
313///         return Ok(());
314///     }
315///     let target = match state.player_by_name(args[0]) {
316///         Some(player) => player,
317///         None => {
318///             sender.send_message(&format!("Player {} not found.", args[0]));
319///             return Ok(());
320///         }
321///     };
322///     
323///     // Build the message by accumulating `args`.
324///     let mut message = String::new();
325///     for arg in &args[1..] {
326///         message += arg;
327///         message.push(' ');
328///     }
329///     target.send_message(&message)?;
330///     Ok(())
331/// }
332/// # Ok(())
333/// # }
334/// ```
335#[derive(Debug)]
336pub struct CommandBuilder;
337
338impl CommandBuilder {
339    /// Creates a new `CommandBuilder` for a command with
340    /// the given name.
341    ///
342    /// # Example
343    /// Create a `CommandBuilder` for the `/msg` command:
344    /// ```no_run
345    /// use quill_prototype::CommandBuilder;
346    /// CommandBuilder::new("msg");
347    /// ```
348    /// (Note the absence of a preceding slash.)
349    pub fn new(_name: &str) -> Self {
350        todo!()
351    }
352
353    /// Adds an alias for this command.
354    pub fn alias(&mut self, _alias: &str) -> &mut Self {
355        todo!()
356    }
357
358    /// Builds this `CommandBuilder`.
359    ///
360    /// This function takes:
361    /// * The function called when the command is executed
362    /// * The [`Setup`] passed to your plugin's `setup` function
363    /// and registers the command with the server.
364    pub fn build(&mut self, _executor: CommandExecutor, _setup: &mut Setup) {
365        todo!()
366    }
367}
368
369/// A system, the `S` part of the ECS.
370///
371/// System functions are invoked each tick.
372///
373// # Errors
374/// A system returns a [`SysResult`], which is a `Result<(), anyhow::Error>`.
375/// (See [`anyhow`](https://docs.rs/anyhow).)
376///
377/// Returning an error should be preferred to panicking, as it allows
378/// for more graceful recovery. If your plugin panics, then the server
379/// will have to reload it as its internal state can no longer be considered
380/// intact. On the other hand, returning an error from a system will result
381/// in a console message, but it typically won't cause anything to break.
382pub type System = fn(&mut State) -> SysResult;
383
384/// Struct passed to your plugin's `setup` function which
385/// is called when it's loaded.
386///
387/// Use this struct to register systems, resources, components,
388/// commands, event handlers, etc.
389#[derive(Debug)]
390pub struct Setup;
391
392impl Setup {
393    /// Registers a resource. The resource can later
394    /// be accessed via [`State::resource`].
395    pub fn resource(&mut self, _resource: impl Resource) -> &mut Self {
396        todo!()
397    }
398
399    /// Registers a system.
400    /// The `system` function will be invoked each tick.
401    pub fn system(&mut self, _system: System) -> &mut Self {
402        todo!()
403    }
404}