@@ -27,7 +27,8 @@ import java.util.concurrent.ConcurrentHashMap
2727object PacketSender {
2828
2929 private val playerConnectionMap = ConcurrentHashMap <String , Any >()
30- private var sendPacketMethod: ClassMethod ? = null
30+ private var sendPacketMethod: Any? = null
31+ private var isFallbackMethod = false
3132
3233 private var newPacketBundlePacket: Constructor <* >? = null
3334 private var useMinecraftMethod = false
@@ -66,19 +67,88 @@ object PacketSender {
6667 // 之前通过 TinyProtocol 的 channel.pipeline().writeAndFlush() 暴力发包会有概率出问题
6768 val connection = getConnection(player)
6869 if (sendPacketMethod == null ) {
69- val reflexClass = ReflexClass .of(connection.javaClass)
70- // 1.18 更名为 send 方法
71- sendPacketMethod = if (MinecraftVersion .isHigherOrEqual(MinecraftVersion .V1_18 )) {
70+ // 在混合服务端下,直接使用原生反射避免触发类型加载
71+ if (MinecraftVersion .isCatServer) {
72+ sendPacketMethod = getSendPacketMethodHybrid(connection, packet)
73+ isFallbackMethod = true
74+ } else {
7275 try {
73- reflexClass.getMethod(" send" , true , true , packet)
74- } catch (_: NoSuchMethodException ) {
75- reflexClass.getMethod(" sendPacket" , true , true , packet)
76+ val reflexClass = ReflexClass .of(connection.javaClass)
77+ sendPacketMethod = if (MinecraftVersion .isHigherOrEqual(MinecraftVersion .V1_18 )) {
78+ try {
79+ reflexClass.getMethod(" send" , true , true , packet)
80+ } catch (_: NoSuchMethodException ) {
81+ reflexClass.getMethod(" sendPacket" , true , true , packet)
82+ }
83+ } else {
84+ reflexClass.getMethod(" sendPacket" , true , true , packet)
85+ }
86+ isFallbackMethod = false
87+ } catch (e: Exception ) {
88+ // 如果 ReflexClass 获取方法失败,使用回退方案
89+ sendPacketMethod = getSendPacketMethodHybrid(connection, packet)
90+ isFallbackMethod = true
91+ }
92+ }
93+ }
94+
95+ // 根据方法类型调用
96+ if (isFallbackMethod) {
97+ (sendPacketMethod as java.lang.reflect.Method ).invoke(connection, packet)
98+ } else {
99+ (sendPacketMethod as ClassMethod ).invoke(connection, packet)
100+ }
101+ }
102+
103+ /* *
104+ * 获取发送数据包方法的回退方案
105+ * 用于处理混合服务端(CatServer等)的兼容性问题
106+ *
107+ * 注意:在混合服务端下必须使用原生 Java 反射
108+ * 因为 TabooLib 的 Reflex API 会尝试获取父类和返回类型
109+ * 这会触发 Class.forName() 加载混淆的类名,导致 CatServer 的 remapper 报错
110+ */
111+ private fun getSendPacketMethodHybrid (connection : Any , packet : Any ): java.lang.reflect.Method {
112+ val connectionClass = connection.javaClass
113+
114+ val methodNames = if (MinecraftVersion .isHigherOrEqual(MinecraftVersion .V1_18 )) {
115+ listOf (" send" , " a" , " sendPacket" )
116+ } else {
117+ listOf (" sendPacket" , " a" , " send" )
118+ }
119+
120+ // 首先尝试查找接受 Packet 基类或接口的方法(更通用)
121+ for (methodName in methodNames) {
122+ for (method in connectionClass.declaredMethods) {
123+ if (method.name == methodName && method.parameterCount == 1 ) {
124+ val paramType = method.parameterTypes[0 ]
125+ // 检查参数类型是否是 Packet 基类或接口
126+ if (paramType.isAssignableFrom(packet.javaClass)) {
127+ method.isAccessible = true
128+ return method
129+ }
76130 }
77- } else {
78- reflexClass.getMethod(" sendPacket" , true , true , packet)
79131 }
80132 }
81- sendPacketMethod!! .invoke(connection, packet)
133+
134+ // 如果找不到通用方法,尝试精确匹配具体的数据包类型
135+ for (methodName in methodNames) {
136+ try {
137+ val method = connectionClass.getDeclaredMethod(methodName, packet.javaClass)
138+ method.isAccessible = true
139+ // 缓存找到的方法
140+ return method
141+ } catch (_: NoSuchMethodException ) {
142+ // 尝试下一个方法名
143+ }
144+ }
145+
146+ throw NoSuchMethodException (
147+ " Failed to find sendPacket method. " +
148+ " Connection type: ${connectionClass.name} , " +
149+ " Packet type: ${packet.javaClass.name} , " +
150+ " Server: ${if (MinecraftVersion .isCatServer) " Hybrid Server" else " Standard Server" } "
151+ )
82152 }
83153
84154 /* *
@@ -88,16 +158,88 @@ object PacketSender {
88158 return if (playerConnectionMap.containsKey(player.name)) {
89159 playerConnectionMap[player.name]!!
90160 } else {
91- val connection = if (MinecraftVersion .isUniversal) {
92- player.getProperty<Any >(" entity/connection" )!!
161+ val connection = if (! MinecraftVersion .isCatServer) {
162+ // 标准方式获取连接
163+ if (MinecraftVersion .isUniversal) {
164+ player.getProperty<Any >(" entity/connection" )!!
165+ } else {
166+ player.getProperty<Any >(" entity/playerConnection" )!!
167+ }
93168 } else {
94- player.getProperty<Any >(" entity/playerConnection" )!!
169+ // 因为 getProperty 会尝试获取字段类型,触发 Class.forName() 导致 ClassNotFoundException
170+ getConnectionHybrid(player)
95171 }
96172 playerConnectionMap[player.name] = connection
97173 connection
98174 }
99175 }
100176
177+ /* *
178+ * 获取玩家连接的回退方案
179+ * 用于处理混合服务端(CatServer等)的兼容性问题
180+ *
181+ * 注意:在混合服务端下必须使用原生 Java 反射
182+ * 因为 TabooLib 的 Reflex API 会尝试获取返回类型和字段类型
183+ * 这会触发 Class.forName() 加载混淆的类名,导致 CatServer 的 remapper 报错
184+ */
185+ private fun getConnectionHybrid (player : Player ): Any {
186+ try {
187+ val playerClass = player.javaClass
188+
189+ val getHandleMethod = playerClass.getDeclaredMethod(" getHandle" )
190+ getHandleMethod.isAccessible = true
191+
192+ val entityPlayer = getHandleMethod.invoke(player)
193+ val entityPlayerClass = entityPlayer.javaClass
194+
195+ val fieldNames = if (MinecraftVersion .isUniversal) {
196+ listOf (" connection" , " b" , " c" , " playerConnection" )
197+ } else {
198+ listOf (" playerConnection" , " b" , " c" , " connection" )
199+ }
200+
201+ for (fieldName in fieldNames) {
202+ try {
203+ val field = entityPlayerClass.getDeclaredField(fieldName)
204+ field.isAccessible = true
205+ val connection = field.get(entityPlayer)
206+ if (connection != null ) {
207+ // 缓存找到的字段
208+ return connection
209+ }
210+ } catch (_: NoSuchFieldException ) {
211+ continue
212+ } catch (_: IllegalAccessException ) {
213+ continue
214+ }
215+ }
216+
217+ for (field in entityPlayerClass.declaredFields) {
218+ val fieldType = field.type.simpleName
219+ if (fieldType.contains(" Connection" ) || fieldType.contains(" PlayerConnection" )) {
220+ try {
221+ field.isAccessible = true
222+ val connection = field.get(entityPlayer)
223+ if (connection != null ) {
224+ return connection
225+ }
226+ } catch (_: Exception ) {
227+ continue
228+ }
229+ }
230+ }
231+ } catch (e: Exception ) {
232+ e.printStackTrace()
233+ }
234+
235+ // 还找不到就真没招了
236+ throw RuntimeException (
237+ " Failed to get player connection. " +
238+ " Server: ${if (MinecraftVersion .isCatServer) " Hybrid Server" else " Standard Server" } , " +
239+ " Version: ${MinecraftVersion .runningVersion} "
240+ )
241+ }
242+
101243 @SubscribeEvent
102244 private fun onJoin (e : PlayerJoinEvent ) {
103245 playerConnectionMap.remove(e.player.name)
0 commit comments