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