feat(wasm-api): add ServerTickEndEvent (#2100)

* feat(plugin-api): add ServerTickEndEvent

Add a non-cancellable event fired at the end of every server tick,
carrying the 0-indexed tick number and the tick duration in nanoseconds.
Wires through all five layers: WIT contract, plugin-api guest binding,
runtime host struct, WASM bridge, and the ticker fire site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix ci/ fmt

* chore: point pumpkin-plugin-wit submodule to fork with server-tick-end-event

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Revert submodule

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
とぴ。(おーけ
2026-05-22 23:34:09 +09:00
committed by GitHub
parent a90996691e
commit f926410e4b
7 changed files with 92 additions and 4 deletions

View File

@@ -1,9 +1,11 @@
pub mod server_broadcast;
pub mod server_command;
pub mod server_tick_end;
pub mod server_tick_start;
pub mod spawn_change;
pub use server_broadcast::*;
pub use server_command::*;
pub use server_tick_end::*;
pub use server_tick_start::*;
pub use spawn_change::*;

View File

@@ -0,0 +1,28 @@
use crate::wit::pumpkin::plugin::event::{Event, EventType, ServerTickEndEventData};
use super::super::FromIntoEvent;
/// An event that fires at the end of every server tick.
///
/// The associated [`ServerTickEndEventData`] carries:
/// - `tick`: the 0-indexed number of the tick that just finished.
/// - `duration_nanos`: how long the tick took, measured from the start of
/// the ticker iteration to just after `Server::tick` returned.
///
/// This event is non-cancellable.
pub struct ServerTickEndEvent;
impl FromIntoEvent for ServerTickEndEvent {
const EVENT_TYPE: EventType = EventType::ServerTickEndEvent;
type Data = ServerTickEndEventData;
fn data_from_event(event: Event) -> Self::Data {
match event {
Event::ServerTickEndEvent(data) => data,
_ => panic!("unexpected event"),
}
}
fn data_into_event(data: Self::Data) -> Event {
Event::ServerTickEndEvent(data)
}
}

View File

@@ -1,3 +1,4 @@
pub mod server_broadcast;
pub mod server_command;
pub mod server_tick_end;
pub mod server_tick_start;

View File

@@ -0,0 +1,22 @@
use pumpkin_macros::Event;
/// An event that fires at the end of every server tick.
#[derive(Event, Clone)]
pub struct ServerTickEndEvent {
/// 0-indexed number of the tick that just finished.
pub tick: i32,
/// Duration (in nanoseconds) of the tick that just finished.
pub duration_nanos: i64,
}
impl ServerTickEndEvent {
/// Creates a new `ServerTickEndEvent`.
#[must_use]
pub const fn new(tick: i32, duration_nanos: i64) -> Self {
Self {
tick,
duration_nanos,
}
}
}

View File

@@ -235,7 +235,7 @@ async fn register_server_event(
) {
use crate::plugin::server::{
server_broadcast::ServerBroadcastEvent, server_command::ServerCommandEvent,
server_tick_start::ServerTickStartEvent,
server_tick_end::ServerTickEndEvent, server_tick_start::ServerTickStartEvent,
};
match event_type {
@@ -246,6 +246,9 @@ async fn register_server_event(
register_typed_event::<ServerBroadcastEvent>(resource, handler, priority, blocking)
.await;
}
EventType::ServerTickEndEvent => {
register_typed_event::<ServerTickEndEvent>(resource, handler, priority, blocking).await;
}
EventType::ServerTickStartEvent => {
register_typed_event::<ServerTickStartEvent>(resource, handler, priority, blocking)
.await;
@@ -317,6 +320,7 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
match event_type {
event_type @ (EventType::ServerCommandEvent
| EventType::ServerBroadcastEvent
| EventType::ServerTickEndEvent
| EventType::ServerTickStartEvent) => {
register_server_event(resource, &handler, priority, blocking, event_type).await;
}

View File

@@ -4,13 +4,14 @@ use crate::plugin::{
wit::v0_1::{
events::{ToFromWasmEvent, consume_text_component},
pumpkin::plugin::event::{
Event, ServerBroadcastEventData, ServerCommandEventData, ServerTickStartEventData,
Event, ServerBroadcastEventData, ServerCommandEventData, ServerTickEndEventData,
ServerTickStartEventData,
},
},
},
server::{
server_broadcast::ServerBroadcastEvent, server_command::ServerCommandEvent,
server_tick_start::ServerTickStartEvent,
server_tick_end::ServerTickEndEvent, server_tick_start::ServerTickStartEvent,
},
};
@@ -61,6 +62,25 @@ impl ToFromWasmEvent for ServerBroadcastEvent {
}
}
impl ToFromWasmEvent for ServerTickEndEvent {
fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event {
Event::ServerTickEndEvent(ServerTickEndEventData {
tick: self.tick,
duration_nanos: self.duration_nanos,
})
}
fn from_wasm_event(event: Event, _state: &mut PluginHostState) -> Self {
match event {
Event::ServerTickEndEvent(data) => Self {
tick: data.tick,
duration_nanos: data.duration_nanos,
},
_ => panic!("unexpected event type"),
}
}
}
impl ToFromWasmEvent for ServerTickStartEvent {
fn to_wasm_event(&self, _state: &mut PluginHostState) -> Event {
Event::ServerTickStartEvent(ServerTickStartEventData { tick: self.tick })

View File

@@ -1,5 +1,9 @@
use crate::{
STOP_INTERRUPT, plugin::server::server_tick_start::ServerTickStartEvent, server::Server,
STOP_INTERRUPT,
plugin::server::{
server_tick_end::ServerTickEndEvent, server_tick_start::ServerTickStartEvent,
},
server::Server,
};
use std::sync::Arc;
use std::sync::atomic::Ordering;
@@ -38,6 +42,13 @@ impl Ticker {
}
let tick_duration_nanos = tick_start_time.elapsed().as_nanos() as i64;
let tick_number = server.tick_count.load(Ordering::Relaxed);
let _ = server
.plugin_manager
.fire(ServerTickEndEvent::new(tick_number, tick_duration_nanos))
.await;
server.update_tick_times(tick_duration_nanos).await;
let tick_interval = if manager.is_sprinting() {