java使用代理api声明匿名类

问题描述 投票:1回答:1

今天早上我陷入了一个从未发生在我身上的特殊情况。我正在使用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 Dynamic Proxy API。我从来没有用过它,想知道你是否知道一个可以向我解释如何去做的地方?如果你知道另一个让我感兴趣的更好的方法!

当我考虑它时,我认为这对于一小段代码来说真的很麻烦x)

编辑:我的插件使用PacketPlayOutCustomPayload(也就是插件消息)与玩家的mod进行通信。它允许我在特定通道(String)上发送消息(byte [])。但是在1.13中,这个String已被MinecraftKey取代(String的包装器取代了一些字符并需要使用“:”)。当玩家连接到我的1.13服务器上的1.12时,这会产生一个问题,所以我没有选择:在这种情况下我必须覆盖MinecraftKey对象。

java reflection proxy minecraft
1个回答
0
投票

我真的不认为使用代理类是一个很好的解决方案,它只会让调试变得更难,但是如果你需要类似的东西你应该使用类似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。

其他解决方案可能是使用一些模板引擎和预处理器在构建时生成java源,请参阅fastutil如何执行此操作:https://github.com/vigna/fastutil

而另一个简单类和代码部分的解决方案是使用内置的javascript或外部脚本语言(如groovy)来创建这个模式行,但是在运行时。但我会把它用于最简单的东西。

另外,只需使用方法,您就可以使用普通反射。

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

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.