levilamina: lifecycle, logging and errors
levilamina: writing LeviLamina mods in safe Rust.
| Rust |
|---|
| struct Hello;
impl LeviMod for Hello {
fn on_load(ctx: &ModContext) -> Result<Self> { Ok(Hello) }
}
levilamina::register_mod!(Hello);
|
On top of levilamina_sys, the cell-for-cell mirror of sdk/abi.h, it does four things
and no more: the two slot gates, string handling, the panic fence, and copying inside a
sink (contract Β§3).
It caches no host state, feigns no synchronization and covers for the host in nothing: a
failure is an Err that says why and never quietly becomes a default (Β§5.1).
prelude
| Rust |
|---|
| pub mod prelude {
pub use crate::{event::names, service};
pub use crate::{
register_mod, Block, ConnectionState, Container, Direction, Directions, Entity, Error,
Event, EventRef, GameMode, GamingStatus, Host, ItemStack, LeviMod, Listener, LogLevel,
Logger, ModContext, NbtValue, Packet, PacketHook, Packets, Player, PlayerIdentity,
PlayerSel, Priority, Result, Server, TaskId, Verdict, Wiring, World,
};
}
|
One use levilamina::prelude::*; brings in what writing a mod needs most.
Logger
| Rust |
|---|
| pub struct Logger(/* private */);
|
Logger::get
Logger::log
| Rust |
|---|
| pub fn log(&self, level: LogLevel, msg: &str)
|
- Parameters:
- level :
LogLevel
- msg :
&str
Logger::fatal
| Rust |
|---|
| pub fn fatal(&self, msg: &str)
|
Logger::error
| Rust |
|---|
| pub fn error(&self, msg: &str)
|
Logger::warn
| Rust |
|---|
| pub fn warn(&self, msg: &str)
|
Logger::info
| Rust |
|---|
| pub fn info(&self, msg: &str)
|
Logger::debug
| Rust |
|---|
| pub fn debug(&self, msg: &str)
|
Logger::trace
| Rust |
|---|
| pub fn trace(&self, msg: &str)
|
ModContext
| Rust |
|---|
| pub struct ModContext(/* private */);
|
The context handed to a mod's lifecycle callbacks.
It carries no state of its own, since the real state lives in RUNTIME. It exists to
give the facades one common entry point, and to make a signature such as
on_load(ctx) read sensibly.
ModContext::logger
| Rust |
|---|
| pub fn logger(&self) -> crate::Logger
|
- Return type:
crate::Logger
ModContext::host
| Rust |
|---|
| pub fn host(&self) -> crate::Host
|
Capabilities at the host and system level: the run stage, scheduling, executing
commands and the protocol version.
ModContext::packets
| Rust |
|---|
| pub fn packets(&self) -> crate::Packets
|
The packet facade.
- Return type:
crate::Packets
ModContext::world
| Rust |
|---|
| pub fn world(&self) -> crate::World
|
The world facade.
- Return type:
crate::World
ModContext::server
| Rust |
|---|
| pub fn server(&self) -> crate::Server
|
Server runtime control: freezing and warping ticks, and performance sampling.
- Return type:
crate::Server
ModContext::host_is_client
| Rust |
|---|
| pub fn host_is_client(&self) -> bool
|
Whether the host was built for the client target.
Rarely needed, since a mod loaded onto the wrong target is refused by the host during
the handshake. It exists so that one source can make a small behavioral distinction
between the two targets without a compile-time feature.
ModContext::host_abi
| Rust |
|---|
| pub fn host_abi(&self) -> (u32, usize)
|
The ABI version and table length of the host. For diagnostics: reporting that a pier is
too old for a feature only tells someone how far to upgrade when these two numbers come
with it.
- Return type:
(u32, usize)
LeviMod
| Rust |
|---|
| pub trait LeviMod: Sized + Send + 'static { /* ... */ }
|
One Pier mod.
Send is what makes ModSlot<T>, a Mutex<Option<T>>, genuinely Sync: a lifecycle
callback may be entered on a different thread, since the host allows unload and a
cross-mod service call to come from another thread.
LeviMod::on_load
| Rust |
|---|
| fn on_load(ctx: &ModContext) -> Result<Self>
|
Load. An Err makes the host treat loading as failed and roll back, with the teardown
steps running as usual.
- Parameters:
- Return type:
Result<Self>
LeviMod::on_enable
| Rust |
|---|
| fn on_enable(&mut self, _ctx: &ModContext) -> Result<()>
|
- Parameters:
- Return type:
Result<()>
LeviMod::on_disable
| Rust |
|---|
| fn on_disable(&mut self, _ctx: &ModContext) -> Result<()>
|
- Parameters:
- Return type:
Result<()>
LeviMod::on_unload
| Rust |
|---|
| fn on_unload(&mut self, _ctx: &ModContext) -> Result<()>
|
- Parameters:
- Return type:
Result<()>
TaskId
| Rust |
|---|
| pub struct TaskId(pub(crate) u64);
|
The ticket of a scheduled task.
- Implements:
Debug, Clone, Copy, PartialEq, Eq, Hash
TaskId::is_valid
| Rust |
|---|
| pub fn is_valid(self) -> bool
|
TaskId::raw
Error
| Rust |
|---|
| pub struct Error(pub String);
|
Why one ABI call failed. The message is meant for a human and can go straight into a
log.
- Implements:
Debug, From, Display, Error
Error::new
| Rust |
|---|
| pub fn new(msg: impl core::fmt::Display) -> Self
|
- Parameters:
- msg :
impl core::fmt::Display
- Return type:
Self
Result
| Rust |
|---|
| pub type Result<T> = core::result::Result<T, Error>;
|
LogLevel
| Rust |
|---|
| pub enum LogLevel {
Fatal = 0,
Error = 1,
Warn = 2,
Info = 3,
Debug = 4,
Trace = 5,
}
|
Mirrors ll::io::LogLevel. The values are part of the ABI.
- Implements:
Debug, Clone, Copy, PartialEq, Eq
Macros
register_mod!
Generates the pier_main entry point. Written once per mod.
| Rust |
|---|
| struct MyMod;
impl LeviMod for MyMod { /* ... */ }
levilamina::register_mod!(MyMod);
|
require_slot!
| Rust |
|---|
| require_slot! ($field:ident, $what:expr)
require_slot! (f)
|
Both gates. Missing either returns an Err that says what is missing.
Usage: at the top of a function body,
require_slot!(md_add_dimension, "creating a dimension");
The message carries no historical product name (contract Β§7). An earlier one read
"...Update levilamina-rust-loader" while no mod of that name exists any more, so anyone
following it finds nothing, which is exactly the shape Β§5.3 opposes when it says a log
line has to answer what to do about it.
has_slot!
Only asks whether it exists and returns no Err. For code that uses it when present
and degrades otherwise.
Both gates again: long enough and non-null counts as present.