diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 740288d8..316ddc0f 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -34,6 +34,7 @@ object Versions { const val snakeyamlVersion = "1.28" const val cloudVersion = "1.5.0" const val bstatsVersion = "3.0.0" + const val mbassadorVersion = "1.3.2" const val javaWebsocketVersion = "1.5.2" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 02fec9dd..258d98c5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { api("cloud.commandframework", "cloud-core", Versions.cloudVersion) api("org.yaml", "snakeyaml", Versions.snakeyamlVersion) api("org.bstats", "bstats-base", Versions.bstatsVersion) + api("net.engio", "mbassador", Versions.mbassadorVersion) } // present on all platforms diff --git a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java index 7b963d73..c02fcfc8 100644 --- a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java +++ b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.UUID; +import net.engio.mbassy.bus.common.PubSubSupport; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.InstanceHolder; import org.geysermc.floodgate.api.handshake.HandshakeHandlers; @@ -43,6 +44,7 @@ import org.geysermc.floodgate.api.packet.PacketHandlers; import org.geysermc.floodgate.config.ConfigLoader; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; +import org.geysermc.floodgate.event.ShutdownEvent; import org.geysermc.floodgate.link.PlayerLinkLoader; import org.geysermc.floodgate.module.ConfigLoadedModule; import org.geysermc.floodgate.module.PostInitializeModule; @@ -130,6 +132,8 @@ public class FloodgatePlatform { } public boolean disable() { + guice.getInstance(PubSubSupport.class).publish(new ShutdownEvent()); + if (injector != null && injector.canRemoveInjection()) { try { if (!injector.removeInjection()) { @@ -139,9 +143,6 @@ public class FloodgatePlatform { logger.error("Failed to remove the injection!", exception); } } - - guice.getInstance(NewsChecker.class).shutdown(); - api.getPlayerLink().stop(); return true; } diff --git a/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java b/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java index a3336e09..ede56730 100644 --- a/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java +++ b/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java @@ -25,24 +25,14 @@ package org.geysermc.floodgate.api; +import com.google.inject.Inject; import java.nio.charset.StandardCharsets; -import org.geysermc.floodgate.api.logger.FloodgateLogger; -import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.crypto.FloodgateCipher; -import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.util.BedrockData; public final class ProxyFloodgateApi extends SimpleFloodgateApi { - private final FloodgateCipher cipher; - - public ProxyFloodgateApi( - PluginMessageManager pluginMessageManager, - FloodgateConfigHolder configHolder, - FloodgateLogger logger, - FloodgateCipher cipher) { - super(pluginMessageManager, configHolder, logger); - this.cipher = cipher; - } + @Inject + private FloodgateCipher cipher; public byte[] createEncryptedData(BedrockData bedrockData) { try { diff --git a/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java b/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java index 94db6743..4fd89f33 100644 --- a/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java +++ b/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java @@ -30,13 +30,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableSet; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.inject.Inject; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import lombok.RequiredArgsConstructor; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.floodgate.api.logger.FloodgateLogger; @@ -47,10 +47,9 @@ import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.pluginmessage.channel.FormChannel; import org.geysermc.floodgate.pluginmessage.channel.TransferChannel; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpUtils; +import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.Utils; -@RequiredArgsConstructor public class SimpleFloodgateApi implements FloodgateApi { private final Map players = new HashMap<>(); private final Cache pendingRemove = @@ -58,9 +57,10 @@ public class SimpleFloodgateApi implements FloodgateApi { .expireAfterWrite(20, TimeUnit.SECONDS) .build(); - private final PluginMessageManager pluginMessageManager; - private final FloodgateConfigHolder configHolder; - private final FloodgateLogger logger; + @Inject private PluginMessageManager pluginMessageManager; + @Inject private FloodgateConfigHolder configHolder; + @Inject private HttpClient httpClient; + @Inject private FloodgateLogger logger; @Override public String getPlayerPrefix() { @@ -148,7 +148,7 @@ public class SimpleFloodgateApi implements FloodgateApi { return Utils.failedFuture(new IllegalStateException("Received an invalid gamertag")); } - return HttpUtils.asyncGet(Constants.GET_XUID_URL + gamertag) + return httpClient.asyncGet(Constants.GET_XUID_URL + gamertag) .thenApply(result -> { JsonObject response = result.getResponse(); @@ -163,7 +163,7 @@ public class SimpleFloodgateApi implements FloodgateApi { @Override public CompletableFuture getGamertagFor(long xuid) { - return HttpUtils.asyncGet(Constants.GET_GAMERTAG_URL + xuid) + return httpClient.asyncGet(Constants.GET_GAMERTAG_URL + xuid) .thenApply(result -> { JsonObject response = result.getResponse(); diff --git a/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java b/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java index 45287f9e..b87a20ee 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java @@ -49,10 +49,11 @@ import org.geysermc.floodgate.player.UserAudience; import org.geysermc.floodgate.player.audience.ProfileAudience; import org.geysermc.floodgate.player.audience.ProfileAudienceArgument; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpUtils; +import org.geysermc.floodgate.util.HttpClient; public class WhitelistCommand implements FloodgateCommand { @Inject private FloodgateConfig config; + @Inject private HttpClient httpClient; @Inject private FloodgateLogger logger; @Override @@ -128,7 +129,7 @@ public class WhitelistCommand implements FloodgateCommand { final String strippedName = name; // We need to get the UUID of the player if it's not manually specified - HttpUtils.asyncGet(Constants.GET_XUID_URL + name) + httpClient.asyncGet(Constants.GET_XUID_URL + name) .whenComplete((result, error) -> { if (error != null) { sender.sendMessage(Message.API_UNAVAILABLE); diff --git a/core/src/main/java/org/geysermc/floodgate/command/main/FirewallCheckSubcommand.java b/core/src/main/java/org/geysermc/floodgate/command/main/FirewallCheckSubcommand.java index 66f439b7..6fa989f5 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/main/FirewallCheckSubcommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/main/FirewallCheckSubcommand.java @@ -29,20 +29,40 @@ import static org.geysermc.floodgate.util.Constants.COLOR_CHAR; import cloud.commandframework.context.CommandContext; import com.google.gson.JsonElement; +import com.google.inject.Inject; import it.unimi.dsi.fastutil.Pair; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; +import org.geysermc.floodgate.command.util.Permission; +import org.geysermc.floodgate.platform.command.FloodgateSubCommand; import org.geysermc.floodgate.player.UserAudience; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpUtils; -import org.geysermc.floodgate.util.HttpUtils.HttpResponse; +import org.geysermc.floodgate.util.HttpClient; +import org.geysermc.floodgate.util.HttpClient.HttpResponse; import org.geysermc.floodgate.util.Utils; -final class FirewallCheckSubcommand { - private FirewallCheckSubcommand() {} +final class FirewallCheckSubcommand extends FloodgateSubCommand { + @Inject + private HttpClient httpClient; - static void executeFirewall(CommandContext context) { + @Override + public String name() { + return "firewall"; + } + + @Override + public String description() { + return "Check if your outgoing firewall allows Floodgate to work properly"; + } + + @Override + public Permission permission() { + return Permission.COMMAND_MAIN_FIREWALL; + } + + @Override + public void execute(CommandContext context) { UserAudience sender = context.getSender(); executeChecks( globalApiCheck(sender) @@ -54,12 +74,12 @@ final class FirewallCheckSubcommand { ); } - private static BooleanSupplier globalApiCheck(UserAudience sender) { + private BooleanSupplier globalApiCheck(UserAudience sender) { return executeFirewallText( sender, "global api", () -> { HttpResponse response = - HttpUtils.get(Constants.HEALTH_URL, JsonElement.class); + httpClient.get(Constants.HEALTH_URL, JsonElement.class); if (!response.isCodeOk()) { throw new IllegalStateException(String.format( @@ -70,7 +90,7 @@ final class FirewallCheckSubcommand { }); } - private static BooleanSupplier executeFirewallText( + private BooleanSupplier executeFirewallText( UserAudience sender, String name, Runnable runnable) { return () -> { sender.sendMessage(COLOR_CHAR + "eTesting " + name + "..."); @@ -86,9 +106,7 @@ final class FirewallCheckSubcommand { }; } - private static CompletableFuture> executeChecks( - BooleanSupplier... checks) { - + private CompletableFuture> executeChecks(BooleanSupplier... checks) { return CompletableFuture.supplyAsync(() -> { AtomicInteger okCount = new AtomicInteger(); AtomicInteger failCount = new AtomicInteger(); diff --git a/core/src/main/java/org/geysermc/floodgate/command/main/MainCommand.java b/core/src/main/java/org/geysermc/floodgate/command/main/MainCommand.java index 093289fe..f5b45e53 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/main/MainCommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/main/MainCommand.java @@ -32,14 +32,25 @@ import cloud.commandframework.Command; import cloud.commandframework.Command.Builder; import cloud.commandframework.CommandManager; import cloud.commandframework.context.CommandContext; +import com.google.inject.Inject; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; -import java.util.function.Consumer; -import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.command.util.Permission; import org.geysermc.floodgate.platform.command.FloodgateCommand; +import org.geysermc.floodgate.platform.command.FloodgateSubCommand; import org.geysermc.floodgate.player.UserAudience; public final class MainCommand implements FloodgateCommand { + private final List subCommands = new ArrayList<>(); + + @Inject + public void createSubCommands(FirewallCheckSubcommand firewallCheckSubcommand) { + //todo move subcommand logic to a separate class + subCommands.clear(); + subCommands.add(firewallCheckSubcommand); + } + @Override public Command buildCommand(CommandManager commandManager) { Builder builder = commandManager.commandBuilder( @@ -49,11 +60,11 @@ public final class MainCommand implements FloodgateCommand { .permission(Permission.COMMAND_MAIN.get()) .handler(this::execute); - for (SubCommand subCommand : SubCommand.VALUES) { + for (FloodgateSubCommand subCommand : subCommands) { commandManager.command(builder - .literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description) - .permission(subCommand.permission.get()) - .handler(subCommand.executor::accept) + .literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description()) + .permission(subCommand.permission().get()) + .handler(subCommand::execute) ); } @@ -65,27 +76,15 @@ public final class MainCommand implements FloodgateCommand { public void execute(CommandContext context) { StringBuilder helpMessage = new StringBuilder("Available subcommands are:\n"); - for (SubCommand subCommand : SubCommand.VALUES) { - if (context.getSender().hasPermission(subCommand.permission.get())) { + for (FloodgateSubCommand subCommand : subCommands) { + if (context.getSender().hasPermission(subCommand.permission().get())) { helpMessage.append('\n').append(COLOR_CHAR).append('b') .append(subCommand.name().toLowerCase(Locale.ROOT)) .append(COLOR_CHAR).append("f - ").append(COLOR_CHAR).append('7') - .append(subCommand.description); + .append(subCommand.description()); } } context.getSender().sendMessage(helpMessage.toString()); } - - @RequiredArgsConstructor - enum SubCommand { - FIREWALL("Check if your outgoing firewall allows Floodgate to work properly", - Permission.COMMAND_MAIN_FIREWALL, FirewallCheckSubcommand::executeFirewall); - - static final SubCommand[] VALUES = values(); - - final String description; - final Permission permission; - final Consumer> executor; - } } diff --git a/core/src/main/java/org/geysermc/floodgate/event/ShutdownEvent.java b/core/src/main/java/org/geysermc/floodgate/event/ShutdownEvent.java new file mode 100644 index 00000000..345c9f30 --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/event/ShutdownEvent.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.event; + +public class ShutdownEvent { +} diff --git a/core/src/main/java/org/geysermc/floodgate/event/util/ListenerAnnotationMatcher.java b/core/src/main/java/org/geysermc/floodgate/event/util/ListenerAnnotationMatcher.java new file mode 100644 index 00000000..614a6a52 --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/event/util/ListenerAnnotationMatcher.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.event.util; + +import com.google.inject.TypeLiteral; +import com.google.inject.matcher.AbstractMatcher; +import net.engio.mbassy.listener.Listener; + +public class ListenerAnnotationMatcher extends AbstractMatcher> { + @Override + public boolean matches(TypeLiteral typeLiteral) { + Class rawType = typeLiteral.getRawType(); + return rawType.isAnnotationPresent(Listener.class); + } +} diff --git a/core/src/main/java/org/geysermc/floodgate/link/CommonPlayerLink.java b/core/src/main/java/org/geysermc/floodgate/link/CommonPlayerLink.java index 0973b8d9..29c9a4ea 100644 --- a/core/src/main/java/org/geysermc/floodgate/link/CommonPlayerLink.java +++ b/core/src/main/java/org/geysermc/floodgate/link/CommonPlayerLink.java @@ -34,6 +34,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import lombok.AccessLevel; import lombok.Getter; +import net.engio.mbassy.listener.Handler; +import net.engio.mbassy.listener.Listener; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.link.LinkRequest; import org.geysermc.floodgate.api.link.PlayerLink; @@ -41,8 +43,10 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.database.config.DatabaseConfig; import org.geysermc.floodgate.database.config.DatabaseConfigLoader; +import org.geysermc.floodgate.event.ShutdownEvent; import org.geysermc.floodgate.util.InjectorHolder; +@Listener public abstract class CommonPlayerLink implements PlayerLink { @Getter(AccessLevel.PROTECTED) private final ExecutorService executorService = Executors.newFixedThreadPool(11); @@ -102,4 +106,9 @@ public abstract class CommonPlayerLink implements PlayerLink { public void stop() { executorService.shutdown(); } + + @Handler + public void onShutdown(ShutdownEvent ignored) { + stop(); + } } diff --git a/core/src/main/java/org/geysermc/floodgate/link/GlobalPlayerLinking.java b/core/src/main/java/org/geysermc/floodgate/link/GlobalPlayerLinking.java index 1ef1227e..bc57c61b 100644 --- a/core/src/main/java/org/geysermc/floodgate/link/GlobalPlayerLinking.java +++ b/core/src/main/java/org/geysermc/floodgate/link/GlobalPlayerLinking.java @@ -29,19 +29,23 @@ import static org.geysermc.floodgate.util.Constants.GET_BEDROCK_LINK; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.inject.Inject; import java.util.UUID; import java.util.concurrent.CompletableFuture; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.api.link.LinkRequestResult; import org.geysermc.floodgate.api.link.PlayerLink; -import org.geysermc.floodgate.util.HttpUtils; -import org.geysermc.floodgate.util.HttpUtils.DefaultHttpResponse; +import org.geysermc.floodgate.util.HttpClient; +import org.geysermc.floodgate.util.HttpClient.DefaultHttpResponse; import org.geysermc.floodgate.util.LinkedPlayer; import org.geysermc.floodgate.util.Utils; @Getter public class GlobalPlayerLinking extends CommonPlayerLink { + @Inject + private HttpClient httpClient; + private PlayerLink databaseImpl; public void setDatabaseImpl(PlayerLink databaseImpl) { @@ -94,7 +98,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink { return CompletableFuture.supplyAsync( () -> { DefaultHttpResponse response = - HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits()); + httpClient.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits()); // either the global api is down or it failed to return link if (!response.isCodeOk()) { @@ -144,7 +148,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink { return CompletableFuture.supplyAsync( () -> { DefaultHttpResponse response = - HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits()); + httpClient.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits()); if (!response.isCodeOk()) { getLogger().error( diff --git a/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java index f075bc22..3275b059 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -28,10 +28,17 @@ package org.geysermc.floodgate.module; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; import com.google.inject.name.Named; +import com.google.inject.spi.InjectionListener; +import com.google.inject.spi.TypeEncounter; +import com.google.inject.spi.TypeListener; import io.netty.util.AttributeKey; import java.nio.file.Path; import lombok.RequiredArgsConstructor; +import net.engio.mbassy.bus.MBassador; +import net.engio.mbassy.bus.common.PubSubSupport; +import net.engio.mbassy.bus.error.IPublicationErrorHandler.ConsoleLogger; import org.geysermc.floodgate.addon.data.HandshakeHandlersImpl; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.SimpleFloodgateApi; @@ -48,29 +55,46 @@ import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.crypto.KeyProducer; +import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.news.NewsChecker; import org.geysermc.floodgate.packet.PacketHandlersImpl; -import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinUploadManager; import org.geysermc.floodgate.util.Constants; +import org.geysermc.floodgate.util.HttpClient; import org.geysermc.floodgate.util.LanguageManager; @RequiredArgsConstructor public class CommonModule extends AbstractModule { + private final PubSubSupport eventBus = new MBassador<>(new ConsoleLogger(true)); private final Path dataDirectory; @Override protected void configure() { + bind(PubSubSupport.class).toInstance(eventBus); + // register every class that has the Listener annotation + bindListener(new ListenerAnnotationMatcher(), new TypeListener() { + @Override + public void hear(TypeLiteral type, TypeEncounter encounter) { + encounter.register((InjectionListener) eventBus::subscribe); + } + }); + + bind(HttpClient.class).in(Singleton.class); + bind(FloodgateApi.class).to(SimpleFloodgateApi.class); bind(PlatformInjector.class).to(CommonPlatformInjector.class); + bind(HandshakeHandlers.class).to(HandshakeHandlersImpl.class); + bind(HandshakeHandlersImpl.class).in(Singleton.class); bind(PacketHandlers.class).to(PacketHandlersImpl.class); bind(PacketHandlersImpl.class).asEagerSingleton(); + + bind(NewsChecker.class).in(Singleton.class); } @Provides @@ -116,12 +140,6 @@ public class CommonModule extends AbstractModule { return new LanguageManager(configHolder, logger); } - @Provides - @Singleton - public HandshakeHandlersImpl handshakeHandlers() { - return new HandshakeHandlersImpl(); - } - @Provides @Singleton public FloodgateHandshakeHandler handshakeHandler( @@ -154,8 +172,16 @@ public class CommonModule extends AbstractModule { @Provides @Singleton - public NewsChecker newsChecker(CommandUtil commandUtil, FloodgateLogger logger) { - return new NewsChecker(commandUtil, logger, Constants.GIT_BRANCH, Constants.BUILD_NUMBER); + @Named("gitBranch") + public String gitBranch() { + return Constants.GIT_BRANCH; + } + + @Provides + @Singleton + @Named("buildNumber") + public int buildNumber() { + return Constants.BUILD_NUMBER; } @Provides diff --git a/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java index bae8cf22..c479852e 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java @@ -31,12 +31,8 @@ import com.google.inject.name.Named; import java.nio.file.Path; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.SimpleFloodgateApi; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; -import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.config.ProxyFloodgateConfig; -import org.geysermc.floodgate.crypto.FloodgateCipher; -import org.geysermc.floodgate.pluginmessage.PluginMessageManager; public final class ProxyCommonModule extends CommonModule { public ProxyCommonModule(Path dataDirectory) { @@ -46,7 +42,9 @@ public final class ProxyCommonModule extends CommonModule { @Override protected void configure() { super.configure(); + bind(SimpleFloodgateApi.class).to(ProxyFloodgateApi.class); + bind(ProxyFloodgateApi.class).in(Singleton.class); } @Provides @@ -55,14 +53,4 @@ public final class ProxyCommonModule extends CommonModule { public Class floodgateConfigClass() { return ProxyFloodgateConfig.class; } - - @Provides - @Singleton - public ProxyFloodgateApi proxyFloodgateApi( - PluginMessageManager pluginMessageManager, - FloodgateConfigHolder configHolder, - FloodgateLogger logger, - FloodgateCipher cipher) { - return new ProxyFloodgateApi(pluginMessageManager, configHolder, logger, cipher); - } } diff --git a/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java index b1b4ab5a..4e544d18 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java @@ -30,29 +30,22 @@ import com.google.inject.Singleton; import com.google.inject.name.Named; import java.nio.file.Path; import org.geysermc.floodgate.api.SimpleFloodgateApi; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; -import org.geysermc.floodgate.config.FloodgateConfigHolder; -import org.geysermc.floodgate.pluginmessage.PluginMessageManager; public final class ServerCommonModule extends CommonModule { public ServerCommonModule(Path dataDirectory) { super(dataDirectory); } + @Override + protected void configure() { + bind(SimpleFloodgateApi.class).in(Singleton.class); + } + @Provides @Singleton @Named("configClass") public Class floodgateConfigClass() { return FloodgateConfig.class; } - - @Provides - @Singleton - public SimpleFloodgateApi floodgateApi( - PluginMessageManager pluginMessageManager, - FloodgateConfigHolder configHolder, - FloodgateLogger logger) { - return new SimpleFloodgateApi(pluginMessageManager, configHolder, logger); - } } diff --git a/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java b/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java index 901321c3..62be4469 100644 --- a/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java +++ b/core/src/main/java/org/geysermc/floodgate/news/NewsChecker.java @@ -27,6 +27,8 @@ package org.geysermc.floodgate.news; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.inject.Inject; +import com.google.inject.name.Named; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -35,34 +37,40 @@ import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import net.engio.mbassy.listener.Handler; +import net.engio.mbassy.listener.Listener; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.command.util.Permission; +import org.geysermc.floodgate.event.ShutdownEvent; import org.geysermc.floodgate.news.data.AnnouncementData; import org.geysermc.floodgate.news.data.BuildSpecificData; import org.geysermc.floodgate.news.data.CheckAfterData; import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.util.HttpUtils; -import org.geysermc.floodgate.util.HttpUtils.HttpResponse; -import org.geysermc.floodgate.command.util.Permission; +import org.geysermc.floodgate.util.HttpClient; +import org.geysermc.floodgate.util.HttpClient.HttpResponse; +@Listener public class NewsChecker { private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); - private final CommandUtil commandUtil; - private final FloodgateLogger logger; - private final Map activeNewsItems = new HashMap<>(); - private final String branch; - private final int build; + + @Inject + private CommandUtil commandUtil; + @Inject + private HttpClient httpClient; + @Inject + private FloodgateLogger logger; + + @Inject + @Named("gitBranch") + private String branch; + @Inject + @Named("buildNumber") + private int build; private boolean firstCheck; - public NewsChecker(CommandUtil commandUtil, FloodgateLogger logger, String branch, int build) { - this.commandUtil = commandUtil; - this.logger = logger; - this.branch = branch; - this.build = build; - } - public void start() { executorService.scheduleWithFixedDelay(this::checkNews, 0, 30, TimeUnit.MINUTES); } @@ -72,10 +80,10 @@ public class NewsChecker { } private void checkNews() { - HttpResponse response = - HttpUtils.getSilent( - Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME, - JsonArray.class); + HttpResponse response = httpClient.getSilent( + Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME, + JsonArray.class + ); JsonArray array = response.getResponse(); @@ -197,4 +205,9 @@ public class NewsChecker { public void shutdown() { executorService.shutdown(); } + + @Handler + public void onShutdown(ShutdownEvent ignored) { + shutdown(); + } } diff --git a/core/src/main/java/org/geysermc/floodgate/platform/command/FloodgateSubCommand.java b/core/src/main/java/org/geysermc/floodgate/platform/command/FloodgateSubCommand.java new file mode 100644 index 00000000..25515ddc --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/platform/command/FloodgateSubCommand.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.platform.command; + +import cloud.commandframework.context.CommandContext; +import org.geysermc.floodgate.command.util.Permission; +import org.geysermc.floodgate.player.UserAudience; + +public abstract class FloodgateSubCommand { + public abstract String name(); + + public abstract String description(); + + public abstract Permission permission(); + + public abstract void execute(CommandContext context); +} diff --git a/core/src/main/java/org/geysermc/floodgate/util/HttpUtils.java b/core/src/main/java/org/geysermc/floodgate/util/HttpClient.java similarity index 81% rename from core/src/main/java/org/geysermc/floodgate/util/HttpUtils.java rename to core/src/main/java/org/geysermc/floodgate/util/HttpClient.java index 56ef2540..c03fd49e 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/HttpUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/HttpClient.java @@ -37,34 +37,38 @@ import java.util.concurrent.Executors; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; +import net.engio.mbassy.listener.Handler; +import net.engio.mbassy.listener.Listener; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.floodgate.event.ShutdownEvent; // resources are properly closed and ignoring the original stack trace is intended @SuppressWarnings({"PMD.CloseResource", "PMD.PreserveStackTrace"}) -public class HttpUtils { - private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(); - - private static final Gson GSON = new Gson(); +@Listener +public class HttpClient { private static final String USER_AGENT = "GeyserMC/Floodgate"; - public static CompletableFuture asyncGet(String urlString) { - return CompletableFuture.supplyAsync(() -> get(urlString), EXECUTOR_SERVICE); + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private final Gson gson = new Gson(); + + public CompletableFuture asyncGet(String urlString) { + return CompletableFuture.supplyAsync(() -> get(urlString), executorService); } - public static DefaultHttpResponse get(String urlString) { + public DefaultHttpResponse get(String urlString) { return readDefaultResponse(request(urlString)); } - public static HttpResponse get(String urlString, Class clazz) { + public HttpResponse get(String urlString, Class clazz) { return readResponse(request(urlString), clazz); } - public static HttpResponse getSilent(String urlString, Class clazz) { + public HttpResponse getSilent(String urlString, Class clazz) { return readResponseSilent(request(urlString), clazz); } - private static HttpURLConnection request(String urlString) { + private HttpURLConnection request(String urlString) { HttpURLConnection connection; try { @@ -88,7 +92,7 @@ public class HttpUtils { } @NonNull - private static HttpResponse readResponse(HttpURLConnection connection, Class clazz) { + private HttpResponse readResponse(HttpURLConnection connection, Class clazz) { InputStreamReader streamReader = createReader(connection); if (streamReader == null) { return new HttpResponse<>(-1, null); @@ -96,7 +100,7 @@ public class HttpUtils { try { int responseCode = connection.getResponseCode(); - T response = GSON.fromJson(streamReader, clazz); + T response = gson.fromJson(streamReader, clazz); return new HttpResponse<>(responseCode, response); } catch (Exception ignored) { return new HttpResponse<>(-1, null); @@ -109,9 +113,7 @@ public class HttpUtils { } @NonNull - private static HttpResponse readResponseSilent( - HttpURLConnection connection, - Class clazz) { + private HttpResponse readResponseSilent(HttpURLConnection connection, Class clazz) { try { return readResponse(connection, clazz); } catch (Exception ignored) { @@ -120,7 +122,7 @@ public class HttpUtils { } @NonNull - private static DefaultHttpResponse readDefaultResponse(HttpURLConnection connection) { + private DefaultHttpResponse readDefaultResponse(HttpURLConnection connection) { InputStreamReader streamReader = createReader(connection); if (streamReader == null) { return new DefaultHttpResponse(-1, null); @@ -128,7 +130,7 @@ public class HttpUtils { try { int responseCode = connection.getResponseCode(); - JsonObject response = GSON.fromJson(streamReader, JsonObject.class); + JsonObject response = gson.fromJson(streamReader, JsonObject.class); return new DefaultHttpResponse(responseCode, response); } catch (Exception exception) { throw new RuntimeException("Failed to read response", exception); @@ -141,7 +143,7 @@ public class HttpUtils { } @Nullable - private static InputStreamReader createReader(HttpURLConnection connection) { + private InputStreamReader createReader(HttpURLConnection connection) { InputStream stream; try { stream = connection.getInputStream(); @@ -160,6 +162,11 @@ public class HttpUtils { return null; } + @Handler + public void onShutdown(ShutdownEvent ignored) { + executorService.shutdown(); + } + @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public static class HttpResponse { diff --git a/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java b/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java index 259d29a7..d7c16754 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java +++ b/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java @@ -29,6 +29,7 @@ import com.google.inject.Inject; import com.google.inject.Injector; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.annotation.DataDirectory; import java.nio.file.Path; import org.geysermc.floodgate.api.logger.FloodgateLogger; @@ -69,4 +70,9 @@ public final class VelocityPlugin { new PluginMessageModule() ); } + + @Subscribe + public void onProxyShutdown(ProxyShutdownEvent event) { + platform.disable(); + } }