Setup rust passing of ffm
This commit is contained in:
parent
373a553722
commit
a31da8c568
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@ build/
|
||||
java/build/
|
||||
java/patchbukkit/build/
|
||||
java/patchbukkit/bin/
|
||||
!rust/build/
|
||||
|
||||
# Examples
|
||||
examples/*/bin
|
||||
|
||||
@ -3,6 +3,8 @@ name = "patchbukkit"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
build = "build/main.rs"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
@ -1,75 +1,8 @@
|
||||
use std::{collections::HashSet, fs, path::PathBuf};
|
||||
|
||||
use glob::glob;
|
||||
use j4rs::{JvmBuilder, LocalJarArtifact, MavenArtifact, MavenArtifactRepo, MavenSettings};
|
||||
|
||||
use prost_build::{Config, Service, ServiceGenerator};
|
||||
|
||||
struct FfiServiceGenerator;
|
||||
|
||||
impl ServiceGenerator for FfiServiceGenerator {
|
||||
fn generate(&mut self, service: Service, buf: &mut String) {
|
||||
for method in &service.methods {
|
||||
let fn_name = format!("ffi_{}_{}", to_snake_case(&service.name), &method.name);
|
||||
let input_type = &method.input_type;
|
||||
let output_type = &method.output_type;
|
||||
|
||||
buf.push_str(&format!(
|
||||
r#"
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn {fn_name}(
|
||||
input_ptr: *const u8,
|
||||
input_len: usize,
|
||||
output_len: *mut usize,
|
||||
) -> *mut u8 {{
|
||||
use prost::Message;
|
||||
|
||||
let input_slice = std::slice::from_raw_parts(input_ptr, input_len);
|
||||
let request = match {input_type}::decode(input_slice) {{
|
||||
Ok(req) => req,
|
||||
Err(_) => {{
|
||||
*output_len = 0;
|
||||
return std::ptr::null_mut();
|
||||
}}
|
||||
}};
|
||||
|
||||
let response = {fn_name}_impl(request);
|
||||
|
||||
let encoded = response.encode_to_vec();
|
||||
*output_len = encoded.len();
|
||||
|
||||
let ptr = encoded.as_ptr() as *mut u8;
|
||||
std::mem::forget(encoded);
|
||||
ptr
|
||||
}}
|
||||
"#
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_snake_case(s: &str) -> String {
|
||||
let mut result = String::new();
|
||||
for (i, c) in s.chars().enumerate() {
|
||||
if c.is_uppercase() {
|
||||
if i > 0 {
|
||||
result.push('_');
|
||||
}
|
||||
result.push(c.to_ascii_lowercase());
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("cargo::rerun-if-changed=rust/src/build.rs");
|
||||
println!("cargo::rerun-if-changed=java/build/libs/");
|
||||
env_logger::init();
|
||||
|
||||
let base = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
pub fn setup_java(base: PathBuf) {
|
||||
let resources = base.join("resources");
|
||||
let jassets = resources.join("jassets");
|
||||
let deps = resources.join("deps");
|
||||
@ -84,19 +17,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
.join("libs")
|
||||
.join("patchbukkit.jar");
|
||||
|
||||
let mut proto_path = base.clone();
|
||||
proto_path.pop();
|
||||
let proto_path = proto_path.join("proto");
|
||||
|
||||
let paths = glob(&format!("{}/**/*.proto", proto_path.display()))
|
||||
.expect("Failed to read glob pattern")
|
||||
.filter_map(|p| p.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut config = Config::new();
|
||||
config.service_generator(Box::new(FfiServiceGenerator));
|
||||
config.compile_protos(paths.as_slice(), &[proto_path])?;
|
||||
|
||||
let dependencies = [
|
||||
("com.google.guava", "guava", "33.3.1-jre"),
|
||||
(
|
||||
17
rust/build/main.rs
Normal file
17
rust/build/main.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::{java::setup_java, protobufs::setup_protobufs};
|
||||
use std::{error::Error, path::PathBuf};
|
||||
|
||||
pub mod java;
|
||||
pub mod protobufs;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("cargo::rerun-if-changed=rust/src/build.rs");
|
||||
println!("cargo::rerun-if-changed=java/build/libs/");
|
||||
env_logger::init();
|
||||
|
||||
let base = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
|
||||
setup_protobufs(base.clone());
|
||||
setup_java(base);
|
||||
Ok(())
|
||||
}
|
||||
113
rust/build/protobufs.rs
Normal file
113
rust/build/protobufs.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use glob::glob;
|
||||
use prost_build::{Config, Service, ServiceGenerator};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct FfiServiceGenerator {
|
||||
impl_module: String,
|
||||
proto_module: String,
|
||||
}
|
||||
|
||||
impl FfiServiceGenerator {
|
||||
pub fn new(impl_module: impl Into<String>, proto_module: impl Into<String>) -> Self {
|
||||
Self {
|
||||
impl_module: impl_module.into(),
|
||||
proto_module: proto_module.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn proto_type_to_rust(&self, proto_type: &str) -> String {
|
||||
let trimmed = proto_type.trim_start_matches('.');
|
||||
|
||||
if let Some(type_name) = trimmed.strip_prefix("google.protobuf.") {
|
||||
return format!("::prost_types::{type_name}");
|
||||
}
|
||||
|
||||
let parts: Vec<&str> = trimmed.split('.').collect();
|
||||
let (modules, type_name) = parts.split_at(parts.len() - 1);
|
||||
|
||||
let module_path = modules
|
||||
.iter()
|
||||
.map(|s| to_snake_case(s))
|
||||
.collect::<Vec<_>>()
|
||||
.join("::");
|
||||
let type_name = to_pascal_case(type_name[0]);
|
||||
|
||||
format!("{}::{module_path}::{type_name}", self.proto_module)
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceGenerator for FfiServiceGenerator {
|
||||
fn generate(&mut self, service: Service, buf: &mut String) {
|
||||
for method in &service.methods {
|
||||
let fn_name = format!("ffi_{}_{}", to_snake_case(&service.name), &method.name);
|
||||
let input_type = self.proto_type_to_rust(&method.input_proto_type);
|
||||
|
||||
buf.push_str(&format!(
|
||||
r#"
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn {fn_name}(
|
||||
input_ptr: *const u8,
|
||||
input_len: usize,
|
||||
output_len: *mut usize,
|
||||
) -> *mut u8 {{
|
||||
use prost::Message;
|
||||
let input_slice = unsafe {{ std::slice::from_raw_parts(input_ptr, input_len) }};
|
||||
let Ok(request) = {input_type}::decode(input_slice) else {{
|
||||
unsafe {{ *output_len = 0 }};
|
||||
return std::ptr::null_mut();
|
||||
}};
|
||||
let Some(response) = {0}::{fn_name}_impl(request) else {{
|
||||
unsafe {{ *output_len = 0 }};
|
||||
return std::ptr::null_mut();
|
||||
}};
|
||||
let encoded = response.encode_to_vec();
|
||||
unsafe {{ *output_len = encoded.len() }};
|
||||
let ptr = encoded.as_ptr() as *mut u8;
|
||||
std::mem::forget(encoded);
|
||||
ptr
|
||||
}}
|
||||
"#,
|
||||
self.impl_module
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_pascal_case(s: &str) -> String {
|
||||
if s.chars().all(|c| c.is_uppercase() || c.is_numeric()) {
|
||||
let mut c = s.chars();
|
||||
c.next()
|
||||
.map(|f| f.to_string() + &c.as_str().to_lowercase())
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_snake_case(s: &str) -> String {
|
||||
s.chars()
|
||||
.enumerate()
|
||||
.flat_map(|(i, c)| {
|
||||
if c.is_uppercase() && i > 0 {
|
||||
vec!['_', c.to_ascii_lowercase()]
|
||||
} else {
|
||||
vec![c.to_ascii_lowercase()]
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn setup_protobufs(base: PathBuf) {
|
||||
let proto_path = base.parent().unwrap().join("proto");
|
||||
let paths: Vec<_> = glob(&format!("{}/**/*.proto", proto_path.display()))
|
||||
.expect("Failed to read glob pattern")
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
let mut config = Config::new();
|
||||
config.service_generator(Box::new(FfiServiceGenerator::new(
|
||||
"crate::java::native_callbacks",
|
||||
"crate::proto",
|
||||
)));
|
||||
config.compile_protos(&paths, &[proto_path]).unwrap();
|
||||
}
|
||||
@ -1,11 +1,7 @@
|
||||
use prost::Message;
|
||||
|
||||
use crate::java::native_callbacks::CALLBACK_CONTEXT;
|
||||
use crate::proto::patchbukkit::common::{Abilities, SetAbilitiesRequest, Uuid};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
||||
|
||||
fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
||||
pub fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
||||
let ctx = CALLBACK_CONTEXT.get()?;
|
||||
let player_uuid = uuid::Uuid::parse_str(&request.value).unwrap();
|
||||
let mut abilities = Abilities::default();
|
||||
@ -26,11 +22,12 @@ fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
||||
return Some(abilities);
|
||||
}
|
||||
|
||||
fn ffi_native_bridge_set_abilities_impl(request: SetAbilitiesRequest) -> Option<bool> {
|
||||
pub fn ffi_native_bridge_set_abilities_impl(request: SetAbilitiesRequest) -> Option<bool> {
|
||||
let ctx = CALLBACK_CONTEXT.get()?;
|
||||
let player_uuid = uuid::Uuid::parse_str(&request.uuid?.value).unwrap();
|
||||
let abilities = Abilities::default();
|
||||
let player_uuid = uuid::Uuid::parse_str(&request.uuid?.value).ok()?;
|
||||
let abilities = request.abilities?;
|
||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
|
||||
|
||||
let mut pumpkin_abilities = tokio::task::block_in_place(|| {
|
||||
ctx.runtime
|
||||
.block_on(async { player.abilities.lock().await })
|
||||
@ -49,5 +46,5 @@ fn ffi_native_bridge_set_abilities_impl(request: SetAbilitiesRequest) -> Option<
|
||||
player.send_abilities_update().await;
|
||||
});
|
||||
|
||||
return Some(true);
|
||||
Some(true)
|
||||
}
|
||||
|
||||
@ -5,9 +5,11 @@ use j4rs::{InvocationArg, Jvm};
|
||||
use pumpkin::plugin::Context;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::java::jvm::commands::JvmCommand;
|
||||
use crate::{java::jvm::commands::JvmCommand, proto};
|
||||
|
||||
mod abilities;
|
||||
pub use abilities::*;
|
||||
|
||||
pub mod abilities;
|
||||
pub mod events;
|
||||
pub mod location;
|
||||
pub mod memory;
|
||||
@ -66,8 +68,10 @@ pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> {
|
||||
let send_message_addr = message::rust_send_message as *const () as i64;
|
||||
let register_event_addr = events::rust_register_event as *const () as i64;
|
||||
let call_event_addr = events::rust_call_event as *const () as i64;
|
||||
let get_abilities_addr = abilities::ffi_native_bridge_set_abilities as *const () as i64;
|
||||
let set_abilities_addr = abilities::ffi_native_bridge_set_abilities as *const () as i64;
|
||||
let get_abilities_addr =
|
||||
proto::patchbukkit::bridge::ffi_native_bridge_get_abilities as *const () as i64;
|
||||
let set_abilities_addr =
|
||||
proto::patchbukkit::bridge::ffi_native_bridge_set_abilities as *const () as i64;
|
||||
let get_location_addr = location::rust_get_location as *const () as i64;
|
||||
let free_string_addr = memory::rust_free_string as *const () as i64;
|
||||
let get_world_addr = world::rust_get_world as *const () as i64;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
pub mod patchbukkit {
|
||||
// pub mod bridge {
|
||||
// include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
||||
// }
|
||||
pub mod bridge {
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
||||
}
|
||||
|
||||
pub mod common {
|
||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.common.rs"));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user