Skip to content

Commit 294f531

Browse files
authored
Refactor lib mode: remove rocket and expose state management API.
1 parent cb95516 commit 294f531

File tree

2 files changed

+81
-101
lines changed

2 files changed

+81
-101
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ native-dialog = "0.9.0"
5050

5151
[target.'cfg(target_os = "android")'.dependencies]
5252
easytier = { git = "https://github.com/burningtnt/EasyTier.git", branch = "develop"}
53-
jni-sys = "0.4.0"
53+
jni = { version = "0.21.1", features = ["invocation"] }
5454
# These libraries are the necessities to interact with EasyTier. DO NOT upgrade their version.
5555
uuid = "1"
5656
toml = "0"

src/lib.rs

Lines changed: 80 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
1-
#![feature(panic_backtrace_config, const_convert, const_trait_impl, unsafe_cell_access)]
1+
#![feature(panic_backtrace_config, const_convert, const_trait_impl, unsafe_cell_access, panic_update_hook, internal_output_capture, string_from_utf8_lossy_owned)]
22

33
extern crate core;
4-
#[macro_use]
5-
extern crate rocket;
64

75
#[macro_export]
86
macro_rules! logging {
97
($prefix:expr, $($arg:tt)*) => {
10-
std::println!("[{}]: {}", $prefix, std::format_args!($($arg)*));
8+
crate::logging_android(std::format!("[{}]: {}", $prefix, std::format_args!($($arg)*)));
119
};
1210
}
1311

1412
use lazy_static::lazy_static;
1513

1614
use chrono::{FixedOffset, TimeZone, Utc};
17-
use jni_sys::{jint, JNIEnv};
15+
use jni::{JNIEnv, objects::JString, strings::JavaStr, sys::{JNI_FALSE, JNI_TRUE, jboolean, jint, jobject}};
1816
use std::{
19-
env, fs,
20-
net::{IpAddr, Ipv4Addr, Ipv6Addr},
21-
sync::mpsc,
22-
thread,
23-
time::SystemTime,
17+
env, ffi::CString, fs, net::{IpAddr, Ipv4Addr, Ipv6Addr}, ptr::null_mut, sync::{Arc, Mutex}, thread
2418
};
19+
use libc::{c_char, c_int};
20+
21+
use crate::controller::Room;
2522

2623
pub mod controller;
2724
mod easytier;
2825
mod scaffolding;
29-
mod server;
3026
pub const MOTD: &'static str = "§6§l双击进入陶瓦联机大厅(请保持陶瓦运行)";
3127

3228
mod mc;
3329
mod ports;
3430

31+
type JNIRawEnv = *mut jni::sys::JNIEnv;
32+
3533
lazy_static::lazy_static! {
3634
static ref ADDRESSES: Vec<IpAddr> = {
3735
let mut addresses: Vec<IpAddr> = vec![];
@@ -79,35 +77,11 @@ lazy_static! {
7977
path
8078
};
8179
static ref MACHINE_ID_FILE: std::path::PathBuf = FILE_ROOT.join("machine-id");
82-
static ref WORKING_DIR: std::path::PathBuf = {
83-
use chrono::{Datelike, Timelike};
84-
let now = chrono::Local::now();
85-
86-
(*FILE_ROOT).join(format!(
87-
"{:04}-{:02}-{:02}-{:02}-{:02}-{:02}-{}",
88-
now.year(),
89-
now.month(),
90-
now.day(),
91-
now.hour(),
92-
now.minute(),
93-
now.second(),
94-
std::process::id()
95-
))
96-
};
97-
static ref LOGGING_FILE: std::path::PathBuf = WORKING_DIR.join("application.log");
9880
}
9981

10082
#[unsafe(no_mangle)]
10183
#[allow(non_snake_case)]
102-
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_start(_env: *mut JNIEnv) -> jint {
103-
run(tokio::runtime::Builder::new_multi_thread()
104-
.enable_all()
105-
.build()
106-
.unwrap()
107-
) as jint
108-
}
109-
110-
pub fn run(runtime: tokio::runtime::Runtime) -> i16 {
84+
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_start(_env: JNIRawEnv) -> jint {
11185
cfg_if::cfg_if! {
11286
if #[cfg(debug_assertions)] {
11387
std::panic::set_backtrace_style(std::panic::BacktraceStyle::Short);
@@ -116,10 +90,18 @@ pub fn run(runtime: tokio::runtime::Runtime) -> i16 {
11690
}
11791
}
11892

119-
cleanup();
120-
redirect_std(&*LOGGING_FILE);
93+
std::panic::update_hook(|prev, info| {
94+
let data = Arc::new(Mutex::new(Vec::<u8>::new()));
95+
std::io::set_output_capture(Some(data.clone()));
96+
prev(info);
97+
std::io::set_output_capture(None);
12198

122-
let (port_callback, port_receiver) = mpsc::channel::<u16>();
99+
let data = match Arc::try_unwrap(data) {
100+
Ok(data) => String::from_utf8_lossy_owned(data.into_inner().unwrap()),
101+
Err(data) => String::from_utf8_lossy_owned(data.lock().unwrap().clone()) // Should NOT happen.
102+
};
103+
logging_android(data);
104+
});
123105

124106
logging!(
125107
"UI",
@@ -136,82 +118,80 @@ pub fn run(runtime: tokio::runtime::Runtime) -> i16 {
136118
env!("CARGO_CFG_TARGET_ENV"),
137119
);
138120

139-
runtime.spawn(server::server_main(port_callback));
121+
if let Err(e) = tokio::runtime::Builder::new_multi_thread()
122+
.enable_all()
123+
.build() {
124+
logging!("UI", "Cannot launch tokio runtime: {:?}", e);
125+
return 2;
126+
}
140127

141128
thread::spawn(|| {
142129
lazy_static::initialize(&controller::SCAFFOLDING_PORT);
143130
lazy_static::initialize(&easytier::FACTORY);
144131
});
145132

146-
match port_receiver.recv() {
147-
Ok(port) => port as i16,
148-
Err(_) => -1,
149-
}
133+
return 0;
150134
}
151135

152-
fn redirect_std(file: &'static std::path::PathBuf) {
153-
if cfg!(debug_assertions) {
154-
return;
136+
fn logging_android(line: String) {
137+
#[link(name = "log")]
138+
unsafe extern "C" {
139+
fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
155140
}
156141

157-
let Some(parent) = file.parent() else {
158-
return;
159-
};
142+
let line = CString::new(line).unwrap();
160143

161-
if !fs::metadata(parent).is_ok() {
162-
if !fs::create_dir_all(parent).is_ok() {
163-
return;
164-
}
144+
// SAFETY: 4 is ANDROID_LOG_INFO, while pointers to tag and line are valid.
145+
unsafe {
146+
__android_log_write(4, c"hello".as_ptr(), line.as_ptr());
165147
}
148+
}
166149

167-
let Ok(logging_file) = fs::File::create(file.clone()) else {
168-
return;
169-
};
150+
#[unsafe(no_mangle)]
151+
#[allow(non_snake_case)]
152+
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_getState(env: JNIRawEnv) -> jobject {
153+
let env = unsafe { JNIEnv::from_raw(env) }.unwrap();
154+
env.new_string(serde_json::to_string(&controller::get_state()).unwrap()).unwrap().into_raw()
155+
}
170156

171-
logging!(
172-
"UI",
173-
"There will be not information on the console. Logs will be saved to {}",
174-
file.to_str().unwrap()
175-
);
157+
#[unsafe(no_mangle)]
158+
#[allow(non_snake_case)]
159+
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_setWaiting(_env: JNIRawEnv) {
160+
controller::set_waiting();
161+
}
176162

177-
use std::os::unix::io::AsRawFd;
178-
unsafe {
179-
libc::dup2(logging_file.as_raw_fd(), libc::STDOUT_FILENO);
180-
libc::dup2(logging_file.as_raw_fd(), libc::STDERR_FILENO);
181-
}
163+
#[unsafe(no_mangle)]
164+
#[allow(non_snake_case)]
165+
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_setScanning(env: JNIRawEnv, player: jobject) {
166+
let env = unsafe { JNIEnv::from_raw(env) }.unwrap();
167+
let player = parse_jstring(&env, player);
168+
169+
controller::set_scanning(player);
182170
}
183171

184-
fn cleanup() {
185-
thread::spawn(move || {
186-
let now = SystemTime::now();
187-
188-
if let Ok(value) = fs::read_dir(&*FILE_ROOT) {
189-
for file in value {
190-
if let Ok(file) = file
191-
&& file
192-
.path()
193-
.file_name()
194-
.and_then(|v| v.to_str())
195-
.is_none_or(|v| v != "terracotta.lock")
196-
&& let Ok(metadata) = file.metadata()
197-
&& let Ok(file_type) = file.file_type()
198-
&& let Ok(time) = metadata.created()
199-
&& let Ok(duration) = now.duration_since(time)
200-
&& duration.as_secs()
201-
>= if cfg!(debug_assertions) {
202-
120
203-
} else {
204-
24 * 60 * 60
205-
}
206-
&& let Err(e) = if file_type.is_dir() {
207-
fs::remove_dir_all(file.path())
208-
} else {
209-
fs::remove_file(file.path())
210-
}
211-
{
212-
logging!("UI", "Cannot remove old file {:?}: {:?}", file.path(), e);
213-
}
214-
}
215-
}
216-
});
172+
#[unsafe(no_mangle)]
173+
#[allow(non_snake_case)]
174+
extern "system" fn Java_net_burningtnt_terracotta_TerracottaAndroidAPI_setGuesting(env: JNIRawEnv, room: jobject, player: jobject) -> jboolean {
175+
let env = unsafe { JNIEnv::from_raw(env) }.unwrap();
176+
let room = parse_jstring(&env, room);
177+
let player = parse_jstring(&env, player);
178+
179+
if let Some(room) = room && let Some(room) = Room::from(&room) && controller::set_guesting(room, player) {
180+
JNI_TRUE
181+
} else {
182+
JNI_FALSE
183+
}
217184
}
185+
186+
fn parse_jstring(env: &JNIEnv<'static>, value: jobject) -> Option<String> {
187+
if value == null_mut() {
188+
None
189+
} else {
190+
// SAFETY: value is a Java String Object
191+
192+
let value = unsafe { JString::from_raw(value) };
193+
Some(<JavaStr<'_, '_, '_> as Into<String>>::into(unsafe {
194+
env.get_string_unchecked(&value)
195+
}.unwrap().into()))
196+
}
197+
}

0 commit comments

Comments
 (0)