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}