Search in sources :

Example 1 with SingleLineChart

use of org.bstats.charts.SingleLineChart in project DiscordSRV by Scarsz.

the class DiscordSRV method init.

public void init() {
    if (Bukkit.getPluginManager().isPluginEnabled("PlugMan")) {
        Plugin plugMan = Bukkit.getPluginManager().getPlugin("PlugMan");
        try {
            List<String> ignoredPlugins = (List<String>) plugMan.getClass().getMethod("getIgnoredPlugins").invoke(plugMan);
            if (!ignoredPlugins.contains("DiscordSRV")) {
                ignoredPlugins.add("DiscordSRV");
            }
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ignored) {
        }
    }
    // check if the person is trying to use the plugin without updating to ASM 5
    try {
        File specialSourceFile = new File("libraries/net/md-5/SpecialSource/1.7-SNAPSHOT/SpecialSource-1.7-SNAPSHOT.jar");
        if (!specialSourceFile.exists())
            specialSourceFile = new File("bin/net/md-5/SpecialSource/1.7-SNAPSHOT/SpecialSource-1.7-SNAPSHOT.jar");
        if (specialSourceFile.exists() && DigestUtils.md5Hex(FileUtils.readFileToByteArray(specialSourceFile)).equalsIgnoreCase("096777a1b6098130d6c925f1c04050a3")) {
            DiscordSRV.warning(LangUtil.InternalMessage.ASM_WARNING.toString().replace("{specialsourcefolder}", specialSourceFile.getParentFile().getPath()));
        }
    } catch (IOException e) {
        error(e);
    }
    requireLinkModule = new RequireLinkModule();
    // start the update checker (will skip if disabled)
    if (!isUpdateCheckDisabled()) {
        if (updateChecker == null) {
            final ThreadFactory gatewayThreadFactory = new ThreadFactoryBuilder().setNameFormat("DiscordSRV - Update Checker").build();
            updateChecker = Executors.newScheduledThreadPool(1);
        }
        updateChecker.schedule(() -> {
            DiscordSRV.updateIsAvailable = UpdateUtil.checkForUpdates();
            DiscordSRV.updateChecked = true;
        }, 0, TimeUnit.SECONDS);
        updateChecker.scheduleAtFixedRate(() -> DiscordSRV.updateIsAvailable = UpdateUtil.checkForUpdates(false), 6, 6, TimeUnit.HOURS);
    }
    // shutdown previously existing jda if plugin gets reloaded
    if (jda != null)
        try {
            jda.shutdown();
            jda = null;
        } catch (Exception e) {
            error(e);
        }
    reloadAllowedMentions();
    // set proxy just in case this JVM doesn't have a proxy selector for some reason
    if (ProxySelector.getDefault() == null) {
        ProxySelector.setDefault(new ProxySelector() {

            private final List<Proxy> DIRECT_CONNECTION = Collections.unmodifiableList(Collections.singletonList(Proxy.NO_PROXY));

            public void connectFailed(URI arg0, SocketAddress arg1, IOException arg2) {
            }

            public List<Proxy> select(URI uri) {
                return DIRECT_CONNECTION;
            }
        });
    }
    // set ssl to TLSv1.2
    if (config().getBoolean("ForceTLSv12")) {
        try {
            SSLContext context = SSLContext.getInstance("TLSv1.2");
            context.init(null, null, null);
            SSLContext.setDefault(context);
        } catch (Exception ignored) {
        }
    }
    // check log4j capabilities
    boolean serverIsLog4jCapable = false;
    boolean serverIsLog4j21Capable = false;
    try {
        serverIsLog4jCapable = Class.forName("org.apache.logging.log4j.core.Logger") != null;
    } catch (ClassNotFoundException e) {
        error("Log4j classes are NOT available, console channel will not be attached");
    }
    try {
        serverIsLog4j21Capable = Class.forName("org.apache.logging.log4j.core.Filter") != null;
    } catch (ClassNotFoundException e) {
        error("Log4j 2.1 classes are NOT available, JDA messages will NOT be formatted properly");
    }
    // add log4j filter for JDA messages
    if (serverIsLog4j21Capable && jdaFilter == null) {
        try {
            Class<?> jdaFilterClass = Class.forName("github.scarsz.discordsrv.objects.log4j.JdaFilter");
            jdaFilter = (JdaFilter) jdaFilterClass.newInstance();
            ((org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager.getRootLogger()).addFilter((org.apache.logging.log4j.core.Filter) jdaFilter);
            debug("JdaFilter applied");
        } catch (Exception e) {
            error("Failed to attach JDA message filter to root logger", e);
        }
    }
    if (Debug.JDA.isVisible()) {
        LoggerContext config = ((LoggerContext) LogManager.getContext(false));
        config.getConfiguration().getLoggerConfig(LogManager.ROOT_LOGGER_NAME).setLevel(Level.ALL);
        config.updateLoggers();
    }
    if (Debug.JDA_REST_ACTIONS.isVisible()) {
        RestAction.setPassContext(true);
    }
    // http client for JDA
    Dns dns = Dns.SYSTEM;
    try {
        List<InetAddress> fallbackDnsServers = new CopyOnWriteArrayList<>(Arrays.asList(// CloudFlare resolvers
        InetAddress.getByName("1.1.1.1"), InetAddress.getByName("1.0.0.1"), // Google resolvers
        InetAddress.getByName("8.8.8.8"), InetAddress.getByName("8.8.4.4")));
        dns = new Dns() {

            // maybe drop minidns in favor of something else
            // https://github.com/dnsjava/dnsjava/blob/master/src/main/java/org/xbill/DNS/SimpleResolver.java
            // https://satreth.blogspot.com/2015/01/java-dns-query.html
            private final DnsClient client = new DnsClient();

            private int failedRequests = 0;

            @NotNull
            @Override
            public List<InetAddress> lookup(@NotNull String host) throws UnknownHostException {
                int max = config.getInt("MaximumAttemptsForSystemDNSBeforeUsingFallbackDNS");
                // >0 = falls back if goes past that amount of failed requests in a row
                if (max < 0 || (max > 0 && failedRequests < max)) {
                    try {
                        List<InetAddress> result = Dns.SYSTEM.lookup(host);
                        // reset on successful lookup
                        failedRequests = 0;
                        return result;
                    } catch (Exception e) {
                        failedRequests++;
                        DiscordSRV.error("System DNS FAILED to resolve hostname " + host + ", " + (max == 0 ? "" : failedRequests >= max ? "using fallback DNS for this request" : "switching to fallback DNS servers") + "!");
                        if (max == 0) {
                            // not using fallback
                            if (e instanceof UnknownHostException) {
                                throw e;
                            } else {
                                return null;
                            }
                        }
                    }
                }
                return lookupPublic(host);
            }

            private List<InetAddress> lookupPublic(String host) throws UnknownHostException {
                for (InetAddress dnsServer : fallbackDnsServers) {
                    try {
                        DnsMessage query = client.query(host, Record.TYPE.A, Record.CLASS.IN, dnsServer).response;
                        if (query.responseCode != DnsMessage.RESPONSE_CODE.NO_ERROR) {
                            DiscordSRV.error("DNS server " + dnsServer.getHostAddress() + " failed our DNS query for " + host + ": " + query.responseCode.name());
                        }
                        List<InetAddress> resolved = query.answerSection.stream().map(record -> record.payloadData.toString()).map(s -> {
                            try {
                                return InetAddress.getByName(s);
                            } catch (UnknownHostException e) {
                                // impossible
                                error(e);
                                return null;
                            }
                        }).filter(Objects::nonNull).distinct().collect(Collectors.toList());
                        if (resolved.size() > 0) {
                            return resolved;
                        } else {
                            DiscordSRV.error("DNS server " + dnsServer.getHostAddress() + " failed to resolve " + host + ": no results");
                        }
                    } catch (Exception e) {
                        DiscordSRV.error("DNS server " + dnsServer.getHostAddress() + " failed to resolve " + host, e);
                    }
                    // this dns server gave us an error so we move this dns server to the end of the
                    // list, effectively making it the last resort for future requests
                    fallbackDnsServers.remove(dnsServer);
                    fallbackDnsServers.add(dnsServer);
                }
                // spitting errors into the console and consuming 100% cpu
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ignored) {
                }
                UnknownHostException exception = new UnknownHostException("All DNS resolvers failed to resolve hostname " + host + ". Not good.");
                exception.setStackTrace(new StackTraceElement[] { exception.getStackTrace()[0] });
                throw exception;
            }
        };
    } catch (Exception e) {
        DiscordSRV.error("Failed to make custom DNS client", e);
    }
    Optional<Boolean> noopHostnameVerifier = config().getOptionalBoolean("NoopHostnameVerifier");
    OkHttpClient httpClient = IOUtil.newHttpClientBuilder().dns(dns).connectTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).hostnameVerifier(noopHostnameVerifier.isPresent() && noopHostnameVerifier.get() ? (hostname, sslSession) -> true : OkHostnameVerifier.INSTANCE).build();
    // set custom RestAction failure handler
    Consumer<? super Throwable> defaultFailure = RestAction.getDefaultFailure();
    RestAction.setDefaultFailure(throwable -> {
        if (throwable instanceof HierarchyException) {
            DiscordSRV.error("DiscordSRV failed to perform an action due to being lower in hierarchy than the action's target: " + throwable.getMessage());
        } else if (throwable instanceof PermissionException) {
            DiscordSRV.error("DiscordSRV failed to perform an action because the bot is missing the " + ((PermissionException) throwable).getPermission().name() + " permission: " + throwable.getMessage());
        } else if (throwable instanceof RateLimitedException) {
            DiscordSRV.error("DiscordSRV encountered rate limiting. If you are running multiple DiscordSRV instances on the same token, this is considered API abuse and risks your server being IP banned from Discord. Make one bot per server.");
        } else if (throwable instanceof ErrorResponseException) {
            if (((ErrorResponseException) throwable).getErrorCode() == 50013) {
                // Missing Permissions, too bad we don't know which one
                DiscordSRV.error("DiscordSRV received a permission error response (50013) from Discord. Unfortunately the specific error isn't provided in that response.");
                DiscordSRV.debug(Debug.JDA_REST_ACTIONS, throwable.getCause());
                return;
            }
            DiscordSRV.error("DiscordSRV encountered an unknown Discord error: " + throwable.getMessage());
        } else {
            DiscordSRV.error("DiscordSRV encountered an unknown exception: " + throwable.getMessage() + "\n" + ExceptionUtils.getStackTrace(throwable));
        }
        if (Debug.JDA_REST_ACTIONS.isVisible()) {
            Throwable cause = throwable.getCause();
            error(cause);
        }
    });
    File tokenFile = new File(getDataFolder(), ".token");
    String token;
    if (StringUtils.isNotBlank(System.getProperty("DISCORDSRV_TOKEN"))) {
        token = System.getProperty("DISCORDSRV_TOKEN");
        DiscordSRV.debug("Using bot token supplied from JVM property DISCORDSRV_TOKEN");
    } else if (StringUtils.isNotBlank(System.getenv("DISCORDSRV_TOKEN"))) {
        token = System.getenv("DISCORDSRV_TOKEN");
        DiscordSRV.debug("Using bot token supplied from environment variable DISCORDSRV_TOKEN");
    } else if (tokenFile.exists()) {
        try {
            token = FileUtils.readFileToString(tokenFile, StandardCharsets.UTF_8);
            DiscordSRV.debug("Using bot token supplied from " + tokenFile.getPath());
        } catch (IOException e) {
            error(".token file could not be read: " + e.getMessage());
            token = null;
        }
    } else {
        token = config.getString("BotToken");
        DiscordSRV.debug("Using bot token supplied from config");
    }
    if (StringUtils.isBlank(token) || "BOTTOKEN".equalsIgnoreCase(token)) {
        disablePlugin();
        error("No bot token has been set in the config; a bot token is required to connect to Discord.");
        invalidBotToken = true;
        return;
    } else if (token.length() < 59) {
        disablePlugin();
        error("An invalid length bot token (" + token.length() + ") has been set in the config; a valid bot token is required to connect to Discord." + (token.length() == 32 ? " Did you copy the \"Client Secret\" instead of the \"Bot Token\" into the config?" : ""));
        invalidBotToken = true;
        return;
    } else {
        // remove invalid characters
        token = token.replaceAll("[^\\w\\d-_.]", "");
    }
    callbackThreadPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), pool -> {
        final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
        worker.setName("DiscordSRV - JDA Callback " + worker.getPoolIndex());
        return worker;
    }, null, true);
    final ThreadFactory gatewayThreadFactory = new ThreadFactoryBuilder().setNameFormat("DiscordSRV - JDA Gateway").build();
    final ScheduledExecutorService gatewayThreadPool = Executors.newSingleThreadScheduledExecutor(gatewayThreadFactory);
    final ThreadFactory rateLimitThreadFactory = new ThreadFactoryBuilder().setNameFormat("DiscordSRV - JDA Rate Limit").build();
    final ScheduledExecutorService rateLimitThreadPool = new ScheduledThreadPoolExecutor(5, rateLimitThreadFactory);
    // log in to discord
    if (config.getBooleanElse("EnablePresenceInformation", false)) {
        DiscordSRV.api.requireIntent(GatewayIntent.GUILD_PRESENCES);
        DiscordSRV.api.requireCacheFlag(CacheFlag.ACTIVITY);
        DiscordSRV.api.requireCacheFlag(CacheFlag.CLIENT_STATUS);
    }
    try {
        // see ApiManager for our default intents & cache flags
        jda = JDABuilder.create(api.getIntents()).disableCache(Arrays.stream(CacheFlag.values()).filter(cacheFlag -> !api.getCacheFlags().contains(cacheFlag)).collect(Collectors.toList())).setMemberCachePolicy(MemberCachePolicy.ALL).setCallbackPool(callbackThreadPool, false).setGatewayPool(gatewayThreadPool, true).setRateLimitPool(rateLimitThreadPool, true).setWebsocketFactory(new WebSocketFactory().setDualStackMode(DualStackMode.IPV4_ONLY)).setHttpClient(httpClient).setAutoReconnect(true).setBulkDeleteSplittingEnabled(false).setToken(token).addEventListeners(new DiscordBanListener()).addEventListeners(new DiscordChatListener()).addEventListeners(new DiscordConsoleListener()).addEventListeners(new DiscordAccountLinkListener()).addEventListeners(new DiscordDisconnectListener()).addEventListeners(groupSynchronizationManager).setContextEnabled(false).build();
        // let JDA be assigned as soon as we can, but wait until it's ready
        jda.awaitReady();
        for (Guild guild : jda.getGuilds()) {
            guild.retrieveOwner(true).queue();
            guild.loadMembers().onSuccess(members -> DiscordSRV.debug("Loaded " + members.size() + " members in guild " + guild)).onError(throwable -> DiscordSRV.error("Failed to retrieve members of guild " + guild, throwable)).get();
        }
    } catch (LoginException e) {
        disablePlugin();
        if (e.getMessage().toLowerCase().contains("the provided token is invalid")) {
            invalidBotToken = true;
            DiscordDisconnectListener.printDisconnectMessage(true, "The bot token is invalid");
        } else {
            DiscordDisconnectListener.printDisconnectMessage(true, e.getMessage());
        }
        return;
    } catch (Exception e) {
        if (e instanceof IllegalStateException && e.getMessage().equals("Was shutdown trying to await status")) {
            // already logged by JDA
            return;
        }
        DiscordSRV.error("An unknown error occurred building JDA...", e);
        return;
    }
    // start presence updater thread
    if (presenceUpdater != null) {
        if (presenceUpdater.getState() != Thread.State.NEW) {
            presenceUpdater.interrupt();
            presenceUpdater = new PresenceUpdater();
        }
        Bukkit.getScheduler().runTaskLater(this, () -> presenceUpdater.start(), 5 * 20);
    } else {
        presenceUpdater = new PresenceUpdater();
        presenceUpdater.start();
    }
    // start nickname updater thread
    if (nicknameUpdater != null) {
        if (nicknameUpdater.getState() != Thread.State.NEW) {
            nicknameUpdater.interrupt();
            nicknameUpdater = new NicknameUpdater();
        }
        Bukkit.getScheduler().runTaskLater(this, () -> nicknameUpdater.start(), 5 * 20);
    } else {
        nicknameUpdater = new NicknameUpdater();
        nicknameUpdater.start();
    }
    // print the things the bot can see
    if (config().getBoolean("PrintGuildsAndChannels")) {
        for (Guild server : jda.getGuilds()) {
            DiscordSRV.info(LangUtil.InternalMessage.FOUND_SERVER + " " + server);
            for (TextChannel channel : server.getTextChannels()) DiscordSRV.info("- " + channel);
        }
    }
    // show warning if bot wasn't in any guilds
    if (jda.getGuilds().size() == 0) {
        DiscordSRV.error(LangUtil.InternalMessage.BOT_NOT_IN_ANY_SERVERS);
        DiscordSRV.error(jda.getInviteUrl(Permission.ADMINISTRATOR));
        return;
    }
    // see if console channel exists; if it does, tell user where it's been assigned & add console appender
    if (serverIsLog4jCapable) {
        DiscordSRV.info(getConsoleChannel() != null ? LangUtil.InternalMessage.CONSOLE_FORWARDING_ASSIGNED_TO_CHANNEL + " " + getConsoleChannel() : LangUtil.InternalMessage.NOT_FORWARDING_CONSOLE_OUTPUT.toString());
        // attach appender to queue console messages
        consoleAppender = new ConsoleAppender();
        // start console message queue worker thread
        if (consoleMessageQueueWorker != null) {
            if (consoleMessageQueueWorker.getState() != Thread.State.NEW) {
                consoleMessageQueueWorker.interrupt();
                consoleMessageQueueWorker = new ConsoleMessageQueueWorker();
            }
        } else {
            consoleMessageQueueWorker = new ConsoleMessageQueueWorker();
        }
        consoleMessageQueueWorker.start();
    }
    reloadChannels();
    reloadRegexes();
    reloadRoleAliases();
    // warn if the console channel is connected to a chat channel
    if (getMainTextChannel() != null && getConsoleChannel() != null && getMainTextChannel().getId().equals(getConsoleChannel().getId()))
        DiscordSRV.warning(LangUtil.InternalMessage.CONSOLE_CHANNEL_ASSIGNED_TO_LINKED_CHANNEL);
    // send server startup message
    Bukkit.getScheduler().runTaskLater(this, () -> {
        DiscordUtil.queueMessage(getOptionalTextChannel("status"), PlaceholderUtil.replacePlaceholdersToDiscord(LangUtil.Message.SERVER_STARTUP_MESSAGE.toString()), true);
    }, 20);
    // big warning about respect chat plugins
    if (!config().getBooleanElse("RespectChatPlugins", true))
        DiscordSRV.warning(LangUtil.InternalMessage.RESPECT_CHAT_PLUGINS_DISABLED);
    // extra enabled check before doing bukkit api stuff
    if (!isEnabled())
        return;
    // start server watchdog
    if (serverWatchdog != null && serverWatchdog.getState() != Thread.State.NEW)
        serverWatchdog.interrupt();
    serverWatchdog = new ServerWatchdog();
    serverWatchdog.start();
    // start lag (tps) monitor
    Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Lag(), 100L, 1L);
    // cancellation detector
    reloadCancellationDetector();
    // load account links
    if (JdbcAccountLinkManager.shouldUseJdbc()) {
        try {
            JdbcAccountLinkManager jdbcManager = new JdbcAccountLinkManager();
            accountLinkManager = jdbcManager;
            jdbcManager.migrateJSON();
        } catch (SQLException e) {
            StringBuilder stringBuilder = new StringBuilder("JDBC account link backend failed to initialize: ");
            Throwable selected = e;
            do {
                stringBuilder.append("\n").append("Caused by: ").append(selected instanceof UnknownHostException ? "UnknownHostException" : ExceptionUtils.getMessage(selected));
                selected = selected.getCause();
            } while (selected != null);
            String message = stringBuilder.toString().replace(config.getString("Experiment_JdbcAccountLinkBackend"), "<jdbc url>").replace(config.getString("Experiment_JdbcUsername"), "<jdbc username>");
            if (!StringUtils.isEmpty(config.getString("Experiment_JdbcPassword"))) {
                message = message.replace(config.getString("Experiment_JdbcPassword"), "");
            }
            for (String line : message.split("\n")) {
                DiscordSRV.warning(line);
            }
            DiscordSRV.warning("Account link manager falling back to flat file");
            accountLinkManager = new FileAccountLinkManager();
        }
    } else {
        accountLinkManager = new FileAccountLinkManager();
    }
    Bukkit.getPluginManager().registerEvents(accountLinkManager, this);
    // register events
    new PlayerBanListener();
    new PlayerDeathListener();
    new PlayerJoinLeaveListener();
    try {
        Class.forName("org.bukkit.event.player.PlayerAdvancementDoneEvent");
        new PlayerAdvancementDoneListener();
    } catch (Exception ignored) {
        new PlayerAchievementsListener();
    }
    // plugin hooks
    for (String hookClassName : new String[] { // chat plugins
    "github.scarsz.discordsrv.hooks.chat.ChattyChatHook", "github.scarsz.discordsrv.hooks.chat.FancyChatHook", "github.scarsz.discordsrv.hooks.chat.HerochatHook", "github.scarsz.discordsrv.hooks.chat.LegendChatHook", "github.scarsz.discordsrv.hooks.chat.LunaChatHook", "github.scarsz.discordsrv.hooks.chat.TownyChatHook", "github.scarsz.discordsrv.hooks.chat.VentureChatHook", // vanish plugins
    "github.scarsz.discordsrv.hooks.vanish.EssentialsHook", "github.scarsz.discordsrv.hooks.vanish.PhantomAdminHook", "github.scarsz.discordsrv.hooks.vanish.SuperVanishHook", "github.scarsz.discordsrv.hooks.vanish.VanishNoPacketHook", // dynmap
    "github.scarsz.discordsrv.hooks.DynmapHook", // luckperms
    "github.scarsz.discordsrv.hooks.permissions.LuckPermsHook" }) {
        try {
            Class<?> hookClass = Class.forName(hookClassName);
            PluginHook pluginHook = (PluginHook) hookClass.getDeclaredConstructor().newInstance();
            if (pluginHook.isEnabled()) {
                DiscordSRV.info(LangUtil.InternalMessage.PLUGIN_HOOK_ENABLING.toString().replace("{plugin}", pluginHook.getPlugin().getName()));
                Bukkit.getPluginManager().registerEvents(pluginHook, this);
                try {
                    pluginHook.hook();
                    pluginHooks.add(pluginHook);
                } catch (Throwable t) {
                    error("Failed to hook " + hookClassName, t);
                }
            }
        } catch (Throwable e) {
            // ignore class not found errors
            if (!(e instanceof ClassNotFoundException) && !(e instanceof NoClassDefFoundError)) {
                DiscordSRV.error("Failed to load " + hookClassName, e);
            }
        }
    }
    if (pluginHooks.stream().noneMatch(pluginHook -> pluginHook instanceof ChatHook)) {
        DiscordSRV.info(LangUtil.InternalMessage.NO_CHAT_PLUGIN_HOOKED);
        try {
            Class.forName("io.papermc.paper.event.player.AsyncChatEvent");
            getServer().getPluginManager().registerEvents(new ModernPlayerChatListener(), this);
            modernChatEventAvailable = true;
        } catch (ClassNotFoundException ignored) {
        }
        boolean configOption = config().getBoolean("UseModernPaperChatEvent");
        @SuppressWarnings("deprecation") Warning warning = AsyncPlayerChatEvent.class.getAnnotation(Warning.class);
        // check if the event has a nag
        boolean isWarning = warning != null && getServer().getWarningState().printFor(warning);
        Runnable registerLegacy = () -> getServer().getPluginManager().registerEvents(new PlayerChatListener(), this);
        if (isWarning) {
            if (!configOption) {
                if (modernChatEventAvailable) {
                    warning("AsyncPlayerChatEvent will be registered because the UseModernPaperChatEvent config option is set to false");
                    warning("You should enable UseModernPaperChatEvent if your chat plugins have updated to using the new event");
                } else {
                    warning("AsyncPlayerChatEvent has a nag but Paper's modern PlayerChatEvent is not available.");
                    warning("Your server platform's chat event isn't supported currently");
                }
                registerLegacy.run();
            }
        } else {
            // there won't be a nag
            registerLegacy.run();
        }
        debug(Debug.MINECRAFT_TO_DISCORD, "Modern PlayerChatEvent (Paper) is " + (modernChatEventAvailable ? "" : "not ") + "available");
    }
    pluginHooks.add(new VanishHook() {

        @Override
        public boolean isVanished(Player player) {
            boolean vanished = false;
            for (MetadataValue metadataValue : player.getMetadata("vanished")) {
                if (metadataValue.asBoolean()) {
                    vanished = true;
                    break;
                }
            }
            return vanished;
        }

        @Override
        public Plugin getPlugin() {
            return null;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }
    });
    if (PluginUtil.pluginHookIsEnabled("PlaceholderAPI", false)) {
        try {
            DiscordSRV.info(LangUtil.InternalMessage.PLUGIN_HOOK_ENABLING.toString().replace("{plugin}", "PlaceholderAPI"));
            Bukkit.getScheduler().runTask(this, () -> {
                try {
                    if (me.clip.placeholderapi.PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().findExpansionByIdentifier("discordsrv").isPresent()) {
                        getLogger().warning("The DiscordSRV PlaceholderAPI expansion is no longer required.");
                        getLogger().warning("The expansion is now integrated in DiscordSRV.");
                    }
                    new github.scarsz.discordsrv.hooks.PlaceholderAPIExpansion().register();
                } catch (Throwable ignored) {
                    getLogger().severe("Failed to hook into PlaceholderAPI, please check your PlaceholderAPI version");
                }
            });
        } catch (Exception e) {
            if (!(e instanceof ClassNotFoundException)) {
                DiscordSRV.error("Failed to load PlaceholderAPI expansion", e);
            }
        }
    }
    // start channel topic updater
    if (channelTopicUpdater != null) {
        if (channelTopicUpdater.getState() != Thread.State.NEW) {
            channelTopicUpdater.interrupt();
            channelTopicUpdater = new ChannelTopicUpdater();
        }
    } else {
        channelTopicUpdater = new ChannelTopicUpdater();
    }
    channelTopicUpdater.start();
    // enable metrics
    if (!config().getBooleanElse("MetricsDisabled", false)) {
        Metrics bStats = new Metrics(this, 387);
        bStats.addCustomChart(new SimplePie("linked_channels", () -> String.valueOf(channels.size())));
        bStats.addCustomChart(new AdvancedPie("hooked_plugins", () -> new HashMap<String, Integer>() {

            {
                if (pluginHooks.size() == 0) {
                    put("none", 1);
                } else {
                    for (PluginHook hookedPlugin : pluginHooks) {
                        Plugin plugin = hookedPlugin.getPlugin();
                        if (plugin == null)
                            continue;
                        put(plugin.getName(), 1);
                    }
                }
            }
        }));
        bStats.addCustomChart(new SingleLineChart("minecraft-discord_account_links", () -> accountLinkManager.getLinkedAccountCount()));
        bStats.addCustomChart(new SimplePie("server_language", () -> DiscordSRV.config().getLanguage().getName()));
        bStats.addCustomChart(new AdvancedPie("features", () -> new HashMap<String, Integer>() {

            {
                if (getConsoleChannel() != null)
                    put("Console channel", 1);
                if (StringUtils.isNotBlank(config().getString("DiscordChatChannelPrefixRequiredToProcessMessage")))
                    put("Chatting prefix", 1);
                if (JdbcAccountLinkManager.shouldUseJdbc(true))
                    put("JDBC", 1);
                if (config().getBoolean("Experiment_MCDiscordReserializer_ToMinecraft"))
                    put("Discord -> MC Reserializer", 1);
                if (config().getBoolean("Experiment_MCDiscordReserializer_ToDiscord"))
                    put("MC -> Discord Reserializer", 1);
                if (config().getBoolean("Experiment_MCDiscordReserializer_InBroadcast"))
                    put("Broadcast Reserializer", 1);
                if (config().getBoolean("Experiment_WebhookChatMessageDelivery"))
                    put("Webhooks", 1);
                if (config().getMap("GroupRoleSynchronizationGroupsAndRolesToSync").values().stream().anyMatch(s -> s.toString().replace("0", "").length() > 0))
                    put("Group -> role synchronization", 1);
                if (config().getBoolean("Voice enabled"))
                    put("Voice", 1);
                if (config().getBoolean("Require linked account to play.Enabled")) {
                    put("Require linked account to play", 1);
                    if (config().getBoolean("Require linked account to play.Subscriber role.Require subscriber role to join")) {
                        put("Required subscriber role to play", 1);
                    }
                }
            }
        }));
        bStats.addCustomChart(new SingleLineChart("atleast_1player_online", () -> PlayerUtil.getOnlinePlayers().isEmpty() ? 0 : 1));
        bStats.addCustomChart(new SimplePie("better_online_mode", () -> {
            boolean onlineMode = Bukkit.getOnlineMode();
            try {
                Class<?> spigotConfig = Class.forName("org.spigotmc.SpigotConfig");
                Field bungee = spigotConfig.getField("bungee");
                if (bungee.getBoolean(null)) {
                    return "bungee";
                }
            } catch (Throwable ignored) {
            }
            try {
                Class<?> paperConfig = Class.forName("com.destroystokyo.paper.PaperConfig");
                Field velocitySupport = paperConfig.getField("velocitySupport");
                Field velocityOnlineMode = paperConfig.getField("velocityOnlineMode");
                if (velocitySupport.getBoolean(null) && velocityOnlineMode.getBoolean(null)) {
                    return "velocity";
                }
            } catch (Throwable ignored) {
            }
            return onlineMode ? "online" : "offline";
        }));
        bStats.addCustomChart(new DrilldownPie("server_plugins", () -> {
            int pluginCount = Bukkit.getPluginManager().getPlugins().length;
            Map<String, Integer> count = new HashMap<>();
            count.put(String.valueOf(pluginCount), 1);
            String key;
            if (pluginCount <= 5) {
                key = "1-5";
            } else if (pluginCount <= 10) {
                key = "6-10";
            } else if (pluginCount <= 20) {
                key = "11-20";
            } else if (pluginCount <= 50) {
                key = "21-50";
            } else if (pluginCount <= 100) {
                key = "51-100";
            } else {
                key = ((int) (Math.floor(pluginCount / 100F) * 100F)) + "+";
            }
            Map<String, Map<String, Integer>> plugins = new HashMap<>();
            plugins.put(key, count);
            return plugins;
        }));
    }
    // metrics file deprecated since v1.18.1
    File metricsFile = new File(getDataFolder(), "metrics.json");
    if (metricsFile.exists() && !metricsFile.delete())
        metricsFile.deleteOnExit();
    // start the group synchronization task
    if (isGroupRoleSynchronizationEnabled()) {
        int cycleTime = DiscordSRV.config().getInt("GroupRoleSynchronizationCycleTime") * 20 * 60;
        if (cycleTime < 20 * 60)
            cycleTime = 20 * 60;
        try {
            groupSynchronizationManager.resync(GroupSynchronizationManager.SyncDirection.AUTHORITATIVE, GroupSynchronizationManager.SyncCause.TIMER);
        } catch (Exception e) {
            error("Failed to resync\n" + ExceptionUtils.getMessage(e));
        }
        Bukkit.getPluginManager().registerEvents(groupSynchronizationManager, this);
        Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> groupSynchronizationManager.resync(GroupSynchronizationManager.SyncDirection.AUTHORITATIVE, GroupSynchronizationManager.SyncCause.TIMER), cycleTime, cycleTime);
    }
    voiceModule = new VoiceModule();
    PluginCommand discordCommand = getCommand("discord");
    if (discordCommand != null && discordCommand.getPlugin() != this) {
        DiscordSRV.warning("/discord command is being handled by plugin other than DiscordSRV. You must use /discordsrv instead.");
    }
    alertListener = new AlertListener();
    jda.addEventListener(alertListener);
    api.subscribe(alertListener);
    // set ready status
    if (jda.getStatus() == JDA.Status.CONNECTED) {
        isReady = true;
        api.callEvent(new DiscordReadyEvent());
    }
}
Also used : SSLContext(javax.net.ssl.SSLContext) LoggerContext(org.apache.logging.log4j.core.LoggerContext) IOUtil(net.dv8tion.jda.internal.utils.IOUtil) Level(org.apache.logging.log4j.Level) DualStackMode(com.neovisionaries.ws.client.DualStackMode) StringUtils(org.apache.commons.lang3.StringUtils) GsonBuilder(com.google.gson.GsonBuilder) GatewayIntent(net.dv8tion.jda.api.requests.GatewayIntent) ChatHook(github.scarsz.discordsrv.hooks.chat.ChatHook) AdvancedPie(org.bstats.charts.AdvancedPie) GroupSynchronizationManager(github.scarsz.discordsrv.objects.managers.GroupSynchronizationManager) CommandSender(org.bukkit.command.CommandSender) StandardCharsets(java.nio.charset.StandardCharsets) InvocationTargetException(java.lang.reflect.InvocationTargetException) github.scarsz.discordsrv.objects.threads(github.scarsz.discordsrv.objects.threads) AccountLinkManager(github.scarsz.discordsrv.objects.managers.AccountLinkManager) PlayerQuitEvent(org.bukkit.event.player.PlayerQuitEvent) WebSocketFactory(com.neovisionaries.ws.client.WebSocketFactory) net.dv8tion.jda.api(net.dv8tion.jda.api) ExceptionUtils(org.apache.commons.lang3.exception.ExceptionUtils) HandlerList(org.bukkit.event.HandlerList) PluginLoader(org.bukkit.plugin.PluginLoader) net.dv8tion.jda.api.entities(net.dv8tion.jda.api.entities) java.util(java.util) VaultHook(github.scarsz.discordsrv.hooks.VaultHook) CloseCode(net.dv8tion.jda.api.requests.CloseCode) Supplier(java.util.function.Supplier) SQLException(java.sql.SQLException) DrilldownPie(org.bstats.charts.DrilldownPie) CacheFlag(net.dv8tion.jda.api.utils.cache.CacheFlag) ApiManager(github.scarsz.discordsrv.api.ApiManager) MemberCachePolicy(net.dv8tion.jda.api.utils.MemberCachePolicy) PluginHook(github.scarsz.discordsrv.hooks.PluginHook) PluginBase(org.bukkit.plugin.PluginBase) ConsoleAppender(github.scarsz.discordsrv.objects.log4j.ConsoleAppender) FileUtils(org.apache.commons.io.FileUtils) Field(java.lang.reflect.Field) RequireLinkModule(github.scarsz.discordsrv.modules.requirelink.RequireLinkModule) CheckReturnValue(javax.annotation.CheckReturnValue) OkHttpClient(okhttp3.OkHttpClient) CommandManager(github.scarsz.discordsrv.objects.managers.CommandManager) Record(org.minidns.record.Record) PluginDescriptionFile(org.bukkit.plugin.PluginDescriptionFile) DynamicConfig(github.scarsz.configuralize.DynamicConfig) Plugin(org.bukkit.plugin.Plugin) LoginException(javax.security.auth.login.LoginException) Dynamic(alexh.weak.Dynamic) AlertListener(github.scarsz.discordsrv.modules.alerts.AlertListener) ShutdownEvent(net.dv8tion.jda.api.events.ShutdownEvent) BiFunction(java.util.function.BiFunction) Player(org.bukkit.entity.Player) SimplePie(org.bstats.charts.SimplePie) ChunkGenerator(org.bukkit.generator.ChunkGenerator) org.bukkit(org.bukkit) DnsMessage(org.minidns.dnsmessage.DnsMessage) java.net(java.net) Language(github.scarsz.configuralize.Language) Gson(com.google.gson.Gson) RateLimitedException(net.dv8tion.jda.api.exceptions.RateLimitedException) MessageAction(net.dv8tion.jda.api.requests.restaction.MessageAction) HierarchyException(net.dv8tion.jda.api.exceptions.HierarchyException) JdbcAccountLinkManager(github.scarsz.discordsrv.objects.managers.link.JdbcAccountLinkManager) Method(java.lang.reflect.Method) github.scarsz.discordsrv.api.events(github.scarsz.discordsrv.api.events) PlayerJoinEvent(org.bukkit.event.player.PlayerJoinEvent) Lag(github.scarsz.discordsrv.objects.Lag) ErrorResponseException(net.dv8tion.jda.api.exceptions.ErrorResponseException) PatternSyntaxException(java.util.regex.PatternSyntaxException) PermissionException(net.dv8tion.jda.api.exceptions.PermissionException) java.util.concurrent(java.util.concurrent) PermissionDefault(org.bukkit.permissions.PermissionDefault) VanishHook(github.scarsz.discordsrv.hooks.vanish.VanishHook) Metrics(org.bstats.bukkit.Metrics) SingleLineChart(org.bstats.charts.SingleLineChart) Logger(java.util.logging.Logger) PluginCommand(org.bukkit.command.PluginCommand) Collectors(java.util.stream.Collectors) JavaPlugin(org.bukkit.plugin.java.JavaPlugin) Nullable(org.jetbrains.annotations.Nullable) MetadataValue(org.bukkit.metadata.MetadataValue) Command(org.bukkit.command.Command) Pattern(java.util.regex.Pattern) NotNull(org.jetbrains.annotations.NotNull) BukkitWorker(org.bukkit.scheduler.BukkitWorker) CancellationDetector(github.scarsz.discordsrv.objects.CancellationDetector) ThreadFactoryBuilder(com.google.common.util.concurrent.ThreadFactoryBuilder) Getter(lombok.Getter) MessageFormat(github.scarsz.discordsrv.objects.MessageFormat) FileConfiguration(org.bukkit.configuration.file.FileConfiguration) ImmutableList(com.google.common.collect.ImmutableList) Component(net.kyori.adventure.text.Component) ParseException(github.scarsz.configuralize.ParseException) github.scarsz.discordsrv.listeners(github.scarsz.discordsrv.listeners) JdaFilter(github.scarsz.discordsrv.objects.log4j.JdaFilter) AsyncPlayerChatEvent(org.bukkit.event.player.AsyncPlayerChatEvent) OkHostnameVerifier(okhttp3.internal.tls.OkHostnameVerifier) github.scarsz.discordsrv.util(github.scarsz.discordsrv.util) ConsoleMessage(github.scarsz.discordsrv.objects.ConsoleMessage) RestAction(net.dv8tion.jda.api.requests.RestAction) ListenerAdapter(net.dv8tion.jda.api.hooks.ListenerAdapter) Dns(okhttp3.Dns) FileAccountLinkManager(github.scarsz.discordsrv.objects.managers.link.FileAccountLinkManager) Consumer(java.util.function.Consumer) java.io(java.io) DnsClient(org.minidns.DnsClient) VoiceModule(github.scarsz.discordsrv.modules.voice.VoiceModule) MultiverseCoreHook(github.scarsz.discordsrv.hooks.world.MultiverseCoreHook) DigestUtils(org.apache.commons.codec.digest.DigestUtils) LogManager(org.apache.logging.log4j.LogManager) ConsoleAppender(github.scarsz.discordsrv.objects.log4j.ConsoleAppender) Dns(okhttp3.Dns) AlertListener(github.scarsz.discordsrv.modules.alerts.AlertListener) LoginException(javax.security.auth.login.LoginException) ErrorResponseException(net.dv8tion.jda.api.exceptions.ErrorResponseException) ChatHook(github.scarsz.discordsrv.hooks.chat.ChatHook) PermissionException(net.dv8tion.jda.api.exceptions.PermissionException) VoiceModule(github.scarsz.discordsrv.modules.voice.VoiceModule) Logger(java.util.logging.Logger) ThreadFactoryBuilder(com.google.common.util.concurrent.ThreadFactoryBuilder) JdbcAccountLinkManager(github.scarsz.discordsrv.objects.managers.link.JdbcAccountLinkManager) FileAccountLinkManager(github.scarsz.discordsrv.objects.managers.link.FileAccountLinkManager) LoggerContext(org.apache.logging.log4j.core.LoggerContext) Lag(github.scarsz.discordsrv.objects.Lag) SimplePie(org.bstats.charts.SimplePie) DnsMessage(org.minidns.dnsmessage.DnsMessage) Metrics(org.bstats.bukkit.Metrics) WebSocketFactory(com.neovisionaries.ws.client.WebSocketFactory) HandlerList(org.bukkit.event.HandlerList) ImmutableList(com.google.common.collect.ImmutableList) MetadataValue(org.bukkit.metadata.MetadataValue) Player(org.bukkit.entity.Player) PluginHook(github.scarsz.discordsrv.hooks.PluginHook) InvocationTargetException(java.lang.reflect.InvocationTargetException) VanishHook(github.scarsz.discordsrv.hooks.vanish.VanishHook) PluginDescriptionFile(org.bukkit.plugin.PluginDescriptionFile) OkHttpClient(okhttp3.OkHttpClient) SQLException(java.sql.SQLException) NotNull(org.jetbrains.annotations.NotNull) HierarchyException(net.dv8tion.jda.api.exceptions.HierarchyException) Field(java.lang.reflect.Field) AdvancedPie(org.bstats.charts.AdvancedPie) RateLimitedException(net.dv8tion.jda.api.exceptions.RateLimitedException) PluginCommand(org.bukkit.command.PluginCommand) RequireLinkModule(github.scarsz.discordsrv.modules.requirelink.RequireLinkModule) DnsClient(org.minidns.DnsClient) SingleLineChart(org.bstats.charts.SingleLineChart) SSLContext(javax.net.ssl.SSLContext) InvocationTargetException(java.lang.reflect.InvocationTargetException) SQLException(java.sql.SQLException) LoginException(javax.security.auth.login.LoginException) RateLimitedException(net.dv8tion.jda.api.exceptions.RateLimitedException) HierarchyException(net.dv8tion.jda.api.exceptions.HierarchyException) ErrorResponseException(net.dv8tion.jda.api.exceptions.ErrorResponseException) PatternSyntaxException(java.util.regex.PatternSyntaxException) PermissionException(net.dv8tion.jda.api.exceptions.PermissionException) ParseException(github.scarsz.configuralize.ParseException) DrilldownPie(org.bstats.charts.DrilldownPie) Plugin(org.bukkit.plugin.Plugin) JavaPlugin(org.bukkit.plugin.java.JavaPlugin)

Aggregations

Dynamic (alexh.weak.Dynamic)1 ImmutableList (com.google.common.collect.ImmutableList)1 ThreadFactoryBuilder (com.google.common.util.concurrent.ThreadFactoryBuilder)1 Gson (com.google.gson.Gson)1 GsonBuilder (com.google.gson.GsonBuilder)1 DualStackMode (com.neovisionaries.ws.client.DualStackMode)1 WebSocketFactory (com.neovisionaries.ws.client.WebSocketFactory)1 DynamicConfig (github.scarsz.configuralize.DynamicConfig)1 Language (github.scarsz.configuralize.Language)1 ParseException (github.scarsz.configuralize.ParseException)1 ApiManager (github.scarsz.discordsrv.api.ApiManager)1 github.scarsz.discordsrv.api.events (github.scarsz.discordsrv.api.events)1 PluginHook (github.scarsz.discordsrv.hooks.PluginHook)1 VaultHook (github.scarsz.discordsrv.hooks.VaultHook)1 ChatHook (github.scarsz.discordsrv.hooks.chat.ChatHook)1 VanishHook (github.scarsz.discordsrv.hooks.vanish.VanishHook)1 MultiverseCoreHook (github.scarsz.discordsrv.hooks.world.MultiverseCoreHook)1 github.scarsz.discordsrv.listeners (github.scarsz.discordsrv.listeners)1 AlertListener (github.scarsz.discordsrv.modules.alerts.AlertListener)1 RequireLinkModule (github.scarsz.discordsrv.modules.requirelink.RequireLinkModule)1