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/build/
|
||||||
java/patchbukkit/build/
|
java/patchbukkit/build/
|
||||||
java/patchbukkit/bin/
|
java/patchbukkit/bin/
|
||||||
|
!rust/build/
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
examples/*/bin
|
examples/*/bin
|
||||||
|
|||||||
@ -3,6 +3,8 @@ name = "patchbukkit"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
build = "build/main.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
|||||||
@ -1,75 +1,8 @@
|
|||||||
use std::{collections::HashSet, fs, path::PathBuf};
|
use std::{collections::HashSet, fs, path::PathBuf};
|
||||||
|
|
||||||
use glob::glob;
|
|
||||||
use j4rs::{JvmBuilder, LocalJarArtifact, MavenArtifact, MavenArtifactRepo, MavenSettings};
|
use j4rs::{JvmBuilder, LocalJarArtifact, MavenArtifact, MavenArtifactRepo, MavenSettings};
|
||||||
|
|
||||||
use prost_build::{Config, Service, ServiceGenerator};
|
pub fn setup_java(base: PathBuf) {
|
||||||
|
|
||||||
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());
|
|
||||||
let resources = base.join("resources");
|
let resources = base.join("resources");
|
||||||
let jassets = resources.join("jassets");
|
let jassets = resources.join("jassets");
|
||||||
let deps = resources.join("deps");
|
let deps = resources.join("deps");
|
||||||
@ -84,19 +17,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
.join("libs")
|
.join("libs")
|
||||||
.join("patchbukkit.jar");
|
.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 = [
|
let dependencies = [
|
||||||
("com.google.guava", "guava", "33.3.1-jre"),
|
("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::java::native_callbacks::CALLBACK_CONTEXT;
|
||||||
use crate::proto::patchbukkit::common::{Abilities, SetAbilitiesRequest, Uuid};
|
use crate::proto::patchbukkit::common::{Abilities, SetAbilitiesRequest, Uuid};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
pub fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
||||||
|
|
||||||
fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
|
||||||
let ctx = CALLBACK_CONTEXT.get()?;
|
let ctx = CALLBACK_CONTEXT.get()?;
|
||||||
let player_uuid = uuid::Uuid::parse_str(&request.value).unwrap();
|
let player_uuid = uuid::Uuid::parse_str(&request.value).unwrap();
|
||||||
let mut abilities = Abilities::default();
|
let mut abilities = Abilities::default();
|
||||||
@ -26,11 +22,12 @@ fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option<Abilities> {
|
|||||||
return Some(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 ctx = CALLBACK_CONTEXT.get()?;
|
||||||
let player_uuid = uuid::Uuid::parse_str(&request.uuid?.value).unwrap();
|
let player_uuid = uuid::Uuid::parse_str(&request.uuid?.value).ok()?;
|
||||||
let abilities = Abilities::default();
|
let abilities = request.abilities?;
|
||||||
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
|
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
|
||||||
|
|
||||||
let mut pumpkin_abilities = tokio::task::block_in_place(|| {
|
let mut pumpkin_abilities = tokio::task::block_in_place(|| {
|
||||||
ctx.runtime
|
ctx.runtime
|
||||||
.block_on(async { player.abilities.lock().await })
|
.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;
|
player.send_abilities_update().await;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Some(true);
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,11 @@ use j4rs::{InvocationArg, Jvm};
|
|||||||
use pumpkin::plugin::Context;
|
use pumpkin::plugin::Context;
|
||||||
use tokio::sync::mpsc;
|
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 events;
|
||||||
pub mod location;
|
pub mod location;
|
||||||
pub mod memory;
|
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 send_message_addr = message::rust_send_message as *const () as i64;
|
||||||
let register_event_addr = events::rust_register_event 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 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 get_abilities_addr =
|
||||||
let set_abilities_addr = abilities::ffi_native_bridge_set_abilities as *const () as i64;
|
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 get_location_addr = location::rust_get_location as *const () as i64;
|
||||||
let free_string_addr = memory::rust_free_string 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;
|
let get_world_addr = world::rust_get_world as *const () as i64;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
pub mod patchbukkit {
|
pub mod patchbukkit {
|
||||||
// pub mod bridge {
|
pub mod bridge {
|
||||||
// include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
include!(concat!(env!("OUT_DIR"), "/patchbukkit.bridge.rs"));
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub mod common {
|
pub mod common {
|
||||||
include!(concat!(env!("OUT_DIR"), "/patchbukkit.common.rs"));
|
include!(concat!(env!("OUT_DIR"), "/patchbukkit.common.rs"));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user