Java使用代理API声明匿名类

Th0rgal

今天早上,我陷入一个从未发生过的特殊情况。我正在使用Minecraft服务器API开发Minecraft插件,该API通常被称为NMS,并参考其软件包的名称(例如,版本1.13的net.minecraft.server.v1_13_R1)。

使用Minecraft服务器API的主要问题是很难编写跨版本代码:实际上,软件包的名称会随每个新版本而变化。

当插件仅支持两个版本时,根据版本,通常更容易使用接口编写两个不同的代码。但是,当您必须支持十几种不同的版本(这就是我的情况)时,这是个坏主意(该插件太重了,它必须导入IDE中的每个jar,并且我必须重做代码)每个新版本)。在这些情况下,我通常使用反射,但我认为这是不可能的:

            packet = packetConstructor.newInstance(
                    new MinecraftKey("q", "q") {
                        @Override
                        public String toString() {
                            return "FML|HS";
                        }
                    },
                    packetDataSerializerConstructor.newInstance(Unpooled.wrappedBuffer(data)));

您可能已经猜到了MinecraftKey是NMS的一个类,并被告知要使用Java动态代理API。我从未使用过它,并且想知道您是否会找到一个可以向我说明如何简单地做的地方?如果您知道另一种让我感兴趣的更好的方法!

当我考虑它时,我认为这对于一小段代码x来说确实很麻烦。

编辑:我的插件使用PacketPlayOutCustomPayload(又名插件消息)与播放器的mod进行通信。它允许我在特定的通道(字符串)上发送消息(字节[])。但是在1.13版本中,此字符串已由MinecraftKey替换(该字符串的包装器替换了一些字符并需要使用“:”)。当玩家连接到我的1.13服务器上的1.12时,这会带来一个问题,因此我没有选择:在这种情况下,我必须覆盖MinecraftKey对象。

最终版

我真的不认为在这里使用代理类是一个好的解决方案,它只会使调试变得更困难,但是如果您需要类似的东西,则应使用ByteBuddy之类的库:(由于Java不能为类生成代理,因此允许接口)

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import static net.bytebuddy.matcher.ElementMatchers.*;

public class Main {
    public static void main(String[] args) throws Exception {
        SomeKey someKey = new SomeKey("my", "key");
        System.out.println(someKey); // :<
        // this class should be cached/saved somewhere, do not create new one each time.
        Class<? extends SomeKey> loaded = new ByteBuddy()
                .subclass(SomeKey.class)
                .method(named("toString").and(returns(String.class).and(takesArguments(0))))
                .intercept(FixedValue.value("something"))
                .make()
                .load(Main.class.getClassLoader()).getLoaded();
        someKey = loaded.getConstructor(String.class, String.class).newInstance("what", "ever");
        System.out.println(someKey); // YeY
    }
}
class SomeKey {
    final String group;
    final String name;
    public SomeKey(String group, String name) {
        this.group = group;
        this.name = name;
    }
    public String getGroup() { return this.group; }
    public String getName() { return this.name; }
    @Override public String toString() {
        return group+":"+name;
    }
}

但是我只是在项目中创建单独的模块,该模块仅与真正的bukkit API配合使用,并且包含许多接口,这些接口以某种标准化和易读的方式表示NMS类型。
每个版本都有单独的模块,那么您将不需要重复太多的代码,因为大多数代码将由该“核心/基础”模块进行抽象和处理。
然后,您可以将其构建为单个胖jar或每个版本单独的.jar。

其他解决方案可能是使用一些模板引擎和预处理器在构建时生成Java源,请参见fastutil的执行方式:https//github.com/vigna/fastutil

对于简单类和部分代码的另一种解决方案是使用内置javascript或groovy之类的外部脚本语言来在运行时创建此模式行。但是我只会将其用于最简单的东西。

同样对于仅使用方法,您也可以使用法线反射。

您也可以随时注入netty,而不必使用默认的数据包序列化程序,而只是写自己的字节,那么根本就不需要该密钥。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章