This commit is contained in:
Vankka 2024-06-24 23:45:25 +03:00
parent 1e9e1c53e2
commit bc12809351
No known key found for this signature in database
GPG Key ID: 62E48025ED4E7EBB
11 changed files with 202 additions and 87 deletions

View File

@ -23,7 +23,9 @@
package com.discordsrv.api.module.type;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.module.Module;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.api.punishment.Punishment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -36,13 +38,14 @@ public interface PunishmentModule extends Module {
interface Bans extends PunishmentModule {
CompletableFuture<@Nullable Punishment> getBan(@NotNull UUID playerUUID);
CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable String reason, @NotNull String punisher);
CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher);
CompletableFuture<Void> removeBan(@NotNull UUID playerUUID);
CompletableFuture<Void> kickPlayer(@NotNull DiscordSRVPlayer player, @NotNull MinecraftComponent message);
}
interface Mutes extends PunishmentModule {
CompletableFuture<@Nullable Punishment> getMute(@NotNull UUID playerUUID);
CompletableFuture<Void> addMute(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable String reason, @NotNull String punisher);
CompletableFuture<Void> addMute(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher);
CompletableFuture<Void> removeMute(@NotNull UUID playerUUID);
}
}

View File

@ -23,31 +23,39 @@
package com.discordsrv.api.punishment;
import org.jetbrains.annotations.Nullable;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
import java.time.Instant;
@PlaceholderPrefix("punishment_")
public class Punishment {
private final Instant until;
private final String reason;
private final String punisher;
public static final Punishment UNKNOWN = new Punishment(null, null, null);
public Punishment(@Nullable Instant until, @Nullable String reason, @Nullable String punisher) {
private final Instant until;
private final MinecraftComponent reason;
private final MinecraftComponent punisher;
public Punishment(Instant until, MinecraftComponent reason, MinecraftComponent punisher) {
this.until = until;
this.reason = reason;
this.punisher = punisher;
}
@Placeholder(value = "until", relookup = "date")
public Instant until() {
return until;
}
public String reason() {
@Placeholder("reason")
public MinecraftComponent reason() {
return reason;
}
public String punisher() {
@Placeholder("punisher")
public MinecraftComponent punisher() {
return punisher;
}
}

View File

@ -18,16 +18,21 @@
package com.discordsrv.bukkit.ban;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.module.type.PunishmentModule;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.api.punishment.Punishment;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.common.bansync.BanSyncModule;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.module.type.AbstractModule;
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerKickEvent;
import org.jetbrains.annotations.NotNull;
@ -44,6 +49,16 @@ public class BukkitBanModule extends AbstractModule<BukkitDiscordSRV> implements
super(discordSRV);
}
@Override
public void enable() {
discordSRV.server().getPluginManager().registerEvents(this, discordSRV.plugin());
}
@Override
public void disable() {
HandlerList.unregisterAll(this);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerKick(PlayerKickEvent event) {
Player player = event.getPlayer();
@ -59,7 +74,7 @@ public class BukkitBanModule extends AbstractModule<BukkitDiscordSRV> implements
}
@Override
public CompletableFuture<com.discordsrv.api.punishment.Punishment> getBan(@NotNull UUID playerUUID) {
public CompletableFuture<Punishment> getBan(@NotNull UUID playerUUID) {
CompletableFuture<BanEntry> entryFuture;
if (PaperBanList.IS_AVAILABLE) {
entryFuture = CompletableFuture.completedFuture(PaperBanList.getBanEntry(discordSRV.server(), playerUUID));
@ -74,20 +89,26 @@ public class BukkitBanModule extends AbstractModule<BukkitDiscordSRV> implements
return null;
}
Date expiration = ban.getExpiration();
return new Punishment(expiration != null ? expiration.toInstant() : null, ban.getReason(), ban.getSource());
return new Punishment(
expiration != null ? expiration.toInstant() : null,
ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(ban.getReason())),
ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(ban.getSource()))
);
});
}
@Override
public CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable String reason, @NotNull String punisher) {
public CompletableFuture<Void> addBan(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher) {
String reasonLegacy = reason != null ? BukkitComponentSerializer.legacy().serialize(ComponentUtil.fromAPI(reason)) : null;
String punisherLegacy = BukkitComponentSerializer.legacy().serialize(ComponentUtil.fromAPI(punisher));
if (PaperBanList.IS_AVAILABLE) {
PaperBanList.addBan(discordSRV.server(), playerUUID, until, reason, punisher);
PaperBanList.addBan(discordSRV.server(), playerUUID, until, reasonLegacy, punisherLegacy);
return CompletableFuture.completedFuture(null);
}
BanList banList = discordSRV.server().getBanList(BanList.Type.NAME);
return discordSRV.playerProvider().lookupOfflinePlayer(playerUUID).thenApply(offlinePlayer -> {
banList.addBan(offlinePlayer.username(), reason, until != null ? Date.from(until) : null, punisher);
banList.addBan(offlinePlayer.username(), reasonLegacy, until != null ? Date.from(until) : null, punisherLegacy);
return null;
});
}
@ -105,4 +126,17 @@ public class BukkitBanModule extends AbstractModule<BukkitDiscordSRV> implements
return null;
});
}
@Override
public CompletableFuture<Void> kickPlayer(@NotNull DiscordSRVPlayer srvPlayer, @NotNull MinecraftComponent message) {
Player player = discordSRV.server().getPlayer(srvPlayer.uniqueId());
if (player == null) {
return CompletableFuture.completedFuture(null);
}
return discordSRV.scheduler().executeOnMainThread(
player,
() -> player.kickPlayer(BukkitComponentSerializer.legacy().serialize(ComponentUtil.fromAPI(message)))
);
}
}

View File

@ -107,15 +107,20 @@ public class EssentialsXIntegration
@Override
public CompletableFuture<com.discordsrv.api.punishment.Punishment> getMute(@NotNull UUID playerUUID) {
return getUser(playerUUID).thenApply(user -> new Punishment(Instant.ofEpochMilli(user.getMuteTimeout()), user.getMuteReason(), null));
return getUser(playerUUID).thenApply(user -> new Punishment(
Instant.ofEpochMilli(user.getMuteTimeout()),
ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(user.getMuteReason())),
null
));
}
@Override
public CompletableFuture<Void> addMute(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable String reason, @NotNull String punisher) {
public CompletableFuture<Void> addMute(@NotNull UUID playerUUID, @Nullable Instant until, @Nullable MinecraftComponent reason, @NotNull MinecraftComponent punisher) {
String reasonLegacy = reason != null ? BukkitComponentSerializer.legacy().serialize(ComponentUtil.fromAPI(reason)) : null;
return getUser(playerUUID).thenApply(user -> {
user.setMuted(true);
user.setMuteTimeout(until != null ? until.toEpochMilli() : 0);
user.setMuteReason(reason);
user.setMuteReason(reasonLegacy);
return null;
});
}

View File

@ -27,6 +27,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@ -54,9 +55,20 @@ public class BukkitPlayerProvider extends ServerPlayerProvider<BukkitPlayer, Buk
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLogin(PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
return;
}
addPlayer(event.getPlayer(), false);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) {
if (player(event.getPlayer()) == null) {
// The player should already be added by this point, but just in case
addPlayer(event.getPlayer(), false);
}
}
private void addPlayer(Player player, boolean initial) {
addPlayer(player.getUniqueId(), new BukkitPlayer(discordSRV, player), initial);
}

View File

@ -18,6 +18,7 @@
package com.discordsrv.common.bansync;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.linking.AccountLinkedEvent;
@ -26,6 +27,7 @@ import com.discordsrv.api.punishment.Punishment;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.bansync.enums.BanSyncCause;
import com.discordsrv.common.bansync.enums.BanSyncResult;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.BanSyncConfig;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.player.IPlayer;
@ -33,13 +35,14 @@ import com.discordsrv.common.someone.Someone;
import com.discordsrv.common.sync.AbstractSyncModule;
import com.discordsrv.common.sync.SyncFail;
import com.discordsrv.common.sync.cause.GenericSyncCauses;
import com.discordsrv.common.sync.result.ISyncResult;
import com.discordsrv.common.sync.enums.SyncDirection;
import com.discordsrv.common.sync.result.GenericSyncResults;
import com.discordsrv.common.sync.result.ISyncResult;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.UserSnowflake;
import net.dv8tion.jda.api.events.guild.GuildAuditLogEntryCreateEvent;
@ -57,7 +60,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig, Void, Long, Punishment> {
public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig, BanSyncModule.Game, Long, Punishment> {
private final Map<Long, PunishmentEvent> events = new ConcurrentHashMap<>();
@ -65,22 +68,13 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
super(discordSRV, "BAN_SYNC");
}
@Override
public boolean isEnabled() {
if (discordSRV.config().banSync.serverId == 0) {
//return false; // TODO
}
return super.isEnabled();
}
@Override
public @NotNull Collection<DiscordGatewayIntent> requiredIntents() {
return Collections.singleton(DiscordGatewayIntent.GUILD_MODERATION);
}
public void notifyBanned(IPlayer player, @Nullable Punishment punishment) {
gameChanged(BanSyncCause.PLAYER_BANNED, Someone.of(player.uniqueId()), null, punishment);
gameChanged(BanSyncCause.PLAYER_BANNED, Someone.of(player.uniqueId()), Game.INSTANCE, punishment);
}
@Override
@ -95,12 +89,12 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
@Override
public String gameTerm() {
return "ban";
return "game ban";
}
@Override
public String discordTerm() {
return "ban";
return "Discord ban";
}
@Override
@ -115,17 +109,19 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
return (oneActive == twoActive) ? GenericSyncResults.both(oneActive) : null;
}
private PunishmentEvent upsertEvent(long userId, boolean newState) {
return events.computeIfAbsent(userId, key -> new PunishmentEvent(userId, newState));
private PunishmentEvent upsertEvent(long guildId, long userId, boolean newState) {
return events.computeIfAbsent(userId, key -> new PunishmentEvent(guildId, userId, newState));
}
private class PunishmentEvent {
private final long guildId;
private final long userId;
private final boolean newState;
private final Future<?> future;
public PunishmentEvent(long userId, boolean newState) {
public PunishmentEvent(long guildId, long userId, boolean newState) {
this.guildId = guildId;
this.userId = userId;
this.newState = newState;
@ -139,25 +135,26 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
}
if (newState && punishment == null) {
punishment = new Punishment(null, null, null);
punishment = Punishment.UNKNOWN;
}
gameChanged(
discordChanged(
GenericSyncCauses.LINK,
Someone.of(userId),
null,
guildId,
punishment
);
events.remove(userId);
}
}
@Subscribe
public void onGuildBan(GuildBanEvent event) {
upsertEvent(event.getUser().getIdLong(), true);
upsertEvent(event.getGuild().getIdLong(), event.getUser().getIdLong(), true);
}
@Subscribe
public void onGuildUnban(GuildUnbanEvent event) {
upsertEvent(event.getUser().getIdLong(), false);
upsertEvent(event.getGuild().getIdLong(), event.getUser().getIdLong(), false);
}
@Subscribe
@ -168,18 +165,36 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
return;
}
long punisherId = entry.getUserIdLong();
User punisher = event.getJDA().getUserById(punisherId);
String punishmentName = punisher != null ? punisher.getName() : Long.toUnsignedString(punisherId);
Guild guild = event.getGuild();
long guildId = guild.getIdLong();
List<BanSyncConfig> configs = configsForDiscord.get(guildId);
BanSyncConfig config = configs.isEmpty() ? null : configs.get(0);
if (config == null) {
return;
}
long punisherId = entry.getUserIdLong();
// This user should be cacheable as they just made an auditable action
User punisher = event.getJDA().getUserById(punisherId);
Member punisherMember = punisher != null ? guild.getMember(punisher) : null;
MinecraftComponent punisherName = discordSRV.componentFactory().textBuilder(config.gamePunisherFormat)
.addContext(punisher, punisherMember)
.applyPlaceholderService()
.build();
Punishment punishment = new Punishment(null, entry.getReason(), punishmentName);
long bannedUserId = entry.getTargetIdLong();
// Apply punishments instantly when audit log events arrive.
if (actionType == ActionType.BAN) {
upsertEvent(bannedUserId, true).applyPunishment(punishment);
upsertEvent(guildId, bannedUserId, true).applyPunishment(new Punishment(
null,
ComponentUtil.fromPlain(entry.getReason()),
punisherName
));
} else {
upsertEvent(bannedUserId, false).applyPunishment(punishment);
upsertEvent(guildId, bannedUserId, false).applyPunishment(null);
}
}
@ -217,7 +232,7 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
}
private Punishment punishment(Guild.Ban ban) {
return ban != null ? new Punishment(null, ban.getReason(), ban.getUser().getName()) : null;
return ban != null ? new Punishment(null, ComponentUtil.fromPlain(ban.getReason()), null) : null;
}
@Override
@ -250,8 +265,7 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
UserSnowflake snowflake = UserSnowflake.fromId(userId);
if (newState != null) {
return guild.ban(snowflake, config.discordMessageHoursToDelete, TimeUnit.HOURS)
.reason(discordSRV.placeholderService().replacePlaceholders(config.discordBanReasonFormat,
newState))
.reason(discordSRV.placeholderService().replacePlaceholders(config.discordBanReasonFormat, newState))
.submit()
.thenApply(v -> GenericSyncResults.ADD_DISCORD);
} else {
@ -274,15 +288,37 @@ public class BanSyncModule extends AbstractSyncModule<DiscordSRV, BanSyncConfig,
}
if (newState != null) {
String reason = discordSRV.placeholderService().replacePlaceholders(config.gameBanReasonFormat,
newState);
String punisher = discordSRV.placeholderService().replacePlaceholders(config.gamePunisherFormat,
newState);
MinecraftComponent reason = discordSRV.componentFactory().textBuilder(config.gameBanReasonFormat)
.addContext(newState)
.applyPlaceholderService()
.build();
MinecraftComponent punisher = discordSRV.componentFactory().textBuilder(config.gamePunisherFormat)
.addContext(newState)
.applyPlaceholderService()
.build();
return bans.addBan(playerUUID, null, reason, punisher)
.thenCompose(v -> {
IPlayer player = discordSRV.playerProvider().player(playerUUID);
if (player == null) {
return CompletableFuture.completedFuture(null);
}
MinecraftComponent kickMessage = discordSRV.componentFactory()
.textBuilder(config.gameKickReason)
.addContext(newState)
.applyPlaceholderService()
.build();
return bans.kickPlayer(player, kickMessage);
})
.thenApply(v -> GenericSyncResults.ADD_GAME);
} else {
return bans.removeBan(playerUUID).thenApply(v -> GenericSyncResults.REMOVE_GAME);
}
}
public enum Game {
INSTANCE
}
}

View File

@ -54,6 +54,14 @@ public final class ComponentUtil {
return component.asPlainString().isEmpty();
}
@Contract("null -> null")
public static MinecraftComponent fromPlain(@Nullable String plainText) {
if (plainText == null) {
return null;
}
return toAPI(Component.text(plainText));
}
@Contract("null -> null")
public static MinecraftComponent toAPI(Component component) {
if (component == null) {

View File

@ -18,21 +18,25 @@
package com.discordsrv.common.config.main;
import com.discordsrv.common.bansync.BanSyncModule;
import com.discordsrv.common.config.main.generic.AbstractSyncConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ConfigSerializable
public class BanSyncConfig extends AbstractSyncConfig<BanSyncConfig, Void, Long> {
public class BanSyncConfig extends AbstractSyncConfig<BanSyncConfig, BanSyncModule.Game, Long> {
@Comment("The id for the Discord server where the bans should be synced from/to")
public long serverId = 0L;
@Comment("The reason applied when creating new bans in Minecraft")
public String gameBanReasonFormat = "%reason%";
public String gameBanReasonFormat = "%punishment_reason%";
@Comment("The punisher applied when creating new bans in Minecraft")
public String gamePunisherFormat = "@%user_effective_server_name%";
public String gamePunisherFormat = "%user_color%@%user_name%";
@Comment("The kick reason when a ban is applied to a online player")
public String gameKickReason = "&cYou have been banned for &f%punishment_reason% &cby &f%punishment_punisher%";
@Comment("The reason applied when creating new bans in Discord")
public String discordBanReasonFormat = "Banned by %punishment_punisher% in Minecraft for %punishment_reason%, ends: %punishment_until:'YYYY-MM-dd HH:mm:ss zzz'|text:'Never'%";
@ -52,8 +56,8 @@ public class BanSyncConfig extends AbstractSyncConfig<BanSyncConfig, Void, Long>
}
@Override
public Void gameId() {
return null;
public BanSyncModule.Game gameId() {
return BanSyncModule.Game.INSTANCE;
}
@Override

View File

@ -93,22 +93,6 @@ public class GroupSyncModule extends AbstractSyncModule<DiscordSRV, GroupSyncCon
return null;
}
@Override
public boolean isEnabled() {
boolean any = false;
for (GroupSyncConfig.PairConfig pair : discordSRV.config().groupSync.pairs) {
if (pair.isSet()) {
any = true;
break;
}
}
if (!any) {
return false;
}
return super.isEnabled();
}
@Subscribe
public void onDebugGenerate(DebugGenerateEvent event) {
StringBuilder builder = new StringBuilder("Active pairs:");
@ -251,6 +235,8 @@ public class GroupSyncModule extends AbstractSyncModule<DiscordSRV, GroupSyncCon
@Override
public CompletableFuture<ISyncResult> applyDiscord(GroupSyncConfig.PairConfig config, long userId, Boolean newState) {
boolean stateToApply = newState != null && newState;
DiscordRole role = discordSRV.discordAPI().getRoleById(config.roleId);
if (role == null) {
return CompletableFutureUtil.failed(new SyncFail(GroupSyncResult.ROLE_DOESNT_EXIST));
@ -258,11 +244,11 @@ public class GroupSyncModule extends AbstractSyncModule<DiscordSRV, GroupSyncCon
Map<Long, Boolean> expected = expectedDiscordChanges.get(userId, key -> new ConcurrentHashMap<>());
if (expected != null) {
expected.put(config.roleId, newState);
expected.put(config.roleId, stateToApply);
}
return role.getGuild().retrieveMemberById(userId)
.thenCompose(member -> newState
.thenCompose(member -> stateToApply
? member.addRole(role).thenApply(v -> (ISyncResult) GenericSyncResults.ADD_DISCORD)
: member.removeRole(role).thenApply(v -> GenericSyncResults.REMOVE_DISCORD)
).whenComplete((r, t) -> {
@ -275,13 +261,15 @@ public class GroupSyncModule extends AbstractSyncModule<DiscordSRV, GroupSyncCon
@Override
public CompletableFuture<ISyncResult> applyGame(GroupSyncConfig.PairConfig config, UUID playerUUID, Boolean newState) {
boolean stateToApply = newState != null && newState;
Map<String, Boolean> expected = expectedMinecraftChanges.get(playerUUID, key -> new ConcurrentHashMap<>());
if (expected != null) {
expected.put(config.groupName, newState);
expected.put(config.groupName, stateToApply);
}
CompletableFuture<ISyncResult> future =
newState
stateToApply
? addGroup(playerUUID, config).thenApply(v -> GenericSyncResults.ADD_GAME)
: removeGroup(playerUUID, config).thenApply(v -> GenericSyncResults.REMOVE_GAME);
return future.exceptionally(t -> {

View File

@ -209,7 +209,7 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
Component messageComponent = DiscordSRVMinecraftRenderer.getWithContext(guild, chatConfig, () ->
discordSRV.componentFactory().minecraftSerializer().serialize(finalMessage));
if (discordSRV.componentFactory().plainSerializer().serialize(messageComponent).trim().isEmpty() && !attachments) {
if (ComponentUtil.isEmpty(messageComponent) && !attachments) {
// Check empty-ness again after rendering
return;
}

View File

@ -32,9 +32,10 @@ import com.discordsrv.common.someone.Someone;
import com.discordsrv.common.sync.cause.GenericSyncCauses;
import com.discordsrv.common.sync.cause.ISyncCause;
import com.discordsrv.common.sync.enums.SyncDirection;
import com.discordsrv.common.sync.result.GenericSyncResults;
import com.discordsrv.common.sync.enums.SyncSide;
import com.discordsrv.common.sync.result.GenericSyncResults;
import com.discordsrv.common.sync.result.ISyncResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
@ -63,8 +64,8 @@ public abstract class AbstractSyncModule<
> extends AbstractModule<DT> {
protected final Map<C, Future<?>> syncs = new LinkedHashMap<>();
private final Map<G, List<C>> configsForGame = new ConcurrentHashMap<>();
private final Map<D, List<C>> configsForDiscord = new ConcurrentHashMap<>();
protected final Map<G, List<C>> configsForGame = new ConcurrentHashMap<>();
protected final Map<D, List<C>> configsForDiscord = new ConcurrentHashMap<>();
public AbstractSyncModule(DT discordSRV, String loggerName) {
super(discordSRV, new NamedLogger(discordSRV, loggerName));
@ -82,6 +83,22 @@ public abstract class AbstractSyncModule<
*/
protected abstract List<C> configs();
@Override
public boolean isEnabled() {
boolean any = false;
for (C config : configs()) {
if (config.isSet()) {
any = true;
break;
}
}
if (!any) {
return false;
}
return super.isEnabled();
}
@Override
public void reload(Consumer<DiscordSRVApi.ReloadResult> resultConsumer) {
synchronized (syncs) {
@ -182,9 +199,9 @@ public abstract class AbstractSyncModule<
* @param newState the newState to apply
* @return a future with the result of the synchronization
*/
protected abstract CompletableFuture<ISyncResult> applyDiscord(C config, long userId, S newState);
protected abstract CompletableFuture<ISyncResult> applyDiscord(C config, long userId, @Nullable S newState);
protected CompletableFuture<ISyncResult> applyDiscordIfDoesNotMatch(C config, long userId, S newState) {
protected CompletableFuture<ISyncResult> applyDiscordIfDoesNotMatch(C config, long userId, @Nullable S newState) {
return getDiscord(config, userId).thenCompose(currentState -> {
ISyncResult result = doesStateMatch(newState, currentState);
if (result != null) {
@ -203,9 +220,9 @@ public abstract class AbstractSyncModule<
* @param newState the newState to apply
* @return a future with the result of the synchronization
*/
protected abstract CompletableFuture<ISyncResult> applyGame(C config, UUID playerUUID, S newState);
protected abstract CompletableFuture<ISyncResult> applyGame(C config, UUID playerUUID, @Nullable S newState);
protected CompletableFuture<ISyncResult> applyGameIfDoesNotMatch(C config, UUID playerUUID, S newState) {
protected CompletableFuture<ISyncResult> applyGameIfDoesNotMatch(C config, UUID playerUUID, @Nullable S newState) {
return getGame(config, playerUUID).thenCompose(currentState -> {
ISyncResult result = doesStateMatch(currentState, newState);
if (result != null) {
@ -216,7 +233,7 @@ public abstract class AbstractSyncModule<
});
}
protected CompletableFuture<SyncSummary<C>> discordChanged(ISyncCause cause, Someone someone, D discordId, S newState) {
protected CompletableFuture<SyncSummary<C>> discordChanged(ISyncCause cause, Someone someone, D discordId, @Nullable S newState) {
List<C> gameConfigs = configsForDiscord.get(discordId);
if (gameConfigs == null) {
return CompletableFuture.completedFuture(null);
@ -264,7 +281,7 @@ public abstract class AbstractSyncModule<
});
}
protected CompletableFuture<SyncSummary<C>> gameChanged(ISyncCause cause, Someone someone, G gameId, S newState) {
protected CompletableFuture<SyncSummary<C>> gameChanged(ISyncCause cause, Someone someone, @NotNull G gameId, @Nullable S newState) {
List<C> discordConfigs = configsForGame.get(gameId);
if (discordConfigs == null) {
return CompletableFuture.completedFuture(null);