1use std::os::raw::c_void;
11
12use yog_abi::{
13 YogAdvancementEvent, YogApi, YogAttackEntityEvent, YogBlockBreakEvent, YogBlockDef,
14 YogChatEvent, YogCommandEvent, YogContainerCloseEvent, YogContainerOpenEvent, YogCraftEvent,
15 YogEntityDamageEvent, YogEntityDeathEvent, YogEntityInteractEvent, YogEntitySpawnEvent,
16 YogExplosionEvent, YogGfxApi, YogItemDef, YogItemPickupEvent, YogKeyPressEvent,
17 YogPacketEvent, YogPlaceBlockEvent, YogPlayerDeathEvent, YogPlayerEvent, YogPlayerMoveEvent,
18 YogPlayerRespawnEvent, YogProjectileHitEvent, YogServer, YogStr, YogStartupGrantDef,
19 YogUseBlockEvent, YogUseItemEvent, YogInventoryDef,
20};
21use yog_book::Book;
22use yog_gfx::GfxContext;
23use yog_command::CommandContext;
24use yog_core::Server;
25use yog_event::{
26 AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
27 ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
28 EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent,
29 ItemPickupEvent, KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent,
30 PlayerLeaveEvent, PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent,
31 UseBlockEvent, UseItemEvent,
32};
33use yog_network::{Packet, PacketEvent};
34use yog_registry::{BlockDef, FurnaceRecipe, ItemDef, ShapedRecipe, ShapelessRecipe, StartupGrant};
35
36pub struct CServer(pub *const YogServer);
41
42unsafe impl Send for CServer {}
43unsafe impl Sync for CServer {}
44
45macro_rules! srv {
46 ($self:ident) => { unsafe { &*$self.0 } };
47}
48
49impl Server for CServer {
50 fn broadcast(&self, message: &str) {
51 let s = srv!(self);
52 unsafe { (s.broadcast)(s.ctx, YogStr::from_str(message)) }
53 }
54
55 fn get_block(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
56 let s = srv!(self);
57 let owned = unsafe {
58 (s.get_block)(s.ctx, YogStr::from_str(dimension),
59 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z })
60 };
61 if owned.is_none() { return None; }
62 let result = unsafe {
63 String::from_utf8(
64 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
65 ).ok()
66 };
67 unsafe { (s.free_str)(owned.ptr, owned.len) };
68 result
69 }
70
71 fn set_block(&self, dimension: &str, pos: yog_core::BlockPos, block_id: &str) -> bool {
72 let s = srv!(self);
73 unsafe {
74 (s.set_block)(s.ctx, YogStr::from_str(dimension),
75 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z },
76 YogStr::from_str(block_id))
77 }
78 }
79
80 fn give_item(&self, player: &str, item_id: &str, count: u32) -> bool {
81 let s = srv!(self);
82 unsafe { (s.give_item)(s.ctx, YogStr::from_str(player), YogStr::from_str(item_id), count) }
83 }
84
85 fn teleport(&self, player: &str, x: f64, y: f64, z: f64) -> bool {
86 let s = srv!(self);
87 unsafe { (s.player_teleport)(s.ctx, YogStr::from_str(player), yog_abi::YogVec3 { x, y, z }) }
88 }
89
90 fn send_to_player(&self, player: &str, channel: &str, payload: &[u8]) -> bool {
91 let s = srv!(self);
92 unsafe {
93 (s.send_to_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(channel),
94 payload.as_ptr(), payload.len() as u32)
95 }
96 }
97
98 fn send_to_server(&self, channel: &str, payload: &[u8]) -> bool {
99 let s = srv!(self);
100 unsafe {
101 (s.send_to_server)(s.ctx, YogStr::from_str(channel),
102 payload.as_ptr(), payload.len() as u32)
103 }
104 }
105
106 fn entity_teleport(&self, uuid: &str, x: f64, y: f64, z: f64) -> bool {
107 let s = srv!(self);
108 unsafe { (s.entity_teleport)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x, y, z }) }
109 }
110
111 fn entity_position(&self, uuid: &str) -> Option<(f64, f64, f64)> {
112 let s = srv!(self);
113 let mut out = yog_abi::YogVec3 { x: 0.0, y: 0.0, z: 0.0 };
114 if unsafe { (s.entity_position)(s.ctx, YogStr::from_str(uuid), &mut out) } {
115 Some((out.x, out.y, out.z))
116 } else {
117 None
118 }
119 }
120
121 fn entity_health(&self, uuid: &str) -> Option<f32> {
122 let s = srv!(self);
123 let mut hp = 0f32;
124 if unsafe { (s.entity_health)(s.ctx, YogStr::from_str(uuid), &mut hp) } { Some(hp) } else { None }
125 }
126
127 fn entity_set_health(&self, uuid: &str, health: f32) -> bool {
128 let s = srv!(self);
129 unsafe { (s.entity_set_health)(s.ctx, YogStr::from_str(uuid), health) }
130 }
131
132 fn entity_kill(&self, uuid: &str) -> bool {
133 let s = srv!(self);
134 unsafe { (s.entity_kill)(s.ctx, YogStr::from_str(uuid)) }
135 }
136
137 fn spawn_entity(&self, entity_type: &str, dimension: &str, x: f64, y: f64, z: f64) -> Option<String> {
138 let s = srv!(self);
139 let owned = unsafe {
140 (s.spawn_entity)(s.ctx, YogStr::from_str(entity_type),
141 YogStr::from_str(dimension), yog_abi::YogVec3 { x, y, z })
142 };
143 if owned.is_none() { return None; }
144 let result = unsafe {
145 String::from_utf8(
146 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
147 ).ok()
148 };
149 unsafe { (s.free_str)(owned.ptr, owned.len) };
150 result
151 }
152
153 fn entity_add_effect(&self, uuid: &str, effect_id: &str, duration_ticks: i32, amplifier: u8, show_particles: bool) -> bool {
154 let s = srv!(self);
155 unsafe { (s.entity_add_effect)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(effect_id), duration_ticks, amplifier, show_particles) }
156 }
157
158 fn entity_remove_effect(&self, uuid: &str, effect_id: &str) -> bool {
159 let s = srv!(self);
160 unsafe { (s.entity_remove_effect)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(effect_id)) }
161 }
162
163 fn entity_clear_effects(&self, uuid: &str) -> bool {
164 let s = srv!(self);
165 unsafe { (s.entity_clear_effects)(s.ctx, YogStr::from_str(uuid)) }
166 }
167
168 fn drop_loot(&self, table_id: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
169 let s = srv!(self);
170 unsafe { (s.drop_loot)(s.ctx, YogStr::from_str(table_id), YogStr::from_str(dimension), yog_abi::YogVec3 { x, y, z }) }
171 }
172
173 fn has_item_tag(&self, item_id: &str, tag_id: &str) -> bool {
174 let s = srv!(self);
175 unsafe { (s.has_item_tag)(s.ctx, YogStr::from_str(item_id), YogStr::from_str(tag_id)) }
176 }
177
178 fn has_block_tag(&self, block_id: &str, tag_id: &str) -> bool {
179 let s = srv!(self);
180 unsafe { (s.has_block_tag)(s.ctx, YogStr::from_str(block_id), YogStr::from_str(tag_id)) }
181 }
182
183 fn world_time(&self, dimension: &str) -> Option<i64> {
184 let s = srv!(self);
185 let mut t = 0i64;
186 if unsafe { (s.world_time)(s.ctx, YogStr::from_str(dimension), &mut t) } { Some(t) } else { None }
187 }
188
189 fn world_set_time(&self, dimension: &str, time: i64) -> bool {
190 let s = srv!(self);
191 unsafe { (s.set_time)(s.ctx, YogStr::from_str(dimension), time) }
192 }
193
194 fn world_is_raining(&self, dimension: &str) -> bool {
195 let s = srv!(self);
196 unsafe { (s.is_raining)(s.ctx, YogStr::from_str(dimension)) }
197 }
198
199 fn world_set_weather(&self, dimension: &str, raining: bool, duration_ticks: i32) -> bool {
200 let s = srv!(self);
201 unsafe { (s.set_weather)(s.ctx, YogStr::from_str(dimension), raining, duration_ticks) }
202 }
203
204 fn entity_velocity(&self, uuid: &str) -> Option<(f64, f64, f64)> {
205 let s = srv!(self);
206 let mut v = yog_abi::YogVec3 { x: 0.0, y: 0.0, z: 0.0 };
207 if unsafe { (s.entity_velocity)(s.ctx, YogStr::from_str(uuid), &mut v) } {
208 Some((v.x, v.y, v.z))
209 } else {
210 None
211 }
212 }
213
214 fn entity_set_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
215 let s = srv!(self);
216 unsafe { (s.entity_set_velocity)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x: vx, y: vy, z: vz }) }
217 }
218
219 fn entity_add_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
220 let s = srv!(self);
221 unsafe { (s.entity_add_velocity)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x: vx, y: vy, z: vz }) }
222 }
223
224 fn scoreboard_get(&self, objective: &str, player: &str) -> Option<i32> {
225 let s = srv!(self);
226 let mut score = 0i32;
227 if unsafe { (s.scoreboard_get)(s.ctx, YogStr::from_str(objective), YogStr::from_str(player), &mut score) } { Some(score) } else { None }
228 }
229
230 fn scoreboard_set(&self, objective: &str, player: &str, score: i32) -> bool {
231 let s = srv!(self);
232 unsafe { (s.scoreboard_set)(s.ctx, YogStr::from_str(objective), YogStr::from_str(player), score) }
233 }
234
235 fn scoreboard_add(&self, objective: &str, player: &str, delta: i32) -> Option<i32> {
236 let s = srv!(self);
237 let mut new_score = 0i32;
238 if unsafe { (s.scoreboard_add)(s.ctx, YogStr::from_str(objective), YogStr::from_str(player), delta, &mut new_score) } { Some(new_score) } else { None }
239 }
240
241 fn game_dir(&self) -> String {
242 let s = srv!(self);
243 let owned = unsafe { (s.game_dir)(s.ctx) };
244 if owned.is_none() { return String::new(); }
245 let result = unsafe {
246 String::from_utf8(
247 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
248 ).unwrap_or_default()
249 };
250 unsafe { (s.free_str)(owned.ptr, owned.len) };
251 result
252 }
253
254 fn play_sound(&self, dimension: &str, x: f64, y: f64, z: f64, sound_id: &str, volume: f32, pitch: f32) -> bool {
255 let s = srv!(self);
256 unsafe { (s.play_sound)(s.ctx, YogStr::from_str(dimension), yog_abi::YogVec3 { x, y, z }, YogStr::from_str(sound_id), volume, pitch) }
257 }
258
259 fn play_sound_to_player(&self, player: &str, sound_id: &str, volume: f32, pitch: f32) -> bool {
260 let s = srv!(self);
261 unsafe { (s.play_sound_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(sound_id), volume, pitch) }
262 }
263
264 fn send_title(&self, player: &str, title: &str, subtitle: &str, fadein: i32, stay: i32, fadeout: i32) -> bool {
265 let s = srv!(self);
266 unsafe { (s.send_title)(s.ctx, YogStr::from_str(player), YogStr::from_str(title), YogStr::from_str(subtitle), fadein, stay, fadeout) }
267 }
268
269 fn entity_rotation(&self, uuid: &str) -> Option<(f32, f32)> {
270 let s = srv!(self);
271 let mut out = yog_abi::YogVec3 { x: 0.0, y: 0.0, z: 0.0 };
272 let ok = unsafe { (s.entity_rotation)(s.ctx, YogStr::from_str(uuid), &mut out) };
273 if ok { Some((out.x as f32, out.y as f32)) } else { None }
274 }
275
276 fn send_actionbar(&self, player: &str, message: &str) -> bool {
277 let s = srv!(self);
278 unsafe { (s.send_actionbar)(s.ctx, YogStr::from_str(player), YogStr::from_str(message)) }
279 }
280
281 fn kick_player(&self, player: &str, reason: &str) -> bool {
282 let s = srv!(self);
283 unsafe { (s.kick_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(reason)) }
284 }
285
286 fn set_gamemode(&self, player: &str, gamemode: &str) -> bool {
287 let s = srv!(self);
288 unsafe { (s.set_gamemode)(s.ctx, YogStr::from_str(player), YogStr::from_str(gamemode)) }
289 }
290
291 fn bossbar_create(&self, id: &str, title: &str, color: &str, style: &str) -> bool {
292 let s = srv!(self);
293 unsafe { (s.bossbar_create)(s.ctx, YogStr::from_str(id), YogStr::from_str(title), YogStr::from_str(color), YogStr::from_str(style)) }
294 }
295
296 fn bossbar_remove(&self, id: &str) -> bool {
297 let s = srv!(self);
298 unsafe { (s.bossbar_remove)(s.ctx, YogStr::from_str(id)) }
299 }
300
301 fn bossbar_set_title(&self, id: &str, title: &str) -> bool {
302 let s = srv!(self);
303 unsafe { (s.bossbar_set_title)(s.ctx, YogStr::from_str(id), YogStr::from_str(title)) }
304 }
305
306 fn bossbar_set_progress(&self, id: &str, progress: f32) -> bool {
307 let s = srv!(self);
308 unsafe { (s.bossbar_set_progress)(s.ctx, YogStr::from_str(id), progress) }
309 }
310
311 fn bossbar_set_color(&self, id: &str, color: &str) -> bool {
312 let s = srv!(self);
313 unsafe { (s.bossbar_set_color)(s.ctx, YogStr::from_str(id), YogStr::from_str(color)) }
314 }
315
316 fn bossbar_add_player(&self, id: &str, player: &str) -> bool {
317 let s = srv!(self);
318 unsafe { (s.bossbar_add_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
319 }
320
321 fn bossbar_remove_player(&self, id: &str, player: &str) -> bool {
322 let s = srv!(self);
323 unsafe { (s.bossbar_remove_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
324 }
325
326 fn bossbar_set_visible(&self, id: &str, visible: bool) -> bool {
327 let s = srv!(self);
328 unsafe { (s.bossbar_set_visible)(s.ctx, YogStr::from_str(id), visible) }
329 }
330
331 fn online_players(&self) -> Vec<String> {
332 let s = srv!(self);
333 let owned = unsafe { (s.online_players)(s.ctx) };
334 if owned.is_none() { return Vec::new(); }
335 let text = unsafe {
336 String::from_utf8(
337 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
338 ).unwrap_or_default()
339 };
340 unsafe { (s.free_str)(owned.ptr, owned.len) };
341 if text.is_empty() { Vec::new() } else { text.lines().map(str::to_owned).collect() }
342 }
343
344 fn get_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
345 let s = srv!(self);
346 let owned = unsafe {
347 (s.get_block_nbt)(s.ctx, YogStr::from_str(dimension),
348 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z })
349 };
350 if owned.is_none() { return None; }
351 let result = unsafe {
352 String::from_utf8(
353 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
354 ).ok()
355 };
356 unsafe { (s.free_str)(owned.ptr, owned.len) };
357 result
358 }
359
360 fn set_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos, snbt: &str) -> bool {
361 let s = srv!(self);
362 unsafe {
363 (s.set_block_nbt)(s.ctx, YogStr::from_str(dimension),
364 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z },
365 YogStr::from_str(snbt))
366 }
367 }
368
369 fn get_inventory_slot(&self, dimension: &str, pos: yog_core::BlockPos, slot: u32) -> Option<(String, u32)> {
370 let s = srv!(self);
371 let owned = unsafe {
372 (s.get_inventory_slot)(s.ctx, YogStr::from_str(dimension),
373 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z }, slot)
374 };
375 if owned.is_none() { return None; }
376 let text = unsafe {
377 String::from_utf8(
378 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
379 ).unwrap_or_default()
380 };
381 unsafe { (s.free_str)(owned.ptr, owned.len) };
382 let (item_id, count) = text.split_once('\t')?;
383 Some((item_id.to_owned(), count.parse().ok()?))
384 }
385
386 fn set_inventory_slot(&self, dimension: &str, pos: yog_core::BlockPos, slot: u32, item_id: &str, count: u32) -> bool {
387 let s = srv!(self);
388 unsafe {
389 (s.set_inventory_slot)(s.ctx, YogStr::from_str(dimension),
390 yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z }, slot,
391 YogStr::from_str(item_id), count)
392 }
393 }
394
395 fn player_inventory(&self, player: &str) -> Vec<(u32, String, u32)> {
396 let s = srv!(self);
397 let owned = unsafe { (s.player_inventory)(s.ctx, YogStr::from_str(player)) };
398 if owned.is_none() { return Vec::new(); }
399 let text = unsafe {
400 String::from_utf8(
401 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
402 ).unwrap_or_default()
403 };
404 unsafe { (s.free_str)(owned.ptr, owned.len) };
405 text.lines().filter_map(|line| {
406 let mut it = line.split('\t');
407 let slot: u32 = it.next()?.parse().ok()?;
408 let item_id = it.next()?.to_owned();
409 let count: u32 = it.next()?.parse().ok()?;
410 Some((slot, item_id, count))
411 }).collect()
412 }
413
414 fn player_set_slot(&self, player: &str, slot: u32, item_id: &str, count: u32) -> bool {
415 let s = srv!(self);
416 unsafe {
417 (s.player_set_slot)(s.ctx, YogStr::from_str(player), slot, YogStr::from_str(item_id), count)
418 }
419 }
420
421 fn teleport_to_dim(&self, player: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
422 let s = srv!(self);
423 unsafe {
424 (s.player_teleport_dim)(s.ctx, YogStr::from_str(player),
425 YogStr::from_str(dimension), yog_abi::YogVec3 { x, y, z })
426 }
427 }
428
429 fn entity_teleport_to_dim(&self, uuid: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
430 let s = srv!(self);
431 unsafe {
432 (s.entity_teleport_dim)(s.ctx, YogStr::from_str(uuid),
433 YogStr::from_str(dimension), yog_abi::YogVec3 { x, y, z })
434 }
435 }
436
437 fn world_entity_count(&self, dimension: &str, entity_type: &str) -> i32 {
438 let s = srv!(self);
439 unsafe { (s.world_entity_count)(s.ctx, YogStr::from_str(dimension), YogStr::from_str(entity_type)) }
440 }
441
442 fn entity_get_nbt(&self, uuid: &str) -> Option<String> {
443 let s = srv!(self);
444 let owned = unsafe { (s.entity_get_nbt)(s.ctx, YogStr::from_str(uuid)) };
445 if owned.is_none() { return None; }
446 let result = unsafe {
447 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()).ok()
448 };
449 unsafe { (s.free_str)(owned.ptr, owned.len) };
450 result
451 }
452
453 fn entity_set_nbt(&self, uuid: &str, snbt: &str) -> bool {
454 let s = srv!(self);
455 unsafe { (s.entity_set_nbt)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(snbt)) }
456 }
457
458 fn spawn_particles(&self, dimension: &str, x: f64, y: f64, z: f64, particle_type: &str, count: i32, dx: f64, dy: f64, dz: f64, speed: f64) -> bool {
459 let s = srv!(self);
460 unsafe {
461 (s.spawn_particles)(s.ctx, YogStr::from_str(dimension),
462 yog_abi::YogVec3 { x, y, z }, YogStr::from_str(particle_type),
463 count, dx, dy, dz, speed)
464 }
465 }
466
467 fn entity_attribute_get(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
468 let s = srv!(self);
469 let v = unsafe { (s.entity_attribute_get)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(attribute_id)) };
470 if v.is_nan() { None } else { Some(v) }
471 }
472
473 fn entity_attribute_set(&self, uuid: &str, attribute_id: &str, value: f64) -> bool {
474 let s = srv!(self);
475 unsafe { (s.entity_attribute_set)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(attribute_id), value) }
476 }
477
478 fn get_held_item_nbt(&self, player: &str) -> Option<String> {
479 let s = srv!(self);
480 let owned = unsafe { (s.get_held_item_nbt)(s.ctx, YogStr::from_str(player)) };
481 if owned.is_none() { return None; }
482 let result = unsafe {
483 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()).ok()
484 };
485 unsafe { (s.free_str)(owned.ptr, owned.len) };
486 result
487 }
488
489 fn set_held_item_nbt(&self, player: &str, snbt: &str) -> bool {
490 let s = srv!(self);
491 unsafe { (s.set_held_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
492 }
493
494 fn get_offhand_item_nbt(&self, player: &str) -> Option<String> {
495 let s = srv!(self);
496 let owned = unsafe { (s.get_offhand_item_nbt)(s.ctx, YogStr::from_str(player)) };
497 if owned.is_none() { return None; }
498 let result = unsafe {
499 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()).ok()
500 };
501 unsafe { (s.free_str)(owned.ptr, owned.len) };
502 result
503 }
504
505 fn set_offhand_item_nbt(&self, player: &str, snbt: &str) -> bool {
506 let s = srv!(self);
507 unsafe { (s.set_offhand_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
508 }
509
510 fn get_slot_item(&self, player: &str, slot: u32) -> Option<(String, u32, String)> {
511 let s = srv!(self);
512 let owned = unsafe { (s.get_slot_item)(s.ctx, YogStr::from_str(player), slot) };
513 if owned.is_none() { return None; }
514 let text = unsafe {
515 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
516 .unwrap_or_default()
517 };
518 unsafe { (s.free_str)(owned.ptr, owned.len) };
519 let mut it = text.splitn(3, '\t');
520 let item_id = it.next()?.to_owned();
521 let count: u32 = it.next()?.parse().ok()?;
522 let nbt = it.next().unwrap_or("{}").to_owned();
523 Some((item_id, count, nbt))
524 }
525
526 fn set_slot_item(&self, player: &str, slot: u32, item_id: &str, count: u32, snbt: &str) -> bool {
527 let s = srv!(self);
528 unsafe {
529 (s.set_slot_item)(s.ctx, YogStr::from_str(player), slot,
530 YogStr::from_str(item_id), count, YogStr::from_str(snbt))
531 }
532 }
533}
534
535macro_rules! trampoline_phased {
553 ($name:ident, $abi_ev:ty, $rust_ev:ty, |$ev:ident| $build:expr) => {
554 unsafe extern "C" fn $name<F>(
555 ud: *mut c_void, srv: *const YogServer, ev: *const $abi_ev, phase: u8,
556 ) -> bool
557 where F: Fn(&$rust_ev, EventPhase, &dyn Server) -> bool + Send + Sync,
558 {
559 let f = &*(ud as *const F);
560 let $ev = &*ev;
561 let rust_ev = $build;
562 let p = if phase == 0 { EventPhase::Pre } else { EventPhase::Post };
563 f(&rust_ev, p, &CServer(srv))
564 }
565 };
566}
567
568trampoline_phased!(trampoline_block_break, YogBlockBreakEvent, BlockBreakEvent, |ev| BlockBreakEvent {
569 player_name: ev.player.as_str().to_owned(),
570 block_id: ev.block.as_str().to_owned(),
571 pos: yog_core::BlockPos { x: ev.pos.x, y: ev.pos.y, z: ev.pos.z },
572});
573
574trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
575 player_name: ev.player.as_str().to_owned(),
576 message: ev.message.as_str().to_owned(),
577});
578
579trampoline_phased!(trampoline_player_join, YogPlayerEvent, PlayerJoinEvent, |ev| PlayerJoinEvent {
580 player_name: ev.player.as_str().to_owned(),
581 uuid: ev.uuid.as_str().to_owned(),
582});
583
584trampoline_phased!(trampoline_player_leave, YogPlayerEvent, PlayerLeaveEvent, |ev| PlayerLeaveEvent {
585 player_name: ev.player.as_str().to_owned(),
586 uuid: ev.uuid.as_str().to_owned(),
587});
588
589trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| UseItemEvent {
590 player_name: ev.player.as_str().to_owned(),
591 item_id: ev.item.as_str().to_owned(),
592 sneaking: ev.sneaking,
593});
594
595trampoline_phased!(trampoline_use_block, YogUseBlockEvent, UseBlockEvent, |ev| UseBlockEvent {
596 player_name: ev.player.as_str().to_owned(),
597 block_id: ev.block.as_str().to_owned(),
598 pos: yog_core::BlockPos { x: ev.pos.x, y: ev.pos.y, z: ev.pos.z },
599});
600
601trampoline_phased!(trampoline_attack_entity, YogAttackEntityEvent, AttackEntityEvent, |ev| AttackEntityEvent {
602 player_name: ev.player.as_str().to_owned(),
603 target_type: ev.target_type.as_str().to_owned(),
604 target_uuid: ev.target_uuid.as_str().to_owned(),
605});
606
607trampoline_phased!(trampoline_entity_damage, YogEntityDamageEvent, EntityDamageEvent, |ev| EntityDamageEvent {
608 entity_type: ev.entity_type.as_str().to_owned(),
609 uuid: ev.uuid.as_str().to_owned(),
610 amount: ev.amount,
611 source: ev.source.as_str().to_owned(),
612});
613
614trampoline_phased!(trampoline_entity_death, YogEntityDeathEvent, EntityDeathEvent, |ev| EntityDeathEvent {
615 entity_type: ev.entity_type.as_str().to_owned(),
616 uuid: ev.uuid.as_str().to_owned(),
617 source: ev.source.as_str().to_owned(),
618});
619
620trampoline_phased!(trampoline_entity_spawn, YogEntitySpawnEvent, EntitySpawnEvent, |ev| EntitySpawnEvent {
621 entity_type: ev.entity_type.as_str().to_owned(),
622 uuid: ev.uuid.as_str().to_owned(),
623 dimension: ev.dimension.as_str().to_owned(),
624});
625
626trampoline_phased!(trampoline_place_block, YogPlaceBlockEvent, PlaceBlockEvent, |ev| PlaceBlockEvent {
627 player_name: ev.player.as_str().to_owned(),
628 block_id: ev.block.as_str().to_owned(),
629 pos: yog_core::BlockPos { x: ev.pos.x, y: ev.pos.y, z: ev.pos.z },
630});
631
632trampoline_phased!(trampoline_player_death, YogPlayerDeathEvent, PlayerDeathEvent, |ev| PlayerDeathEvent {
633 player_name: ev.player.as_str().to_owned(),
634 uuid: ev.uuid.as_str().to_owned(),
635 source: ev.source.as_str().to_owned(),
636});
637
638trampoline_phased!(trampoline_player_respawn, YogPlayerRespawnEvent, PlayerRespawnEvent, |ev| PlayerRespawnEvent {
639 player_name: ev.player.as_str().to_owned(),
640 uuid: ev.uuid.as_str().to_owned(),
641 at_anchor: ev.at_anchor,
642});
643
644trampoline_phased!(trampoline_advancement, YogAdvancementEvent, AdvancementEvent, |ev| AdvancementEvent {
645 player_name: ev.player.as_str().to_owned(),
646 uuid: ev.uuid.as_str().to_owned(),
647 advancement_id: ev.advancement.as_str().to_owned(),
648});
649
650trampoline_phased!(trampoline_entity_interact, YogEntityInteractEvent, EntityInteractEvent, |ev| EntityInteractEvent {
651 player_name: ev.player.as_str().to_owned(),
652 player_uuid: ev.player_uuid.as_str().to_owned(),
653 entity_type: ev.entity_type.as_str().to_owned(),
654 entity_uuid: ev.entity_uuid.as_str().to_owned(),
655 hand: ev.hand.as_str().to_owned(),
656});
657
658trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| CraftEvent {
659 player_name: ev.player.as_str().to_owned(),
660 player_uuid: ev.player_uuid.as_str().to_owned(),
661 result_item: ev.result_item.as_str().to_owned(),
662 result_count: ev.result_count,
663});
664
665trampoline_phased!(trampoline_explosion, YogExplosionEvent, ExplosionEvent, |ev| ExplosionEvent {
666 dimension: ev.dimension.as_str().to_owned(),
667 x: ev.x,
668 y: ev.y,
669 z: ev.z,
670 power: ev.power,
671 cause_uuid: ev.cause_uuid.as_str().to_owned(),
672});
673
674trampoline_phased!(trampoline_item_pickup, YogItemPickupEvent, ItemPickupEvent, |ev| ItemPickupEvent {
675 player_name: ev.player.as_str().to_owned(),
676 player_uuid: ev.player_uuid.as_str().to_owned(),
677 item_id: ev.item_id.as_str().to_owned(),
678 item_count: ev.item_count,
679 entity_uuid: ev.entity_uuid.as_str().to_owned(),
680});
681
682trampoline_phased!(trampoline_player_move, YogPlayerMoveEvent, PlayerMoveEvent, |ev| PlayerMoveEvent {
683 player_name: ev.player.as_str().to_owned(),
684 player_uuid: ev.player_uuid.as_str().to_owned(),
685 x: ev.x,
686 y: ev.y,
687 z: ev.z,
688 yaw: ev.yaw,
689 pitch: ev.pitch,
690});
691
692trampoline_phased!(trampoline_container_open, YogContainerOpenEvent, ContainerOpenEvent, |ev| ContainerOpenEvent {
693 player_name: ev.player.as_str().to_owned(),
694 player_uuid: ev.player_uuid.as_str().to_owned(),
695 container_type: ev.container_type.as_str().to_owned(),
696});
697
698trampoline_phased!(trampoline_container_close, YogContainerCloseEvent, ContainerCloseEvent, |ev| ContainerCloseEvent {
699 player_name: ev.player.as_str().to_owned(),
700 player_uuid: ev.player_uuid.as_str().to_owned(),
701});
702
703trampoline_phased!(trampoline_projectile_hit, YogProjectileHitEvent, ProjectileHitEvent, |ev| ProjectileHitEvent {
704 projectile_type: ev.projectile_type.as_str().to_owned(),
705 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
706 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
707 hit_type: ev.hit_type.as_str().to_owned(),
708 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
709 x: ev.x,
710 y: ev.y,
711 z: ev.z,
712 dimension: ev.dimension.as_str().to_owned(),
713});
714
715unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
716where F: Fn(&dyn Server) + Send + Sync,
717{
718 let f = &*(ud as *const F);
719 f(&CServer(srv));
720}
721
722unsafe extern "C" fn trampoline_packet<F>(ud: *mut c_void, srv: *const YogServer, ev: *const YogPacketEvent)
723where F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
724{
725 let f = &*(ud as *const F);
726 let ev = &*ev;
727 let rust_ev = PacketEvent {
728 channel: ev.channel.as_str().to_owned(),
729 player: ev.player.as_str().to_owned(),
730 payload: std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec(),
731 };
732 f(&rust_ev, &CServer(srv));
733}
734
735unsafe extern "C" fn trampoline_command<F>(
736 ud: *mut c_void,
737 srv: *const YogServer,
738 ev: *const YogCommandEvent,
739 reply_buf: *mut u8,
740 reply_cap: u32,
741 reply_len: *mut u32,
742) where F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
743{
744 let f = &*(ud as *const F);
745 let ev = &*ev;
746 let ctx = CommandContext {
747 name: ev.name.as_str().to_owned(),
748 args: ev.args.as_str().to_owned(),
749 source: ev.source.as_str().to_owned(),
750 uuid: ev.uuid.as_str().to_owned(),
751 };
752 *reply_len = 0;
753 if let Some(reply) = f(&ctx, &CServer(srv)) {
754 let bytes = reply.as_bytes();
755 let n = bytes.len().min(reply_cap as usize);
756 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
757 *reply_len = n as u32;
758 }
759}
760
761unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
762where F: Fn(&dyn Server) + Send + Sync,
763{
764 let f = &*(ud as *const F);
765 f(&CServer(srv));
766}
767
768unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
771where F: Fn(&ClientTickEvent) + Send + Sync,
772{
773 let f = &*(ud as *const F);
774 f(&ClientTickEvent {});
775}
776
777unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
778where F: Fn(&GfxContext) + Send + Sync,
779{
780 let f = &*(ud as *const F);
781 f(&GfxContext::from_raw(gfx));
782}
783
784unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
785where F: Fn(&GfxContext) + Send + Sync,
786{
787 let f = &*(ud as *const F);
788 f(&GfxContext::from_raw(gfx));
789}
790
791unsafe extern "C" fn trampoline_key_press<F>(
792 ud: *mut c_void,
793 ev: *const YogKeyPressEvent,
794) -> bool
795where F: Fn(&KeyPressEvent) -> bool + Send + Sync,
796{
797 let f = &*(ud as *const F);
798 let e = &*ev;
799 f(&KeyPressEvent {
800 key_code: e.key_code,
801 scan_code: e.scan_code,
802 action: e.action,
803 modifiers: e.modifiers,
804 })
805}
806
807unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
808where F: Fn(&ScreenEvent) + Send + Sync,
809{
810 let f = &*(ud as *const F);
811 f(&ScreenEvent { screen_class: screen_class.as_str().to_owned() });
812 true
813}
814
815static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
826 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
827
828#[derive(Debug, Clone)]
830pub struct ModInfo {
831 pub source: String,
833 pub id: String,
834 pub name: String,
835 pub version: String,
836 pub authors: String,
838 pub description: String,
839}
840
841pub fn installed_mods() -> Vec<ModInfo> {
847 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
848 if api.is_null() { return Vec::new(); }
849 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
850 if owned.is_none() { return Vec::new(); }
851 let text = unsafe {
852 String::from_utf8(
853 std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()
854 ).unwrap_or_default()
855 };
856 unsafe { ((*api).free_str)(owned.ptr, owned.len) };
857 text.lines()
858 .filter_map(|line| {
859 let mut f = line.split('\t');
860 Some(ModInfo {
861 source: f.next()?.to_string(),
862 id: f.next()?.to_string(),
863 name: f.next().unwrap_or_default().to_string(),
864 version: f.next().unwrap_or_default().to_string(),
865 authors: f.next().unwrap_or_default().to_string(),
866 description: f.next().unwrap_or_default().to_string(),
867 })
868 })
869 .collect()
870}
871
872pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
876 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
877 if api.is_null() { return; }
878 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
879}
880
881pub fn server() -> Option<CServer> {
886 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
887 if api.is_null() { return None; }
888 let srv = unsafe { (*api).server };
889 if srv.is_null() { return None; }
890 Some(CServer(srv))
891}
892
893pub struct Registry {
894 api: *const YogApi,
895}
896
897unsafe impl Send for Registry {}
899unsafe impl Sync for Registry {}
900
901impl Registry {
902 pub unsafe fn from_raw(api: *const YogApi) -> Self {
904 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
905 Self { api }
906 }
907
908 #[inline]
909 fn ctx(&self) -> *mut c_void { unsafe { (*self.api).ctx } }
910
911 fn leak<F: 'static>(f: F) -> *mut c_void {
914 Box::into_raw(Box::new(f)) as *mut c_void
915 }
916
917 pub fn on_block_break<F>(&mut self, handler: F)
925 where F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
926 let ud = Self::leak(handler);
927 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
928 }
929
930 pub fn on_chat<F>(&mut self, handler: F)
931 where F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
932 let ud = Self::leak(handler);
933 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
934 }
935
936 pub fn on_player_join<F>(&mut self, handler: F)
937 where F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
938 let ud = Self::leak(handler);
939 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
940 }
941
942 pub fn on_player_leave<F>(&mut self, handler: F)
943 where F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
944 let ud = Self::leak(handler);
945 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
946 }
947
948 pub fn on_use_item<F>(&mut self, handler: F)
949 where F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
950 let ud = Self::leak(handler);
951 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
952 }
953
954 pub fn on_use_block<F>(&mut self, handler: F)
955 where F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
956 let ud = Self::leak(handler);
957 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
958 }
959
960 pub fn on_attack_entity<F>(&mut self, handler: F)
961 where F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
962 let ud = Self::leak(handler);
963 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
964 }
965
966 pub fn on_entity_damage<F>(&mut self, handler: F)
967 where F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
968 let ud = Self::leak(handler);
969 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
970 }
971
972 pub fn on_entity_death<F>(&mut self, handler: F)
973 where F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
974 let ud = Self::leak(handler);
975 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
976 }
977
978 pub fn on_entity_spawn<F>(&mut self, handler: F)
979 where F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
980 let ud = Self::leak(handler);
981 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
982 }
983
984 pub fn on_player_place_block<F>(&mut self, handler: F)
985 where F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
986 let ud = Self::leak(handler);
987 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
988 }
989
990 pub fn on_player_death<F>(&mut self, handler: F)
991 where F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
992 let ud = Self::leak(handler);
993 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
994 }
995
996 pub fn on_player_respawn<F>(&mut self, handler: F)
997 where F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
998 let ud = Self::leak(handler);
999 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
1000 }
1001
1002 pub fn on_advancement<F>(&mut self, handler: F)
1003 where F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1004 let ud = Self::leak(handler);
1005 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
1006 }
1007
1008 pub fn on_entity_interact<F>(&mut self, handler: F)
1009 where F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1010 let ud = Self::leak(handler);
1011 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
1012 }
1013
1014 pub fn on_item_craft<F>(&mut self, handler: F)
1015 where F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1016 let ud = Self::leak(handler);
1017 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
1018 }
1019
1020 pub fn on_explosion<F>(&mut self, handler: F)
1021 where F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1022 let ud = Self::leak(handler);
1023 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
1024 }
1025
1026 pub fn on_item_pickup<F>(&mut self, handler: F)
1027 where F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1028 let ud = Self::leak(handler);
1029 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
1030 }
1031
1032 pub fn on_player_move<F>(&mut self, handler: F)
1033 where F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1034 let ud = Self::leak(handler);
1035 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
1036 }
1037
1038 pub fn on_container_open<F>(&mut self, handler: F)
1039 where F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1040 let ud = Self::leak(handler);
1041 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
1042 }
1043
1044 pub fn on_container_close<F>(&mut self, handler: F)
1045 where F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1046 let ud = Self::leak(handler);
1047 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
1048 }
1049
1050 pub fn on_projectile_hit<F>(&mut self, handler: F)
1051 where F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static {
1052 let ud = Self::leak(handler);
1053 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
1054 }
1055
1056 pub fn on_tick<F>(&mut self, listener: F)
1057 where F: Fn(&dyn Server) + Send + Sync + 'static {
1058 let ud = Self::leak(listener);
1059 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
1060 }
1061
1062 pub fn on_server_started<F>(&mut self, listener: F)
1063 where F: Fn(&dyn Server) + Send + Sync + 'static {
1064 let ud = Self::leak(listener);
1065 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
1066 }
1067
1068 pub fn on_server_stopping<F>(&mut self, listener: F)
1069 where F: Fn(&dyn Server) + Send + Sync + 'static {
1070 let ud = Self::leak(listener);
1071 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
1072 }
1073
1074 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
1077 where F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static {
1078 let name_ys = YogStr::from_str(name.as_ref());
1079 let ud = Self::leak(handler);
1080 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
1081 }
1082
1083 pub fn on_typed_command<F>(&mut self, name: impl AsRef<str>, schema: impl AsRef<str>, handler: F)
1090 where F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static {
1091 let name_ys = YogStr::from_str(name.as_ref());
1092 let schema_ys = YogStr::from_str(schema.as_ref());
1093 let ud = Self::leak(handler);
1094 unsafe { ((*self.api).register_typed_command)(self.ctx(), name_ys, schema_ys, ud, trampoline_command::<F>) }
1095 }
1096
1097 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1100 where F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static {
1101 let ch = YogStr::from_str(channel.as_ref());
1102 let ud = Self::leak(handler);
1103 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1104 }
1105
1106 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1107 where F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static {
1108 let ch = YogStr::from_str(channel.as_ref());
1109 let ud = Self::leak(handler);
1110 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1111 }
1112
1113 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
1118 where
1119 P: Packet + Send + Sync + 'static,
1120 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
1121 {
1122 self.on_packet(channel, move |ev, srv| {
1123 if let Some(pkt) = P::decode(&ev.payload) {
1124 handler(&pkt, srv);
1125 }
1126 });
1127 }
1128
1129 fn recipe(&mut self, ns: &str, name: &str, json: &str) {
1132 unsafe {
1133 ((*self.api).register_recipe_json)(
1134 self.ctx(),
1135 YogStr::from_str(ns), YogStr::from_str(name), YogStr::from_str(json),
1136 )
1137 }
1138 }
1139
1140 pub fn add_shaped_recipe(&mut self, recipe: ShapedRecipe) {
1142 let json = recipe.to_json();
1143 let (ns, name) = recipe.ns_name();
1144 self.recipe(ns, name, &json);
1145 }
1146
1147 pub fn add_shapeless_recipe(&mut self, recipe: ShapelessRecipe) {
1149 let json = recipe.to_json();
1150 let (ns, name) = recipe.ns_name();
1151 self.recipe(ns, name, &json);
1152 }
1153
1154 pub fn add_furnace_recipe(&mut self, recipe: FurnaceRecipe) {
1156 let json = recipe.to_json();
1157 let (ns, name) = recipe.ns_name();
1158 self.recipe(ns, name, &json);
1159 }
1160
1161 pub fn register_item(&mut self, def: ItemDef) {
1164 let food_nutrition = def.food.as_ref().map_or(0, |f| f.nutrition);
1168 let food_saturation = def.food.as_ref().map_or(0.0, |f| f.saturation);
1169 let food_always_eat = def.food.as_ref().map_or(false, |f| f.can_always_eat);
1170 let c = YogItemDef {
1171 id: YogStr::from_str(&def.id),
1172 max_stack: def.max_stack as u32,
1173 name: def.name.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1174 tooltip: def.tooltip.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1175 max_damage: def.max_damage,
1176 fire_resistant: def.fire_resistant,
1177 fuel_ticks: def.fuel_ticks,
1178 food_nutrition,
1179 food_saturation,
1180 food_always_eat,
1181 };
1182 unsafe { ((*self.api).register_item)(self.ctx(), &c) }
1183 }
1184
1185 pub fn register_block(&mut self, def: BlockDef) {
1186 let shape = def.shape.unwrap_or([0.0; 6]);
1187 let groups_joined = def.connect_groups.join(",");
1188 let c = YogBlockDef {
1189 id: YogStr::from_str(&def.id),
1190 hardness: def.hardness,
1191 resistance: def.resistance,
1192 name: def.name.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1193 light_level: def.light_level,
1194 sound: def.sound.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1195 requires_tool: def.requires_tool,
1196 no_collision: def.no_collision,
1197 slipperiness: def.slipperiness,
1198 shape,
1199 connects: def.connects,
1200 connect_groups: YogStr::from_str(&groups_joined),
1201 inventory_id: def.inventory_id.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1202 };
1203 unsafe { ((*self.api).register_block)(self.ctx(), &c) }
1204 }
1205
1206 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
1210 let layout = yog_inventory::encode_layout(&def.resolved_layout());
1211 let c = YogInventoryDef {
1212 id: YogStr::from_str(&def.id),
1213 slot_count: def.slot_count as u32,
1214 layout: YogStr::from_str(&layout),
1215 include_player_inventory: def.include_player_inventory,
1216 player_inv_x: def.player_inv_offset.0,
1217 player_inv_y: def.player_inv_offset.1,
1218 background_texture: def.background_texture.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1219 title: YogStr::from_str(&def.title),
1220 };
1221 unsafe { ((*self.api).register_inventory)(self.ctx(), &c) }
1222 }
1223
1224 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
1228 let items_str = grant.items.join("|");
1229 let c = YogStartupGrantDef {
1230 id: YogStr::from_str(&grant.id),
1231 items: YogStr::from_str(&items_str),
1232 book: grant.book.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1233 command: grant.command.as_deref().map(YogStr::from_str).unwrap_or(YogStr::EMPTY),
1234 };
1235 unsafe { ((*self.api).register_startup_grant)(self.ctx(), &c) }
1236 }
1237
1238 pub fn register_book(&mut self, book: &Book) {
1240 let json = book.to_json();
1241 let id = YogStr::from_str(&book.id);
1242 let j = YogStr::from_str(&json);
1243 unsafe { ((*self.api).register_book)(self.ctx(), id, j) }
1244 }
1245
1246
1247 pub fn installed_mods(&self) -> Vec<ModInfo> {
1254 installed_mods()
1255 }
1256
1257 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
1258 let l = YogStr::from_str(label);
1259 let u = YogStr::from_str(ui_id);
1260 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
1261 }
1262
1263 pub fn register_ui<F>(&mut self, ui_id: &str, handler: F)
1267 where F: Fn(&str, &str) + Send + Sync + 'static {
1268 let ud = Self::leak(handler);
1269 let id = YogStr::from_str(ui_id);
1270 let empty = YogStr::from_str("{}");
1271 unsafe {
1272 ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F>)
1273 }
1274 }
1275
1276 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
1279 where F: Fn(&dyn Server) + Send + Sync + 'static {
1280 let ud = Self::leak(handler);
1281 unsafe { ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>) }
1282 }
1283
1284 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
1285 where F: Fn(&dyn Server) + Send + Sync + 'static {
1286 let ud = Self::leak(handler);
1287 unsafe { ((*self.api).schedule_repeating)(self.ctx(), period_ticks, ud, trampoline_scheduled::<F>) }
1288 }
1289
1290 pub fn on_client_tick<F>(&mut self, handler: F)
1294 where F: Fn(&ClientTickEvent) + Send + Sync + 'static {
1295 let ud = Self::leak(handler);
1296 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
1297 }
1298
1299 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
1308 where F: Fn(&GfxContext) + Send + Sync + 'static {
1309 let ud = Self::leak(handler);
1310 let id = YogStr::from_str(ui_id);
1311 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
1312 }
1313
1314 pub fn on_hud_render<F>(&mut self, handler: F)
1319 where F: Fn(&GfxContext) + Send + Sync + 'static {
1320 let ud = Self::leak(handler);
1321 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
1322 }
1323
1324 pub fn on_world_render<F>(&mut self, handler: F)
1330 where F: Fn(&GfxContext) + Send + Sync + 'static {
1331 let ud = Self::leak(handler);
1332 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
1333 }
1334
1335 pub fn on_key_press<F>(&mut self, handler: F)
1338 where F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static {
1339 let ud = Self::leak(handler);
1340 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
1341 }
1342
1343 pub fn on_screen_open<F>(&mut self, handler: F)
1345 where F: Fn(&ScreenEvent) + Send + Sync + 'static {
1346 let ud = Self::leak(handler);
1347 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
1348 }
1349
1350 pub fn on_screen_close<F>(&mut self, handler: F)
1352 where F: Fn(&ScreenEvent) + Send + Sync + 'static {
1353 let ud = Self::leak(handler);
1354 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
1355 }
1356}
1357
1358unsafe extern "C" fn trampoline_ui_event<F>(ud: *mut c_void, ui_id: yog_abi::YogStr, event_id: yog_abi::YogStr)
1359where F: Fn(&str, &str) + Send + Sync + 'static
1360{
1361 let f = &*(ud as *const F);
1362 let ui = unsafe { ui_id.as_str() };
1363 let ev = unsafe { event_id.as_str() };
1364 f(ui, ev);
1365}
1366
1367pub trait Mod {
1371 fn register(registry: &mut Registry);
1372}