Я создаю слушателей, когда канал читается и когда канал пишет в Netty. Слушатели обрабатываются в системе событий Bukkit, проблема не в этом, проблема в безопасности потоков. Bukkit Api запускается в основном потоке и должен быть синхронизирован, иначе сервер взорвется. Netty работает в нескольких потоках, поэтому перекрестная связь может быть затруднена с API Bukkit. Я спросил на форумах Bukkit, и лучший ответ, который я получил, заключался в том, чтобы создать AtomicBoolean и иметь цикл while, это решило бы проблему с синхронизацией отмены отправки и записи пакетов, но это не решает проблему изменения пакета что отправляется/пишется. когда событие вызывается в Bukkit, слушатели вызываются из каждого класса в @EventHandler. весь этот код должен быть синхронизирован с основным потоком, я не уверен, как бы я использовал синхронизацию в этом сценарии. извините за плохое форматирование кода, я не могу работать с этой системой очень хорошо.
public class ConnectionInjector extends ChannelDuplexHandler {
private User user;
private Channel channel;
private PacketRecieveEvent recieve;
private PacketSendEvent send;
private boolean isInjected = false;
private boolean isClosed = false;
public ConnectionInjector(User user) {
this.init(user.getPlayer());
}
public void close() {
if (!this.isClosed) {
this.isClosed = true;
if (this.isInjected) {
getChannel().eventLoop().submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
getChannel().pipeline().remove(ConnectionInjector.this);
return null;
}
});
this.isInjected = false;
}
}
}
public boolean isInjected() {
return this.isInjected;
}
public boolean isClosed() {
return this.isClosed;
}
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channel) {
if (this.isClosed()){
throw new IllegalStateException("Connection closed already");
}
PacketData d = new PacketData(packet);
send = new PacketSendEvent(user, d);//the event
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
public void run() {
Bukkit.getPluginManager().callEvent(send); //runnable puts it on the main thread
}
});//end of bukkit thread back on netty
if (!send.isCancelled()){// this possible happens before the above code which is very bad
try {
if (send.getPacket().getRawPacket() == null){
throw new IllegalStateException("sent packet was null: " + send.getPacket().getRawPacket().getClass().getName());
}
super.write(context, send.getPacket().getRawPacket(), channel); //possible happens before send is even created.
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void channelRead(ChannelHandlerContext context, final Object packet) throws Exception {
PacketData p = new PacketData(packet);
recieve = new PacketRecieveEvent(user, p);
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), new Runnable() {
public void run() {
Bukkit.getPluginManager().callEvent(recieve);
}
});
if (recieve.isCancelled()){
super.channelRead(context, recieve.getPacket().getRawPacket());
}
}
public void injectfakePacket(Object packet) {
if (this.isClosed()){
throw new IllegalStateException("Injector is closed");
}
this.getChannel().pipeline().context("encoder").fireChannelRead(packet);
}
public void init(Player player) {
this.channel = (Channel) Nms.getNetChannel(player);
this.channel.pipeline().addBefore("packet_handler", "epickitpvp", this);
this.isInjected = true;
}
public Channel getChannel() {
if (this.channel == null){
throw new IllegalStateException("channel is null");
}
return this.channel;
}
}