veloren/plugin/api/src/lib.rs
2021-03-02 09:45:15 +00:00

225 lines
7.0 KiB
Rust

pub extern crate common;
pub use common::comp::Health;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub use common::{resources::GameMode, uid::Uid};
mod errors;
pub use errors::*;
pub use event::*;
/// The [`Action`] enum represents a push modification that will be made in the
/// ECS in the next tick Note that all actions when sent are async and will not
/// be executed in order like [`Retrieve`] that are sync. All actions sent will
/// be executed in the send order in the ticking before the rest of the logic
/// applies.
///
/// # Usage:
/// ```rust
/// # use veloren_plugin_api::*;
/// # pub fn emit_action(action: Action) { emit_actions(vec![action]) }
/// # pub fn emit_actions(_actions: Vec<Action>) {}
/// // Packing actions is better than sending multiple ones at the same time!
/// emit_actions(vec![
/// Action::KillEntity(Uid(1)),
/// Action::PlayerSendMessage(Uid(0), "This is a test message".to_owned()),
/// ]);
/// // You can also use this to only send one action
/// emit_action(Action::KillEntity(Uid(1)));
/// ```
#[derive(Deserialize, Serialize, Debug)]
pub enum Action {
ServerClose,
Print(String),
PlayerSendMessage(Uid, String),
KillEntity(Uid),
}
/// The [`Retrieve`] enum represents read of the ECS is sync and blocking.
/// This enum shouldn't be used by itself. You should always prefer `get`
/// methods on Plugin API Types For instance, prefer this method:
/// ```rust
/// # use veloren_plugin_api::*;
/// # let entityid = Player {id: Uid(0)};
/// # trait G { fn get_entity_health(&self) -> Option<i64>; }
/// # impl G for Player {fn get_entity_health(&self) -> Option<i64> {Some(1)}}
/// let life = entityid.get_entity_health().unwrap();
/// // Do something with life
/// ```
/// Over this one:
/// ```rust
/// # use veloren_plugin_api::*;
/// # let entityid = Uid(0);
/// # fn retrieve_action(r: &Retrieve) -> Result<RetrieveResult, RetrieveError> { Ok(RetrieveResult::GetEntityHealth(Health::empty()))}
/// let life = if let RetrieveResult::GetEntityHealth(e) =
/// retrieve_action(&Retrieve::GetEntityHealth(entityid)).unwrap()
/// {
/// e
/// } else {
/// unreachable!()
/// };
/// // Do something with life
/// ```
#[derive(Deserialize, Serialize, Debug)]
pub enum Retrieve {
GetPlayerName(Uid),
GetEntityHealth(Uid),
}
/// The [`RetrieveResult`] struct is generated while using the `retrieve_action`
/// function
///
/// You should always prefer using `get` methods available in Plugin API types.
///
/// Example:
/// ```rust
/// # use veloren_plugin_api::*;
/// # let entityid = Uid(0);
/// # fn retrieve_action(r: &Retrieve) -> Result<RetrieveResult, RetrieveError> { Ok(RetrieveResult::GetEntityHealth(Health::empty()))}
/// let life = if let RetrieveResult::GetEntityHealth(e) =
/// retrieve_action(&Retrieve::GetEntityHealth(entityid)).unwrap()
/// {
/// e
/// } else {
/// unreachable!()
/// };
/// // Do something with life
/// ```
#[derive(Serialize, Deserialize, Debug)]
pub enum RetrieveResult {
GetPlayerName(String),
GetEntityHealth(Health),
}
/// This trait is implement by all events and ensure type safety of FFI.
pub trait Event: Serialize + DeserializeOwned + Send + Sync {
type Response: Serialize + DeserializeOwned + Send + Sync;
fn get_event_name(&self) -> String;
}
/// This module contains all events from the api
pub mod event {
use super::*;
use serde::{Deserialize, Serialize};
/// This event is called when a chat command is run.
/// Your event should be named `on_command_<Your command>`
///
/// If you return an Error the displayed message will be the error message
/// in red You can return a Vec<String> that will be print to player
/// chat as info
///
/// # Example
/// ```ignore
/// #[event_handler]
/// pub fn on_command_testplugin(command: ChatCommandEvent) -> Result<Vec<String>, String> {
/// Ok(vec![format!(
/// "Player of id {:?} named {} with {:?} sended command with args {:?}",
/// command.player.id,
/// command
/// .player
/// .get_player_name()
/// .expect("Can't get player name"),
/// command
/// .player
/// .get_entity_health()
/// .expect("Can't get player health"),
/// command.command_args
/// )])
/// }
/// ```
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct ChatCommandEvent {
pub command: String,
pub command_args: Vec<String>,
pub player: Player,
}
impl Event for ChatCommandEvent {
type Response = Result<Vec<String>, String>;
fn get_event_name(&self) -> String { format!("on_command_{}", self.command) }
}
/// This struct represent a player
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct Player {
pub id: Uid,
}
/// This event is called when a player connects.
/// Your event should be named `on_join`
///
/// You can either return `CloseConnection` or `None`
/// If `CloseConnection` is returned the player will be kicked
///
/// # Example
/// ```ignore
/// #[event_handler]
/// pub fn on_join(command: PlayerJoinEvent) -> PlayerJoinResult {
/// PlayerJoinResult::CloseConnection
/// }
/// ```
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct PlayerJoinEvent {
pub player_name: String,
pub player_id: [u8; 16],
}
impl Event for PlayerJoinEvent {
type Response = PlayerJoinResult;
fn get_event_name(&self) -> String { "on_join".to_owned() }
}
/// This is the return type of an `on_join` event. See [`PlayerJoinEvent`]
///
/// Variants:
/// - `CloseConnection` will kick the player.
/// - `None` will let the player join the server.
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum PlayerJoinResult {
Kick(String),
None,
}
impl Default for PlayerJoinResult {
fn default() -> Self { Self::None }
}
/// This event is called when the plugin is loaded
/// Your event should be named `on_load`
///
/// # Example
/// ```ignore
/// #[event_handler]
/// pub fn on_load(load: PluginLoadEvent) {
/// match load.game_mode {
/// GameMode::Server => emit_action(Action::Print("Hello, server!".to_owned())),
/// GameMode::Client => emit_action(Action::Print("Hello, client!".to_owned())),
/// GameMode::Singleplayer => emit_action(Action::Print("Hello, singleplayer!".to_owned())),
/// }
/// }
/// ```
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct PluginLoadEvent {
pub game_mode: GameMode,
}
impl Event for PluginLoadEvent {
type Response = ();
fn get_event_name(&self) -> String { "on_load".to_owned() }
}
// impl Default for PlayerJoinResult {
// fn default() -> Self {
// Self::None
// }
// }
}