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
33extern crate core;
4- #[ macro_use]
5- extern crate rocket;
64
75#[ macro_export]
86macro_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
1412use lazy_static:: lazy_static;
1513
1614use 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 } } ;
1816use 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
2623pub mod controller;
2724mod easytier;
2825mod scaffolding;
29- mod server;
3026pub const MOTD : & ' static str = "§6§l双击进入陶瓦联机大厅(请保持陶瓦运行)" ;
3127
3228mod mc;
3329mod ports;
3430
31+ type JNIRawEnv = * mut jni:: sys:: JNIEnv ;
32+
3533lazy_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