diff --git a/api/src/main/java/com/github/retrooper/packetevents/PacketEvents.java b/api/src/main/java/com/github/retrooper/packetevents/PacketEvents.java index 39e504632..caab51c35 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/PacketEvents.java +++ b/api/src/main/java/com/github/retrooper/packetevents/PacketEvents.java @@ -18,12 +18,20 @@ package com.github.retrooper.packetevents; +import com.github.retrooper.packetevents.injector.ChannelInjector; +import com.github.retrooper.packetevents.manager.player.PlayerManager; +import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; +import com.github.retrooper.packetevents.manager.server.ServerManager; +import com.github.retrooper.packetevents.netty.NettyManager; +import com.github.retrooper.packetevents.settings.PacketEventsSettings; +import com.github.retrooper.packetevents.util.LogManager; + public final class PacketEvents { private static PacketEventsAPI API; //Put these variable names anywhere else, they are really only for the injectors public static String IDENTIFIER, ENCODER_NAME, DECODER_NAME, CONNECTION_HANDLER_NAME, SERVER_CHANNEL_HANDLER_NAME, - TIMEOUT_HANDLER_NAME; + TIMEOUT_HANDLER_NAME; public static PacketEventsAPI getAPI() { return API; @@ -32,4 +40,87 @@ public final class PacketEvents { public static void setAPI(PacketEventsAPI api) { API = api; } + + public static Builder builder() { + return new Builder(); + } + + public abstract static class Context { + private final Object pluginInstance; + private final ProtocolManager protocolManager; + private final ServerManager serverManager; + private final PlayerManager playerManager; + private final NettyManager nettyManager; + private final ChannelInjector injector; + private final LogManager logManager; + protected boolean lateBind = false; + + public Context(Object pluginInstance, ProtocolManager protocolManager, ServerManager serverManager, PlayerManager playerManager, + NettyManager nettyManager, ChannelInjector injector, LogManager logManager) { + this.pluginInstance = pluginInstance; + this.protocolManager = protocolManager; + this.serverManager = serverManager; + this.playerManager = playerManager; + this.nettyManager = nettyManager; + this.injector = injector; + this.logManager = logManager; + } + + protected abstract PacketEventsAPI build(); + + public Object getPluginInstance() { + return pluginInstance; + } + + public ProtocolManager getProtocolManager() { + return protocolManager; + } + + public ServerManager getServerManager() { + return serverManager; + } + + public PlayerManager getPlayerManager() { + return playerManager; + } + + public NettyManager getNettyManager() { + return nettyManager; + } + + public ChannelInjector getInjector() { + return injector; + } + + public LogManager getLogManager() { + return logManager; + } + } + + public static class Builder { + private Context context = null; + private PacketEventsSettings settings = new PacketEventsSettings(); + public Builder context(Context context) { + this.context = context; + return this; + } + + public Builder settings(PacketEventsSettings settings) { + this.settings = settings; + return this; + } + + public PacketEventsAPI build() { + if (context == null) { + throw new IllegalStateException("Failed to build a PacketEvents instance. Please specify a PacketEvents context in the PacketEvents builder to allow PacketEvents to load the correct implementation."); + } + PacketEventsAPI api = context.build(); + api.setSettings(settings); + //The implementation may have already set the API. + if (getAPI() == null) { + setAPI(api); + } + return api; + } + } } diff --git a/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java b/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java index cf7d991fe..b55e71411 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java +++ b/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java @@ -33,10 +33,10 @@ import java.util.logging.Logger; public abstract class PacketEventsAPI { private static final EventManager EVENT_MANAGER = new EventManager(); - private static final PacketEventsSettings SETTINGS = new PacketEventsSettings(); private static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(); - private final Logger LOGGER = Logger.getLogger(PacketEventsAPI.class.getName()); private static final LogManager LOG_MANAGER = new LogManager(); + private final Logger LOGGER = Logger.getLogger(PacketEventsAPI.class.getName()); + private PacketEventsSettings settings = new PacketEventsSettings(); //TODO UPDATE private static final PEVersion VERSION = new PEVersion(2, 0, 0); @@ -45,7 +45,11 @@ public abstract class PacketEventsAPI { } public PacketEventsSettings getSettings() { - return SETTINGS; + return settings; + } + + public void setSettings(PacketEventsSettings settings) { + this.settings = settings; } public UpdateChecker getUpdateChecker() { diff --git a/spigot/src/main/java/io/github/retrooper/packetevents/PacketEventsPlugin.java b/spigot/src/main/java/io/github/retrooper/packetevents/PacketEventsPlugin.java index 6b10f8dfd..2634dec8c 100644 --- a/spigot/src/main/java/io/github/retrooper/packetevents/PacketEventsPlugin.java +++ b/spigot/src/main/java/io/github/retrooper/packetevents/PacketEventsPlugin.java @@ -40,6 +40,7 @@ import com.github.retrooper.packetevents.util.Vector3f; import com.github.retrooper.packetevents.util.Vector3i; import com.github.retrooper.packetevents.wrapper.play.client.*; import com.github.retrooper.packetevents.wrapper.play.server.*; +import io.github.retrooper.packetevents.context.SpigotContext; import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -63,6 +64,7 @@ public class PacketEventsPlugin extends JavaPlugin { //Register your listeners PacketEvents.getAPI().getSettings().debug(false).bStats(true) .checkForUpdates(true).timeStampMode(TimeStampMode.MILLIS).readOnlyListeners(false); + //PacketEvents.getAPI().load(); PacketEvents.getAPI().init(); SimplePacketListenerAbstract listener = new SimplePacketListenerAbstract(PacketListenerPriority.HIGH) { @Override @@ -81,6 +83,7 @@ public class PacketEventsPlugin extends JavaPlugin { new Vector3d(player.getLocation().getX(), player.getLocation().getY() + 1, player.getLocation().getZ()), new Vector3f(0.65F, 0, 0.65F), 1, 100)); user.sendMessage("Sent!"); + player.sendMessage("User and player object both work!!!"); } break; case PLAYER_FLYING: @@ -167,7 +170,7 @@ public class PacketEventsPlugin extends JavaPlugin { System.out.println("User: (host-name) " + event.getUser().getAddress().getHostString() + " disconnected..."); } }; - //PacketEvents.getAPI().getEventManager().registerListener(listener); + PacketEvents.getAPI().getEventManager().registerListener(listener); } @Override diff --git a/spigot/src/main/java/io/github/retrooper/packetevents/context/SpigotContext.java b/spigot/src/main/java/io/github/retrooper/packetevents/context/SpigotContext.java new file mode 100644 index 000000000..212f9d361 --- /dev/null +++ b/spigot/src/main/java/io/github/retrooper/packetevents/context/SpigotContext.java @@ -0,0 +1,239 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2022 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.context; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.injector.ChannelInjector; +import com.github.retrooper.packetevents.manager.InternalPacketListener; +import com.github.retrooper.packetevents.manager.player.PlayerManager; +import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; +import com.github.retrooper.packetevents.manager.server.ServerManager; +import com.github.retrooper.packetevents.netty.NettyManager; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.settings.PacketEventsSettings; +import com.github.retrooper.packetevents.util.PEVersion; +import io.github.retrooper.packetevents.bstats.Metrics; +import io.github.retrooper.packetevents.bukkit.InternalBukkitListener; +import io.github.retrooper.packetevents.injector.SpigotChannelInjector; +import io.github.retrooper.packetevents.manager.player.PlayerManagerImpl; +import io.github.retrooper.packetevents.manager.protocol.ProtocolManagerImpl; +import io.github.retrooper.packetevents.manager.server.ServerManagerImpl; +import io.github.retrooper.packetevents.netty.NettyManagerImpl; +import io.github.retrooper.packetevents.util.BukkitLogManager; +import io.github.retrooper.packetevents.util.SpigotReflectionUtil; +import io.github.retrooper.packetevents.util.protocolsupport.ProtocolSupportUtil; +import io.github.retrooper.packetevents.util.viaversion.CustomPipelineUtil; +import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class SpigotContext extends PacketEvents.Context { + public SpigotContext(Plugin plugin) { + super(plugin, new ProtocolManagerImpl(), new ServerManagerImpl(), new PlayerManagerImpl(), + new NettyManagerImpl(), new SpigotChannelInjector(), new BukkitLogManager()); + } + + @Override + protected PacketEventsAPI build() { + PacketEventsAPI api = new PacketEventsAPI() { + @Override + public void load() { + //TODO DO WE REALLY NEED THE INIT FUNCTION + if (getSettings().shouldCheckForUpdates()) { + getUpdateChecker().handleUpdateCheck(); + } + + if (getSettings().isbStatsEnabled()) { + Metrics metrics = new Metrics((JavaPlugin) getPlugin(), 11327); + //Just to have an idea what versions of packetevents people use + metrics.addCustomChart(new Metrics.SimplePie("packetevents_version", () -> { + return getVersion().toString() + "-beta";//TODO Cut off "-beta" once 2.0 releases + })); + } + Bukkit.getPluginManager().registerEvents(new InternalBukkitListener(), + getPlugin()); + //TODO Clean up and remove redundant post inject task? or is it? + Runnable postInjectTask = () -> { + /*for (final Player p : Bukkit.getOnlinePlayers()) { + try { + Object channel = PacketEvents.getAPI().getPlayerManager().getChannel(p); + User user = PacketEvents.getAPI().getPlayerManager().getUser(p); + injector.updatePlayer(user, p); + getEventManager().callEvent(new UserLoginEvent(user, p)); + } catch (Exception ex) { + p.kickPlayer("Failed to inject... Please rejoin!"); + ex.printStackTrace(); + } + }*/ + }; + + if (SpigotContext.this.lateBind) { + //If late-bind is enabled, we still need to inject (after all plugins enabled). + Runnable lateBindTask = () -> { + if (getInjector().isServerBound()) { + getInjector().inject(); + postInjectTask.run(); + } + }; + Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), lateBindTask); + } else { + postInjectTask.run(); + } + + checkCompatibility(); + } + + @Override + public boolean isLoaded() { + return false; + } + + @Override + public void init() { + + } + + @Override + public boolean isInitialized() { + return false; + } + + @Override + public void terminate() { + //Uninject the injector if needed(depends on the injector implementation) + getInjector().uninject(); + //Unregister all listeners. Because if we attempt to reload, we will end up with duplicate listeners. + getEventManager().unregisterAllListeners(); + } + + @Override + public Plugin getPlugin() { + return (Plugin) SpigotContext.this.getPluginInstance(); + } + + @Override + public ServerManager getServerManager() { + return SpigotContext.this.getServerManager(); + } + + @Override + public ProtocolManager getProtocolManager() { + return SpigotContext.this.getProtocolManager(); + } + + @Override + public PlayerManager getPlayerManager() { + return SpigotContext.this.getPlayerManager(); + } + + @Override + public NettyManager getNettyManager() { + return SpigotContext.this.getNettyManager(); + } + + @Override + public ChannelInjector getInjector() { + return SpigotContext.this.getInjector(); + } + + + private void checkCompatibility() { + // PacketEvents is now enabled, we can now check + ViaVersionUtil.checkIfViaIsPresent(); + ProtocolSupportUtil.checkIfProtocolSupportIsPresent(); + //If ProtocolSupport is present, it needs to be x or newer + Plugin protocolSupportPlugin = Bukkit.getPluginManager().getPlugin("ProtocolSupport"); + if (protocolSupportPlugin != null) { + String psVersionString = protocolSupportPlugin.getDescription().getVersion().split("-")[0]; + PEVersion psVersion; + try { + psVersion = new PEVersion(psVersionString); + } + catch (Exception ex) { + PacketEvents.getAPI().getLogManager().severe("You are attempting to combine 2.0 PacketEvents with a " + + "ProtocolSupport version older than v1.18.1-1. (Failed to parse the ProtocolSupport version)" + + "This is no longer works, please update to a newer build. " + + "https://www.spigotmc.org/resources/protocolsupport.7201/"); + Plugin ourPlugin = getPlugin(); + Bukkit.getPluginManager().disablePlugin(ourPlugin); + throw new IllegalStateException("ProtocolSupport incompatibility! Update to v1.18.1-1 or newer!"); + } + PEVersion minimumPSVersion = new PEVersion(1, 18, 1); + if (psVersion.isOlderThan(minimumPSVersion)) { + PacketEvents.getAPI().getLogManager().severe("You are attempting to combine 2.0 PacketEvents with a " + + "ProtocolSupport version older than v1.18.1-1. (" + psVersion.toString() + ") " + + "This is no longer works, please update to a newer build. " + + "https://www.spigotmc.org/resources/protocolsupport.7201/"); + Plugin ourPlugin = getPlugin(); + Bukkit.getPluginManager().disablePlugin(ourPlugin); + throw new IllegalStateException("ProtocolSupport incompatibility! Update to v1.18.1-1 or newer!"); + } + } + //If ProtocolLib is present, it needs to be v5.0.0 or newer + Plugin protocolLibPlugin = Bukkit.getPluginManager().getPlugin("ProtocolLib"); + if (protocolLibPlugin != null) { + int majorVersion = Integer.parseInt(protocolLibPlugin.getDescription().getVersion().split("\\.", 2)[0]); + if (majorVersion < 5) { + PacketEvents.getAPI().getLogManager().severe("You are attempting to combine 2.0 PacketEvents with a " + + "ProtocolLib version older than v5.0.0. " + + "This is no longer works, please update to their dev builds. " + + "https://ci.dmulloy2.net/job/ProtocolLib/lastBuild/"); + Plugin ourPlugin = getPlugin(); + Bukkit.getPluginManager().disablePlugin(ourPlugin); + throw new IllegalStateException("ProtocolLib incompatibility! Update to v5.0.0 or newer!"); + } + } + } + }; + //Allow the API handle usage globally + PacketEvents.setAPI(api); + + //Resolve server version and cache + String id = ((Plugin)(SpigotContext.this.getPluginInstance())).getName().toLowerCase(); + PacketEvents.IDENTIFIER = "pe-" + id; + PacketEvents.ENCODER_NAME = "pe-encoder-" + id; + PacketEvents.DECODER_NAME = "pe-decoder-" + id; + PacketEvents.CONNECTION_HANDLER_NAME = "pe-connection-handler-" + id; + PacketEvents.SERVER_CHANNEL_HANDLER_NAME = "pe-connection-initializer-" + id; + PacketEvents.TIMEOUT_HANDLER_NAME = "pe-timeout-handler-" + id; + try { + SpigotReflectionUtil.init(api.getServerManager().getVersion()); + CustomPipelineUtil.init(); + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + + if (!PacketType.isPrepared()) { + PacketType.prepare(); + } + //Server hasn't bound to the port yet. + SpigotContext.this.lateBind = !getInjector().isServerBound(); + //If late-bind is enabled, we will inject a bit later. + if (!SpigotContext.this.lateBind) { + getInjector().inject(); + } + + //Register internal packet listener (should be the first listener) + //This listener doesn't do any modifications to the packets, just reads data + api.getEventManager().registerListener(new InternalPacketListener()); + return api; + } +} diff --git a/spigot/src/main/java/io/github/retrooper/packetevents/factory/spigot/SpigotPacketEventsBuilder.java b/spigot/src/main/java/io/github/retrooper/packetevents/factory/spigot/SpigotPacketEventsBuilder.java index 70a7b497f..755aa5172 100644 --- a/spigot/src/main/java/io/github/retrooper/packetevents/factory/spigot/SpigotPacketEventsBuilder.java +++ b/spigot/src/main/java/io/github/retrooper/packetevents/factory/spigot/SpigotPacketEventsBuilder.java @@ -98,7 +98,7 @@ public class SpigotPacketEventsBuilder { PacketEvents.SERVER_CHANNEL_HANDLER_NAME = "pe-connection-initializer-" + id; PacketEvents.TIMEOUT_HANDLER_NAME = "pe-timeout-handler-" + id; try { - SpigotReflectionUtil.init(); + SpigotReflectionUtil.init(getServerManager().getVersion()); CustomPipelineUtil.init(); } catch (Exception ex) { throw new IllegalStateException(ex); diff --git a/spigot/src/main/java/io/github/retrooper/packetevents/util/SpigotReflectionUtil.java b/spigot/src/main/java/io/github/retrooper/packetevents/util/SpigotReflectionUtil.java index 4b04e7a21..002a3010a 100644 --- a/spigot/src/main/java/io/github/retrooper/packetevents/util/SpigotReflectionUtil.java +++ b/spigot/src/main/java/io/github/retrooper/packetevents/util/SpigotReflectionUtil.java @@ -119,14 +119,14 @@ public final class SpigotReflectionUtil { GET_LEVEL_ENTITY_GETTER_ITERABLE_METHOD = Reflection.getMethod(LEVEL_ENTITY_GETTER_CLASS, Iterable.class, 0); } if (DIMENSION_MANAGER_CLASS != null) { - if (PacketEvents.getAPI().getServerManager().getVersion() == ServerVersion.V_1_16 - || PacketEvents.getAPI().getServerManager().getVersion() == ServerVersion.V_1_16_1) { + if (VERSION == ServerVersion.V_1_16 + || VERSION == ServerVersion.V_1_16_1) { GET_DIMENSION_KEY = Reflection.getMethod(NMS_WORLD_CLASS, "getTypeKey", 0); } GET_DIMENSION_MANAGER = Reflection.getMethod(NMS_WORLD_CLASS, DIMENSION_MANAGER_CLASS, 0); GET_DIMENSION_ID = Reflection.getMethod(DIMENSION_MANAGER_CLASS, int.class, 0); } - if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_16_2)) { + if (VERSION.isNewerThanOrEquals(ServerVersion.V_1_16_2)) { CODEC_ENCODE_METHOD = Reflection.getMethod(MOJANG_ENCODER_CLASS, "encodeStart", 0); DATA_RESULT_GET_METHOD = Reflection.getMethod(DATA_RESULT_CLASS, "result", 0); } @@ -158,7 +158,7 @@ public final class SpigotReflectionUtil { ENTITY_BOUNDING_BOX_FIELD = Reflection.getField(NMS_ENTITY_CLASS, BOUNDING_BOX_CLASS, 0, true); ENTITY_PLAYER_PING_FIELD = Reflection.getField(ENTITY_PLAYER_CLASS, "ping"); BYTE_BUF_IN_PACKET_DATA_SERIALIZER = Reflection.getField(NMS_PACKET_DATA_SERIALIZER_CLASS, BYTE_BUF_CLASS, 0, true); - if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_16_2)) { + if (VERSION.isNewerThanOrEquals(ServerVersion.V_1_16_2)) { DIMENSION_CODEC_FIELD = Reflection.getField(DIMENSION_MANAGER_CLASS, MOJANG_CODEC_CLASS, 0); DYNAMIC_OPS_NBT_INSTANCE_FIELD = Reflection.getField(DYNAMIC_OPS_NBT_CLASS, DYNAMIC_OPS_NBT_CLASS, 0); } @@ -187,7 +187,7 @@ public final class SpigotReflectionUtil { PERSISTENT_ENTITY_SECTION_MANAGER_CLASS = getServerClass("world.level.entity.PersistentEntitySectionManager", ""); } DIMENSION_MANAGER_CLASS = getServerClass("world.level.dimension.DimensionManager", "DimensionManager"); - if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_16_2)) { + if (VERSION.isNewerThanOrEquals(ServerVersion.V_1_16_2)) { MOJANG_CODEC_CLASS = Reflection.getClassByNameWithoutException("com.mojang.serialization.Codec"); MOJANG_ENCODER_CLASS = Reflection.getClassByNameWithoutException("com.mojang.serialization.Encoder"); DATA_RESULT_CLASS = Reflection.getClassByNameWithoutException("com.mojang.serialization.DataResult"); @@ -216,8 +216,8 @@ public final class SpigotReflectionUtil { NBT_COMPRESSION_STREAM_TOOLS_CLASS = getServerClass("nbt.NBTCompressedStreamTools", "NBTCompressedStreamTools"); } - public static void init() { - VERSION = PacketEvents.getAPI().getServerManager().getVersion(); + public static void init(ServerVersion version) { + VERSION = version; V_1_19_OR_HIGHER = VERSION.isNewerThanOrEquals(ServerVersion.V_1_19); V_1_17_OR_HIGHER = VERSION.isNewerThanOrEquals(ServerVersion.V_1_17); V_1_12_OR_HIGHER = VERSION.isNewerThanOrEquals(ServerVersion.V_1_12); @@ -604,13 +604,13 @@ public final class SpigotReflectionUtil { } public static int getBlockDataCombinedId(MaterialData materialData) { - if (PacketEvents.getAPI().getServerManager().getVersion() == ServerVersion.V_1_7_10) { + if (VERSION == ServerVersion.V_1_7_10) { //TODO Finish for 1.7.10 throw new IllegalStateException("This operation is not supported yet on 1.7.10!"); } //TODO Finish int combinedID; - if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) { + if (VERSION.isNewerThanOrEquals(ServerVersion.V_1_13)) { combinedID = -1; } else { combinedID = materialData.getItemType().getId() << 4 | materialData.getData(); @@ -629,7 +629,7 @@ public final class SpigotReflectionUtil { } public static MaterialData getBlockDataByCombinedId(int combinedID) { - if (PacketEvents.getAPI().getServerManager().getVersion() == ServerVersion.V_1_7_10) { + if (VERSION == ServerVersion.V_1_7_10) { //TODO Finish for 1.7.10 throw new IllegalStateException("This operation is not supported yet on 1.7.10!"); }