From 3ce62e5a4df47ddda8a5fc1a2a76e768ebdd2545 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Fri, 20 Feb 2026 13:34:35 +0800 Subject: [PATCH 1/3] Feature: Enable developers to custom their own extra nodes. --- ffi/TerracottaAndroidAPI.java | 24 ++++++++++++++---------- src/lib.rs | 12 ++++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/ffi/TerracottaAndroidAPI.java b/ffi/TerracottaAndroidAPI.java index dbaff0e..38be999 100644 --- a/ffi/TerracottaAndroidAPI.java +++ b/ffi/TerracottaAndroidAPI.java @@ -18,6 +18,7 @@ import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -234,32 +235,35 @@ public static void setWaiting() { /** *

Set Terracotta Android into 'host-scanning' state.

* - * @param player the player's name. A default value will be taken if it's null. + * @param room the room code, which may be used if it's valid. + * @param player the player's name. A default value will be taken if it's null. + * @param extraNodes extra public server nodes for EasyTier. * @throws IllegalStateException if Terracotta Android hasn't been initialized. * @implNote Usually, this method doesn't take a long time to fetch states. * However, when initializing the EasyTier, state fetching may block for ~1 seconds. */ - public static void setScanning(@Nullable String room, @Nullable String player) { + public static void setScanning(@Nullable String room, @Nullable String player, @Nullable List extraNodes) { assertStarted(); - setScanning0(room, player); + setScanning0(room, playerm, extraNodes == null ? null : String.join("\0", extraNodes)); } /** *

Set Terracotta Android into 'guest-connecting' state.

* - * @param room the room code. False will be returned if it's invalid. - * @param player the player's name. A default value will be taken if it's null. + * @param room the room code. False will be returned if it's invalid. + * @param player the player's name. A default value will be taken if it's null. + * @param extraNodes extra public server nodes for EasyTier. * @return True if room code is valid, false otherwise. * @throws IllegalStateException if Terracotta Android hasn't been initialized. * @throws NullPointerException if room is null. * @implNote Usually, this method doesn't take a long time to fetch states. * However, when initializing the EasyTier, state fetching may block for ~1 seconds. */ - public static boolean setGuesting(String room, @Nullable String player) { + public static boolean setGuesting(String room, @Nullable String player, @Nullable List extraNodes) { Objects.requireNonNull(room, "room"); assertStarted(); - return setGuesting0(room, player); + return setGuesting0(room, player, extraNodes == null ? null : String.join("\0", extraNodes)); } /** @@ -381,7 +385,7 @@ public static void panic() { private static final long FD_PENDING = ((long) Integer.MAX_VALUE) + 1; private static final long FD_REJECT = FD_PENDING + 1; -; + @SuppressWarnings("unused") // Native callback private static int onVpnServiceStateChanged(byte ip1, byte ip2, byte ip3, byte ip4, short network_length, String cidr) throws UnknownHostException { if (pendingRequest != null) { @@ -460,9 +464,9 @@ private static void assertStarted() { private static native void setWaiting0(); - private static native void setScanning0(String room, String player); + private static native void setScanning0(String room, String player, String extraNodes); - private static native boolean setGuesting0(String room, String player); + private static native boolean setGuesting0(String room, String player, String extraNodes); private static native int verifyRoomCode0(String room); diff --git a/src/lib.rs b/src/lib.rs index 2398829..3032d54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,20 +277,24 @@ extern "system" fn jni_set_waiting<'l>(jenv: JNIEnv<'l>, _: JClass<'l>) { } } -extern "system" fn jni_set_scanning<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: JString<'l>, player: JString<'l>) { +extern "system" fn jni_set_scanning<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: JString<'l>, player: JString<'l>, extra_nodes: JString<'l>) { try_jvm! { |jenv| let room = parse_jstring(&jenv, &room); let player = parse_jstring(&jenv, &player); - controller::set_scanning(room, player, vec![]); + let extra_nodes = parse_jstring(&jenv, &extra_nodes); + let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").collect()).unwrap_or(vec![]); + controller::set_scanning(room, player, extra_nodes); } } -extern "system" fn jni_set_guesting<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: JString<'l>, player: JString<'l>) -> jboolean { +extern "system" fn jni_set_guesting<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: JString<'l>, player: JString<'l>, extra_nodes: JString<'l>) -> jboolean { try_jvm! { |jenv| let room = parse_jstring(&jenv, &room).expect("'room' must not be NULL."); let player = parse_jstring(&jenv, &player); + let extra_nodes = parse_jstring(&jenv, &extra_nodes); + let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").collect()).unwrap_or(vec![]); - if let Some(room) = Room::from(&room) && controller::set_guesting(room, player, vec![]) { + if let Some(room) = Room::from(&room) && controller::set_guesting(room, player, extra_nodes) { JNI_TRUE } else { JNI_FALSE From 2b96d812f252a913e9337e1c651959adcd2408f9 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Fri, 20 Feb 2026 13:48:51 +0800 Subject: [PATCH 2/3] Fix. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3032d54..6debfcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,7 @@ extern "system" fn jni_set_scanning<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: J let room = parse_jstring(&jenv, &room); let player = parse_jstring(&jenv, &player); let extra_nodes = parse_jstring(&jenv, &extra_nodes); - let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").collect()).unwrap_or(vec![]); + let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").map(|s| s.into()).collect()).unwrap_or(vec![]); controller::set_scanning(room, player, extra_nodes); } } @@ -292,7 +292,7 @@ extern "system" fn jni_set_guesting<'l>(jenv: JNIEnv<'l>, _: JClass<'l>, room: J let room = parse_jstring(&jenv, &room).expect("'room' must not be NULL."); let player = parse_jstring(&jenv, &player); let extra_nodes = parse_jstring(&jenv, &extra_nodes); - let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").collect()).unwrap_or(vec![]); + let extra_nodes: Vec = extra_nodes.map(|s| s.split("\0").map(|s| s.into()).collect()).unwrap_or(vec![]); if let Some(room) = Room::from(&room) && controller::set_guesting(room, player, extra_nodes) { JNI_TRUE From 7d3f2f624aba2abc23068e1b710083073cab2b16 Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 7 Mar 2026 13:56:41 +0800 Subject: [PATCH 3/3] Fix: invalid signatures in register_native_methods --- ffi/TerracottaAndroidAPI.java | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ffi/TerracottaAndroidAPI.java b/ffi/TerracottaAndroidAPI.java index 38be999..a307e63 100644 --- a/ffi/TerracottaAndroidAPI.java +++ b/ffi/TerracottaAndroidAPI.java @@ -244,7 +244,7 @@ public static void setWaiting() { */ public static void setScanning(@Nullable String room, @Nullable String player, @Nullable List extraNodes) { assertStarted(); - setScanning0(room, playerm, extraNodes == null ? null : String.join("\0", extraNodes)); + setScanning0(room, player, extraNodes == null ? null : String.join("\0", extraNodes)); } /** diff --git a/src/lib.rs b/src/lib.rs index 7a2b543..ba2efdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,8 +138,8 @@ extern "system" fn JNI_OnLoad(vm: JavaVM, _: *mut c_void) -> jint { of!["start0", "(Ljava/lang/String;I)I", jni_start], of!["getState0", "()Ljava/lang/String;", jni_get_state], of!["setWaiting0", "()V", jni_set_waiting], - of!["setScanning0", "(Ljava/lang/String;Ljava/lang/String;)V", jni_set_scanning], - of!["setGuesting0", "(Ljava/lang/String;Ljava/lang/String;)Z", jni_set_guesting], + of!["setScanning0", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", jni_set_scanning], + of!["setGuesting0", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", jni_set_guesting], of!["verifyRoomCode0", "(Ljava/lang/String;)I", jni_verify_room_code], of!["getMetadata0", "()Ljava/lang/String;", jni_get_metadata], of!["prepareExportLogs0", "()J", jni_prepare_export_logs],