Start & stop messages, some bug fixes & cleanup

This commit is contained in:
Vankka 2022-04-01 19:07:14 +03:00
parent 60c7dbfbc0
commit 2b878f1265
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
26 changed files with 378 additions and 120 deletions

View File

@ -30,5 +30,7 @@ public abstract class ProxyDiscordSRV<C extends MainConfig, CC extends Connectio
super.enable();
registerModule(ServerSwitchMessageModule::new);
startedMessage();
}
}

View File

@ -21,10 +21,11 @@ package com.discordsrv.proxy.config.channels;
import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.channels.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class ServerSwitchMessageConfig {
public class ServerSwitchMessageConfig implements IMessageConfig {
public boolean enabled = true;
@ -40,4 +41,14 @@ public class ServerSwitchMessageConfig {
.setColor(0x5555FF)
.build()
);
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -19,7 +19,6 @@
package com.discordsrv.proxy.modules;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.forward.game.ServerSwitchMessageForwardedEvent;
@ -27,7 +26,6 @@ import com.discordsrv.api.event.events.message.receive.game.ServerSwitchMessageR
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.messageforwarding.game.AbstractGameMessageModule;
import com.discordsrv.proxy.config.channels.ServerSwitchMessageConfig;
import com.discordsrv.proxy.config.channels.base.ProxyBaseChannelConfig;
@ -35,7 +33,7 @@ import com.discordsrv.proxy.config.channels.base.ProxyBaseChannelConfig;
public class ServerSwitchMessageModule extends AbstractGameMessageModule<ServerSwitchMessageConfig> {
public ServerSwitchMessageModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "SERVER_SWITCH"));
super(discordSRV, "SERVER_SWITCH_MESSAGES");
}
@Subscribe(priority = EventPriority.LAST)
@ -53,16 +51,6 @@ public class ServerSwitchMessageModule extends AbstractGameMessageModule<ServerS
return channelConfig.map(cfg -> ((ProxyBaseChannelConfig) cfg).serverSwitchMessages);
}
@Override
public boolean isEnabled(OrDefault<ServerSwitchMessageConfig> config) {
return config.get(cfg -> cfg.enabled, false);
}
@Override
public SendableDiscordMessage.Builder getFormat(OrDefault<ServerSwitchMessageConfig> config) {
return config.get(cfg -> cfg.format);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {
discordSRV.eventBus().publish(new ServerSwitchMessageForwardedEvent(cluster));

View File

@ -45,11 +45,16 @@ public abstract class ServerDiscordSRV<C extends MainConfig, CC extends Connecti
}
public final CompletableFuture<Void> invokeServerStarted() {
return invokeLifecycle(this::serverStarted, "Failed to enable", true);
return invokeLifecycle(() -> {
if (status().isShutdown()) {
return;
}
this.serverStarted();
});
}
@OverridingMethodsMustInvokeSuper
protected void serverStarted() {
startedMessage();
}
}

View File

@ -21,11 +21,12 @@ package com.discordsrv.common.server.config.channels;
import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.channels.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public class DeathMessageConfig {
public class DeathMessageConfig implements IMessageConfig {
@Comment("Enable death message forwarding")
public boolean enabled = true;
@ -38,4 +39,14 @@ public class DeathMessageConfig {
.setColor(1)
.build()
);
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -19,7 +19,6 @@
package com.discordsrv.common.server.modules;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.forward.game.DeathMessageForwardedEvent;
@ -27,7 +26,6 @@ import com.discordsrv.api.event.events.message.receive.game.DeathMessageReceiveE
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.messageforwarding.game.AbstractGameMessageModule;
import com.discordsrv.common.server.config.channels.DeathMessageConfig;
import com.discordsrv.common.server.config.channels.base.ServerBaseChannelConfig;
@ -35,7 +33,7 @@ import com.discordsrv.common.server.config.channels.base.ServerBaseChannelConfig
public class DeathMessageModule extends AbstractGameMessageModule<DeathMessageConfig> {
public DeathMessageModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "DEATH"));
super(discordSRV, "DEATH_MESSAGES");
}
@Subscribe(priority = EventPriority.LAST)
@ -53,16 +51,6 @@ public class DeathMessageModule extends AbstractGameMessageModule<DeathMessageCo
return channelConfig.map(cfg -> ((ServerBaseChannelConfig) cfg).deathMessages);
}
@Override
public boolean isEnabled(OrDefault<DeathMessageConfig> config) {
return config.get(cfg -> cfg.enabled, true);
}
@Override
public SendableDiscordMessage.Builder getFormat(OrDefault<DeathMessageConfig> config) {
return config.get(cfg -> cfg.format);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {
discordSRV.eventBus().publish(new DeathMessageForwardedEvent(cluster));

View File

@ -19,7 +19,6 @@
package com.discordsrv.common;
import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
import com.discordsrv.api.event.bus.EventBus;
import com.discordsrv.api.event.events.lifecycle.DiscordSRVConnectedEvent;
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReadyEvent;
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadedEvent;
@ -58,6 +57,8 @@ import com.discordsrv.common.messageforwarding.discord.DiscordChatMessageModule;
import com.discordsrv.common.messageforwarding.discord.DiscordMessageMirroringModule;
import com.discordsrv.common.messageforwarding.game.JoinMessageModule;
import com.discordsrv.common.messageforwarding.game.LeaveMessageModule;
import com.discordsrv.common.messageforwarding.game.StartMessageModule;
import com.discordsrv.common.messageforwarding.game.StopMessageModule;
import com.discordsrv.common.module.ModuleManager;
import com.discordsrv.common.module.type.AbstractModule;
import com.discordsrv.common.placeholder.ComponentResultStringifier;
@ -100,9 +101,10 @@ import java.util.jar.Manifest;
public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends ConnectionConfig> implements DiscordSRV {
private final AtomicReference<Status> status = new AtomicReference<>(Status.INITIALIZED);
private CompletableFuture<Void> enableFuture;
// DiscordSRVApi
private EventBus eventBus;
private EventBusImpl eventBus;
private ProfileManager profileManager;
private PlaceholderServiceImpl placeholderService;
private ComponentFactory componentFactory;
@ -201,7 +203,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
@Override
public @NotNull EventBus eventBus() {
public @NotNull EventBusImpl eventBus() {
return eventBus;
}
@ -274,7 +276,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
@Override
public DiscordConnectionManager discordConnectionManager() {
public JDAConnectionManager discordConnectionManager() {
return discordConnectionManager;
}
@ -384,7 +386,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
// Lifecycle
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable runnable, String message, boolean enable) {
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable runnable) {
return invoke(() -> {
try {
lifecycleLock.lock();
@ -392,7 +394,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
} finally {
lifecycleLock.unlock();
}
}, message, enable);
}, "Failed to enable", true);
}
protected CompletableFuture<Void> invoke(CheckedRunnable runnable, String message, boolean enable) {
@ -406,21 +408,24 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
logger().error(message, t);
}
}, scheduler().executor());
}, scheduler().executorService());
}
@Override
public final CompletableFuture<Void> invokeEnable() {
return invokeLifecycle(() -> {
return enableFuture = invokeLifecycle(() -> {
this.enable();
waitForStatus(Status.CONNECTED);
eventBus().publish(new DiscordSRVReadyEvent());
}, "Failed to enable", true);
});
}
@Override
public final CompletableFuture<Void> invokeDisable() {
return invokeLifecycle(this::disable, "Failed to disable", false);
if (enableFuture != null && !enableFuture.isDone()) {
enableFuture.cancel(true);
}
return CompletableFuture.runAsync(this::disable, scheduler().executorService());
}
@Override
@ -469,6 +474,11 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
}
protected final void startedMessage() {
registerModule(StartMessageModule::new);
registerModule(StopMessageModule::new);
}
private StorageType getStorageType() {
String backend = connectionConfig().storage.backend;
switch (backend.toLowerCase(Locale.ROOT)) {
@ -487,6 +497,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
this.status.set(Status.SHUTTING_DOWN);
eventBus().publish(new DiscordSRVShuttingDownEvent());
eventBus().shutdown();
this.status.set(Status.SHUTDOWN);
}
@ -574,7 +585,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
} else {
discordConnectionManager.connect().get();
}
if (!initial) {
waitForStatus(Status.CONNECTED, 20, TimeUnit.SECONDS);
}
} catch (ExecutionException e) {
throw e.getCause();
}

View File

@ -30,7 +30,7 @@ import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.console.Console;
import com.discordsrv.common.debug.data.OnlineMode;
import com.discordsrv.common.discord.api.DiscordAPIImpl;
import com.discordsrv.common.discord.connection.DiscordConnectionManager;
import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;
import com.discordsrv.common.linking.LinkProvider;
import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.logging.impl.DiscordSRVLogger;
@ -50,7 +50,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.util.*;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@ -110,7 +111,7 @@ public interface DiscordSRV extends DiscordSRVApi {
ChannelConfigHelper channelConfig();
// Internal
DiscordConnectionManager discordConnectionManager();
JDAConnectionManager discordConnectionManager();
// Modules
@Nullable

View File

@ -0,0 +1,27 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
public interface IMessageConfig {
boolean enabled();
SendableDiscordMessage.Builder format();
}

View File

@ -24,7 +24,7 @@ import com.discordsrv.common.config.annotation.Untranslated;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class JoinMessageConfig {
public class JoinMessageConfig implements IMessageConfig {
public boolean enabled = true;
@ -36,4 +36,14 @@ public class JoinMessageConfig {
.setColor(0x55FF55)
.build()
);
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -24,7 +24,7 @@ import com.discordsrv.common.config.annotation.Untranslated;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class LeaveMessageConfig {
public class LeaveMessageConfig implements IMessageConfig {
public boolean enabled = true;
@ -36,4 +36,14 @@ public class LeaveMessageConfig {
.setColor(0xFF5555)
.build()
);
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -28,7 +28,7 @@ import java.util.Map;
import java.util.regex.Pattern;
@ConfigSerializable
public class MinecraftToDiscordChatConfig {
public class MinecraftToDiscordChatConfig implements IMessageConfig {
@Comment("Is Minecraft to Discord chat forwarding enabled")
public boolean enabled = true;
@ -46,6 +46,16 @@ public class MinecraftToDiscordChatConfig {
@Comment("What mentions should be translated from chat messages to mentions (this does not effect if they will cause a notification or not)")
public Mentions mentions = new Mentions();
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
@ConfigSerializable
public static class Mentions {

View File

@ -0,0 +1,43 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class StartMessageConfig implements IMessageConfig {
public boolean enabled = true;
@Untranslated(Untranslated.Type.VALUE)
public SendableDiscordMessage.Builder format = SendableDiscordMessage.builder()
.setContent(":arrow_forward: **The server has started**");
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class StopMessageConfig implements IMessageConfig {
public boolean enabled = true;
@Untranslated(Untranslated.Type.VALUE)
public SendableDiscordMessage.Builder format = SendableDiscordMessage.builder()
.setContent(":pause_button: **The server has stopped**");
@Override
public boolean enabled() {
return enabled;
}
@Override
public SendableDiscordMessage.Builder format() {
return format;
}
}

View File

@ -31,6 +31,8 @@ public class BaseChannelConfig {
public DiscordToMinecraftChatConfig discordToMinecraft = new DiscordToMinecraftChatConfig();
public JoinMessageConfig joinMessages = new JoinMessageConfig();
public LeaveMessageConfig leaveMessages = new LeaveMessageConfig();
public StartMessageConfig startMessage = new StartMessageConfig();
public StopMessageConfig stopMessage = new StopMessageConfig();
@Order(10)
@Comment("Settings for synchronizing messages between the defined Discord channels and threads")

View File

@ -159,7 +159,8 @@ public class DiscordAPIImpl implements DiscordAPI {
futures.add(future.handle((threadChannel, t) -> {
if (t != null) {
discordSRV.logger().error("Failed to deliver message to thread \""
discordSRV.discordConnectionManager().handleRequestFailure(
"Failed to deliver message to thread \""
+ threadConfig.threadName + "\" in channel " + channel, t);
throw new RuntimeException(); // Just here to fail the future
}

View File

@ -77,6 +77,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
}
private final DiscordSRV discordSRV;
private final DefaultFailureCallback defaultFailureCallback;
private ScheduledExecutorService gatewayPool;
private ScheduledExecutorService rateLimitPool;
@ -94,9 +95,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
public JDAConnectionManager(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.defaultFailureCallback = new DefaultFailureCallback(new NamedLogger(discordSRV, "DISCORD_REQUESTS"));
// Set default failure handling
RestAction.setDefaultFailure(new DefaultFailureCallback(new NamedLogger(discordSRV, "DISCORD_REQUESTS")));
RestAction.setDefaultFailure(defaultFailureCallback);
// Disable all mentions by default for safety
AllowedMentions.setDefaultMentions(Collections.emptyList());
@ -211,9 +213,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
throw new IllegalStateException("Cannot reconnect, still active");
}
CompletableFuture<Void> future = CompletableFuture.runAsync(this::connectInternal, discordSRV.scheduler().executor());
connectionFuture = future;
return future;
return connectionFuture = CompletableFuture.runAsync(this::connectInternal, discordSRV.scheduler().executor());
}
private void connectInternal() {
@ -306,7 +306,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
try {
discordSRV.scheduler().run(() -> {
try {
while (instance.getStatus() != JDA.Status.SHUTDOWN) {
while (instance != null && instance.getStatus() != JDA.Status.SHUTDOWN) {
Thread.sleep(50);
}
} catch (InterruptedException ignored) {}
@ -436,6 +436,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
discordSRV.logger().error("+------------------------------>");
}
public void handleRequestFailure(String context, Throwable cause) {
defaultFailureCallback.accept(context, cause);
}
private class DefaultFailureCallback implements Consumer<Throwable> {
private final Logger logger;
@ -446,6 +450,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
@Override
public void accept(Throwable t) {
accept(null, t);
}
public void accept(String context, Throwable t) {
if ((t instanceof InterruptedIOException || t instanceof InterruptedException)
&& discordSRV.status().isShutdown()) {
// Ignore interrupted exceptions when DiscordSRV is shutting down or shutdown
@ -471,7 +479,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
// Run the cause through this method again
accept(cause);
} else {
logger.error("Failed to complete request for a unknown reason", exception);
logger.error((context != null ? context + ": " : "") + "Failed to complete request for a unknown reason", exception);
}
return;
}
@ -520,8 +528,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
default: break;
}
logger.error("Failed to complete a request: " + response.getMeaning());
logger.error((context != null ? context : "Failed to complete a request") + ": " + response.getMeaning());
logger.debug(exception);
} else {
logger.error(context != null ? context : "Failed to complete a request due to unknown error", t);
}
}
}

View File

@ -39,7 +39,10 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
@ -65,6 +68,11 @@ public class EventBusImpl implements EventBus {
subscribe(this);
}
public void shutdown() {
listeners.clear();
allListeners.clear();
}
@Override
public void subscribe(@NotNull Object eventListener) {
if (listeners.containsKey(eventListener)) {

View File

@ -31,11 +31,13 @@ import com.discordsrv.api.placeholder.FormattedText;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.IMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.module.type.AbstractModule;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
@ -46,47 +48,46 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractGameMessageModule<T> extends AbstractModule<DiscordSRV> {
public abstract class AbstractGameMessageModule<T extends IMessageConfig> extends AbstractModule<DiscordSRV> {
public AbstractGameMessageModule(DiscordSRV discordSRV, Logger logger) {
super(discordSRV, logger);
public AbstractGameMessageModule(DiscordSRV discordSRV, String loggerName) {
super(discordSRV, new NamedLogger(discordSRV, loggerName));
}
public abstract OrDefault<T> mapConfig(OrDefault<BaseChannelConfig> channelConfig);
public abstract boolean isEnabled(OrDefault<T> config);
public abstract SendableDiscordMessage.Builder getFormat(OrDefault<T> config);
public abstract void postClusterToEventBus(ReceivedDiscordMessageCluster cluster);
public final void process(
@NotNull AbstractGameMessageReceiveEvent event,
@NotNull DiscordSRVPlayer player,
public final CompletableFuture<?> process(
@Nullable AbstractGameMessageReceiveEvent event,
@Nullable DiscordSRVPlayer player,
@Nullable GameChannel channel
) {
if (channel == null) {
// Send to all channels due to lack of specified channel
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (OrDefault<BaseChannelConfig> channelConfig : discordSRV.channelConfig().getAllChannels()) {
forwardToChannel(event, player, channelConfig);
futures.add(forwardToChannel(event, player, channelConfig));
}
return;
return CompletableFutureUtil.combine(futures);
}
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(channel);
forwardToChannel(event, player, channelConfig);
return forwardToChannel(event, player, channelConfig);
}
private void forwardToChannel(
@NotNull AbstractGameMessageReceiveEvent event,
@NotNull DiscordSRVPlayer player,
private CompletableFuture<Void> forwardToChannel(
@Nullable AbstractGameMessageReceiveEvent event,
@Nullable DiscordSRVPlayer player,
@NotNull OrDefault<BaseChannelConfig> channelConfig
) {
OrDefault<T> config = mapConfig(channelConfig);
if (!isEnabled(config)) {
return;
if (!config.get(IMessageConfig::enabled, true)) {
return null;
}
IChannelConfig iChannelConfig = channelConfig.get(cfg -> cfg instanceof IChannelConfig ? (IChannelConfig) cfg : null);
if (iChannelConfig == null) {
return;
return null;
}
List<DiscordMessageChannel> messageChannels = new CopyOnWriteArrayList<>();
@ -108,13 +109,14 @@ public abstract class AbstractGameMessageModule<T> extends AbstractModule<Discor
discordSRV.discordAPI().findOrCreateThreads(iChannelConfig, messageChannels::add, futures);
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((v, t1) -> {
SendableDiscordMessage.Builder format = getFormat(config);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((v, t1) -> {
SendableDiscordMessage.Builder format = config.get(IMessageConfig::format);
if (format == null) {
return;
}
String message = convertMessage(config, ComponentUtil.fromAPI(event.getMessage()));
Component component = event != null ? ComponentUtil.fromAPI(event.getMessage()) : null;
String message = component != null ? convertMessage(config, component) : null;
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> messageFutures;
messageFutures = sendMessageToChannels(
config, format, messageChannels, message,
@ -129,7 +131,11 @@ public abstract class AbstractGameMessageModule<T> extends AbstractModule<Discor
CompletableFuture<ReceivedDiscordMessage> future = entry.getKey();
if (future.isCompletedExceptionally()) {
future.exceptionally(t -> {
discordSRV.logger().error("Failed to deliver a message to the " + entry.getValue() + " channel", t);
if (t instanceof CompletionException) {
t = t.getCause();
}
discordSRV.discordConnectionManager().handleRequestFailure(
"Failed to deliver a message to " + entry.getValue(), t);
return null;
});
// Ignore ones that failed
@ -140,7 +146,7 @@ public abstract class AbstractGameMessageModule<T> extends AbstractModule<Discor
messages.add(future.join());
}
if (message.isEmpty()) {
if (messages.isEmpty()) {
// Nothing was delivered
return;
}

View File

@ -19,7 +19,6 @@
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.forward.game.JoinMessageForwardedEvent;
@ -28,12 +27,11 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.JoinMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
public class JoinMessageModule extends AbstractGameMessageModule<JoinMessageConfig> {
public JoinMessageModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "JOIN_MESSAGES"));
super(discordSRV, "JOIN_MESSAGES");
}
@Override
@ -41,16 +39,6 @@ public class JoinMessageModule extends AbstractGameMessageModule<JoinMessageConf
return channelConfig.map(cfg -> cfg.joinMessages);
}
@Override
public boolean isEnabled(OrDefault<JoinMessageConfig> config) {
return config.get(cfg -> cfg.enabled, true);
}
@Override
public SendableDiscordMessage.Builder getFormat(OrDefault<JoinMessageConfig> config) {
return config.get(cfg -> cfg.format);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {
discordSRV.eventBus().publish(new JoinMessageForwardedEvent(cluster));

View File

@ -19,7 +19,6 @@
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.forward.game.LeaveMessageForwardedEvent;
@ -28,12 +27,11 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.LeaveMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
public class LeaveMessageModule extends AbstractGameMessageModule<LeaveMessageConfig> {
public LeaveMessageModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "LEAVE_MESSAGES"));
super(discordSRV, "LEAVE_MESSAGES");
}
@Override
@ -41,16 +39,6 @@ public class LeaveMessageModule extends AbstractGameMessageModule<LeaveMessageCo
return channelConfig.map(cfg -> cfg.leaveMessages);
}
@Override
public boolean isEnabled(OrDefault<LeaveMessageConfig> config) {
return config.get(cfg -> cfg.enabled, true);
}
@Override
public SendableDiscordMessage.Builder getFormat(OrDefault<LeaveMessageConfig> config) {
return config.get(cfg -> cfg.format);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {
discordSRV.eventBus().publish(new LeaveMessageForwardedEvent(cluster));

View File

@ -37,7 +37,6 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.Member;
@ -66,7 +65,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
private final Map<Long, Map<Long, CachedMention>> channelMentions = new ConcurrentHashMap<>();
public MinecraftToDiscordChatModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "MINECRAFT_TO_DISCORD"));
super(discordSRV, "MINECRAFT_TO_DISCORD");
}
@Override
@ -91,16 +90,6 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
return channelConfig.map(cfg -> cfg.minecraftToDiscord);
}
@Override
public boolean isEnabled(OrDefault<MinecraftToDiscordChatConfig> config) {
return config.get(cfg -> cfg.enabled, true);
}
@Override
public SendableDiscordMessage.Builder getFormat(OrDefault<MinecraftToDiscordChatConfig> config) {
return config.get(cfg -> cfg.format);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {
discordSRV.eventBus().publish(new GameChatMessageForwardedEvent(cluster));

View File

@ -0,0 +1,45 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.StartMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
public class StartMessageModule extends AbstractGameMessageModule<StartMessageConfig> {
public StartMessageModule(DiscordSRV discordSRV) {
super(discordSRV, "START_MESSAGE");
}
@Override
public OrDefault<StartMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.startMessage);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
@Override
public void enable() {
process(null, null, null);
}
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV 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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.StopMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class StopMessageModule extends AbstractGameMessageModule<StopMessageConfig> {
public StopMessageModule(DiscordSRV discordSRV) {
super(discordSRV, "START_MESSAGE");
}
@Override
public OrDefault<StopMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.stopMessage);
}
@Override
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
@Override
public void disable() {
try {
process(null, null, null).get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
logger().error("Failed to queue stop message to be sent within 5 seconds.");
} catch (InterruptedException | ExecutionException ignored) {}
}
}

View File

@ -118,7 +118,9 @@ public class ModuleManager {
throw new IllegalArgumentException("Cannot unregister a delegate");
}
if (getAbstract(module).isHasBeenEnabled()) {
disable(module);
}
this.modules.remove(module);
this.moduleLookupTable.values().removeIf(mod -> mod == module);

View File

@ -44,6 +44,10 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
return logger;
}
public boolean isHasBeenEnabled() {
return hasBeenEnabled;
}
public final boolean enableModule() {
if (hasBeenEnabled || !isEnabled()) {
return false;