Skip to main content

kojacoord_plugin_system/
api.rs

1use bytes::Bytes;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use uuid::Uuid;
5
6pub struct PluginContext {
7    pub plugin_id: String,
8    pub version: String,
9    pub config: HashMap<String, String>,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum PluginEvent {
14    PlayerJoin {
15        uuid: Uuid,
16        username: String,
17    },
18    PlayerLeave {
19        uuid: Uuid,
20    },
21    PlayerChat {
22        uuid: Uuid,
23        message: String,
24    },
25    PlayerMove {
26        uuid: Uuid,
27        x: f64,
28        y: f64,
29        z: f64,
30        on_ground: bool,
31    },
32    ServerMessage {
33        message: String,
34    },
35    Custom {
36        event_type: String,
37        data: serde_json::Value,
38    },
39}
40
41pub trait Plugin: Send + Sync {
42    fn name(&self) -> &str;
43    fn version(&self) -> &str;
44    fn author(&self) -> &str;
45    fn description(&self) -> &str;
46
47    fn on_load(&mut self, context: &PluginContext) -> anyhow::Result<()>;
48    fn on_unload(&mut self) -> anyhow::Result<()>;
49    fn on_enable(&mut self) -> anyhow::Result<()>;
50    fn on_disable(&mut self) -> anyhow::Result<()>;
51
52    fn handle_event(&mut self, event: &PluginEvent) -> anyhow::Result<Option<PluginResponse>>;
53
54    fn register_packet_hooks(&mut self) -> Vec<PacketEvent> {
55        Vec::new()
56    }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub enum PluginResponse {
61    None,
62    Message(String),
63    KickPlayer { uuid: Uuid, reason: String },
64    Broadcast(String),
65    Custom(serde_json::Value),
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct PluginMetadata {
70    pub name: String,
71    pub version: String,
72    pub author: String,
73    pub description: String,
74    pub min_proxy_version: String,
75    pub dependencies: Vec<String>,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum PacketDirection {
80    Clientbound,
81    Serverbound,
82}
83
84#[derive(Debug, Clone)]
85pub struct PacketFilter {
86    pub protocol_version: Option<u32>,
87    pub packet_id: Option<i32>,
88    pub direction: PacketDirection,
89}
90
91impl PacketFilter {
92    pub fn new(direction: PacketDirection) -> Self {
93        Self {
94            protocol_version: None,
95            packet_id: None,
96            direction,
97        }
98    }
99
100    pub fn with_protocol_version(mut self, version: u32) -> Self {
101        self.protocol_version = Some(version);
102        self
103    }
104
105    pub fn with_packet_id(mut self, id: i32) -> Self {
106        self.packet_id = Some(id);
107        self
108    }
109}
110
111#[derive(Debug, Clone)]
112pub struct PacketData {
113    pub protocol_version: u32,
114    pub packet_id: i32,
115    pub direction: PacketDirection,
116    pub data: Bytes,
117    pub player_uuid: Option<Uuid>,
118}
119
120#[derive(Debug, Clone)]
121pub enum PacketHookResult {
122    Forward,
123
124    Drop,
125
126    Modify(Bytes),
127
128    Replace { packet_id: i32, data: Bytes },
129}
130
131pub type PacketHookFn = Box<dyn Fn(&PacketData) -> anyhow::Result<PacketHookResult> + Send + Sync>;
132
133pub struct PacketEvent {
134    filter: PacketFilter,
135    hook: PacketHookFn,
136}
137
138impl PacketEvent {
139    pub fn hook(filter: PacketFilter, hook: PacketHookFn) -> Self {
140        Self { filter, hook }
141    }
142
143    pub fn hook_to_clientbound<F>(
144        protocol_version: Option<u32>,
145        packet_id: Option<i32>,
146        hook: F,
147    ) -> Self
148    where
149        F: Fn(&PacketData) -> anyhow::Result<PacketHookResult> + Send + Sync + 'static,
150    {
151        let filter = PacketFilter {
152            protocol_version,
153            packet_id,
154            direction: PacketDirection::Clientbound,
155        };
156        Self {
157            filter,
158            hook: Box::new(hook),
159        }
160    }
161
162    /// Hook to a specific serverbound packet
163    pub fn hook_to_serverbound<F>(
164        protocol_version: Option<u32>,
165        packet_id: Option<i32>,
166        hook: F,
167    ) -> Self
168    where
169        F: Fn(&PacketData) -> anyhow::Result<PacketHookResult> + Send + Sync + 'static,
170    {
171        let filter = PacketFilter {
172            protocol_version,
173            packet_id,
174            direction: PacketDirection::Serverbound,
175        };
176        Self {
177            filter,
178            hook: Box::new(hook),
179        }
180    }
181
182    pub fn matches(&self, packet: &PacketData) -> bool {
183        if packet.direction != self.filter.direction {
184            return false;
185        }
186        if let Some(version) = self.filter.protocol_version {
187            if packet.protocol_version != version {
188                return false;
189            }
190        }
191        if let Some(id) = self.filter.packet_id {
192            if packet.packet_id != id {
193                return false;
194            }
195        }
196        true
197    }
198
199    pub fn execute(&self, packet: &PacketData) -> anyhow::Result<PacketHookResult> {
200        (self.hook)(packet)
201    }
202
203    pub fn filter(&self) -> &PacketFilter {
204        &self.filter
205    }
206}