diff --git a/api/src/main/java/com/discordsrv/api/punishment/Punishment.java b/api/src/main/java/com/discordsrv/api/punishment/Punishment.java index a9713078..8ce872d5 100644 --- a/api/src/main/java/com/discordsrv/api/punishment/Punishment.java +++ b/api/src/main/java/com/discordsrv/api/punishment/Punishment.java @@ -1,3 +1,26 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2024 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package com.discordsrv.api.punishment; import org.jetbrains.annotations.Nullable; diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/ban/BukkitBanModule.java b/bukkit/src/main/java/com/discordsrv/bukkit/ban/BukkitBanModule.java index ca88ad0e..81981b9e 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/ban/BukkitBanModule.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/ban/BukkitBanModule.java @@ -70,6 +70,9 @@ public class BukkitBanModule extends AbstractModule implements } return entryFuture.thenApply(ban -> { + if (ban == null) { + return null; + } Date expiration = ban.getExpiration(); return new Punishment(expiration != null ? expiration.toInstant() : null, ban.getReason(), ban.getSource()); }); diff --git a/common/src/main/java/com/discordsrv/common/bansync/BanSyncModule.java b/common/src/main/java/com/discordsrv/common/bansync/BanSyncModule.java index 47c4d148..bdda2377 100644 --- a/common/src/main/java/com/discordsrv/common/bansync/BanSyncModule.java +++ b/common/src/main/java/com/discordsrv/common/bansync/BanSyncModule.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.bansync; import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent; @@ -71,7 +89,7 @@ public class BanSyncModule extends AbstractSyncModule applyDiscord(BanSyncConfig config, long userId, Punishment state) { + protected CompletableFuture applyDiscord(BanSyncConfig config, long userId, Punishment newState) { if (config.direction == SyncDirection.DISCORD_TO_MINECRAFT) { return CompletableFuture.completedFuture(GenericSyncResults.WRONG_DIRECTION); } @@ -228,9 +248,10 @@ public class BanSyncModule extends AbstractSyncModule GenericSyncResults.ADD_DISCORD); } else { @@ -242,7 +263,7 @@ public class BanSyncModule extends AbstractSyncModule applyGame(BanSyncConfig config, UUID playerUUID, Punishment state) { + protected CompletableFuture applyGame(BanSyncConfig config, UUID playerUUID, Punishment newState) { if (config.direction == SyncDirection.MINECRAFT_TO_DISCORD) { return CompletableFuture.completedFuture(GenericSyncResults.WRONG_DIRECTION); } @@ -252,9 +273,11 @@ public class BanSyncModule extends AbstractSyncModule GenericSyncResults.ADD_GAME); } else { diff --git a/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncCause.java b/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncCause.java index ba4abb2a..cb66d88a 100644 --- a/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncCause.java +++ b/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncCause.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.bansync.enums; import com.discordsrv.common.sync.cause.ISyncCause; diff --git a/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncResult.java b/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncResult.java index f0bffc80..4dee08f3 100644 --- a/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncResult.java +++ b/common/src/main/java/com/discordsrv/common/bansync/enums/BanSyncResult.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.bansync.enums; import com.discordsrv.common.sync.result.ISyncResult; diff --git a/common/src/main/java/com/discordsrv/common/config/main/BanSyncConfig.java b/common/src/main/java/com/discordsrv/common/config/main/BanSyncConfig.java index 6a1c484e..05106f14 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/BanSyncConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/BanSyncConfig.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.config.main; import com.discordsrv.common.config.main.generic.AbstractSyncConfig; diff --git a/common/src/main/java/com/discordsrv/common/config/main/generic/AbstractSyncConfig.java b/common/src/main/java/com/discordsrv/common/config/main/generic/AbstractSyncConfig.java index 15d6396f..e4d0fe47 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/generic/AbstractSyncConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/generic/AbstractSyncConfig.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.config.main.generic; import com.discordsrv.common.DiscordSRV; diff --git a/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java b/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java index 214f8989..8ae46c33 100644 --- a/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java +++ b/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java @@ -66,7 +66,7 @@ public class GroupSyncModule extends AbstractSyncModule serverContext, GroupSyncCause cause, - Boolean state + boolean state ) { if (cause.isDiscordSRVCanCause() && checkExpectation(expectedMinecraftChanges, playerUUID, groupName, state)) { return; @@ -247,7 +250,7 @@ public class GroupSyncModule extends AbstractSyncModule applyDiscord(GroupSyncConfig.PairConfig config, long userId, Boolean state) { + public CompletableFuture applyDiscord(GroupSyncConfig.PairConfig config, long userId, Boolean newState) { DiscordRole role = discordSRV.discordAPI().getRoleById(config.roleId); if (role == null) { return CompletableFutureUtil.failed(new SyncFail(GroupSyncResult.ROLE_DOESNT_EXIST)); @@ -255,11 +258,11 @@ public class GroupSyncModule extends AbstractSyncModule expected = expectedDiscordChanges.get(userId, key -> new ConcurrentHashMap<>()); if (expected != null) { - expected.put(config.roleId, state); + expected.put(config.roleId, newState); } return role.getGuild().retrieveMemberById(userId) - .thenCompose(member -> state + .thenCompose(member -> newState ? member.addRole(role).thenApply(v -> (ISyncResult) GenericSyncResults.ADD_DISCORD) : member.removeRole(role).thenApply(v -> GenericSyncResults.REMOVE_DISCORD) ).whenComplete((r, t) -> { @@ -271,14 +274,14 @@ public class GroupSyncModule extends AbstractSyncModule applyGame(GroupSyncConfig.PairConfig config, UUID playerUUID, Boolean state) { + public CompletableFuture applyGame(GroupSyncConfig.PairConfig config, UUID playerUUID, Boolean newState) { Map expected = expectedMinecraftChanges.get(playerUUID, key -> new ConcurrentHashMap<>()); if (expected != null) { - expected.put(config.groupName, state); + expected.put(config.groupName, newState); } CompletableFuture future = - state + newState ? addGroup(playerUUID, config).thenApply(v -> GenericSyncResults.ADD_GAME) : removeGroup(playerUUID, config).thenApply(v -> GenericSyncResults.REMOVE_GAME); return future.exceptionally(t -> { diff --git a/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java b/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java deleted file mode 100644 index e69de29b..00000000 diff --git a/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java b/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java index 9a0f7ac9..53d70d7f 100644 --- a/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java +++ b/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java @@ -178,8 +178,8 @@ public class LuckPermsIntegration extends PluginIntegration implemen InheritanceNode node = InheritanceNode.builder(group).context(contexts).build(); DataMutateResult result = function.apply(user.data(), node); - if (result != DataMutateResult.SUCCESS) { - return CompletableFutureUtil.failed(new MessageException(result.name())); + if (!result.wasSuccessful()) { + return CompletableFutureUtil.failed(new MessageException("Group mutate failed: " + result.name())); } return luckPerms.getUserManager().saveUser(user); diff --git a/common/src/main/java/com/discordsrv/common/someone/Someone.java b/common/src/main/java/com/discordsrv/common/someone/Someone.java index 6711d069..d33222e9 100644 --- a/common/src/main/java/com/discordsrv/common/someone/Someone.java +++ b/common/src/main/java/com/discordsrv/common/someone/Someone.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.someone; import com.discordsrv.api.discord.entity.DiscordUser; diff --git a/common/src/main/java/com/discordsrv/common/sync/AbstractSyncModule.java b/common/src/main/java/com/discordsrv/common/sync/AbstractSyncModule.java index 6a5f367d..c793c757 100644 --- a/common/src/main/java/com/discordsrv/common/sync/AbstractSyncModule.java +++ b/common/src/main/java/com/discordsrv/common/sync/AbstractSyncModule.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync; import com.discordsrv.api.DiscordSRVApi; @@ -17,11 +35,12 @@ 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.ISyncResult; -import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.function.Consumer; @@ -51,17 +70,17 @@ public abstract class AbstractSyncModule< super(discordSRV, new NamedLogger(discordSRV, loggerName)); } - public abstract String syncName(); - public abstract String logName(); + protected abstract String syncName(); + protected abstract String logFileName(); - public abstract String gameTerm(); - public abstract String discordTerm(); + protected abstract String gameTerm(); + protected abstract String discordTerm(); /** * Returns a list of all in use synchronizables. * @return a list of configurations for synchronizables */ - public abstract List configs(); + protected abstract List configs(); @Override public void reload(Consumer resultConsumer) { @@ -131,12 +150,11 @@ public abstract class AbstractSyncModule< } /** - * Check if the provided state is active or inactive, should this not match for the state of the two sides, synchronization will occur. - * - * @param state the state - * @return {@code true} indicating the provided state is "active" + * Checks if the given new and current state are the same, basically meaning that no update is necessary. + * @return the result stating the states are the same, otherwise {@code null} to state they are not */ - protected abstract boolean isActive(S state); + @Nullable + protected abstract ISyncResult doesStateMatch(S one, S two); /** * Gets the current state of the provided config for the specified user on Discord. @@ -161,18 +179,18 @@ public abstract class AbstractSyncModule< * * @param config the configuration for the synchronizable * @param userId the Discord user id - * @param state the state to apply + * @param newState the newState to apply * @return a future with the result of the synchronization */ - protected abstract CompletableFuture applyDiscord(C config, long userId, S state); + protected abstract CompletableFuture applyDiscord(C config, long userId, S newState); - protected CompletableFuture applyDiscordIfNot(C config, long userId, S state) { - return getDiscord(config, userId).thenCompose(value -> { - boolean actualValue; - if ((actualValue = isActive(state)) == isActive(value)) { - return CompletableFuture.completedFuture(actualValue ? GenericSyncResults.BOTH_TRUE : GenericSyncResults.BOTH_FALSE); + protected CompletableFuture applyDiscordIfDoesNotMatch(C config, long userId, S newState) { + return getDiscord(config, userId).thenCompose(currentState -> { + ISyncResult result = doesStateMatch(newState, currentState); + if (result != null) { + return CompletableFuture.completedFuture(result); } else { - return applyDiscord(config, userId, state); + return applyDiscord(config, userId, newState); } }); } @@ -182,23 +200,23 @@ public abstract class AbstractSyncModule< * * @param config the configuration for the synchronizable * @param playerUUID the Minecraft player {@link UUID} - * @param state the state to apply + * @param newState the newState to apply * @return a future with the result of the synchronization */ - protected abstract CompletableFuture applyGame(C config, UUID playerUUID, S state); + protected abstract CompletableFuture applyGame(C config, UUID playerUUID, S newState); - protected CompletableFuture applyGameIfNot(C config, UUID playerUUID, S state) { - return getGame(config, playerUUID).thenCompose(value -> { - boolean active; - if ((active = isActive(state)) == isActive(value)) { - return CompletableFuture.completedFuture(active ? GenericSyncResults.BOTH_TRUE : GenericSyncResults.BOTH_FALSE); + protected CompletableFuture applyGameIfDoesNotMatch(C config, UUID playerUUID, S newState) { + return getGame(config, playerUUID).thenCompose(currentState -> { + ISyncResult result = doesStateMatch(currentState, newState); + if (result != null) { + return CompletableFuture.completedFuture(result); } else { - return applyGame(config, playerUUID, state); + return applyGame(config, playerUUID, newState); } }); } - protected CompletableFuture> discordChanged(ISyncCause cause, Someone someone, D discordId, S state) { + protected CompletableFuture> discordChanged(ISyncCause cause, Someone someone, D discordId, S newState) { List gameConfigs = configsForDiscord.get(discordId); if (gameConfigs == null) { return CompletableFuture.completedFuture(null); @@ -206,10 +224,10 @@ public abstract class AbstractSyncModule< return someone.withLinkedAccounts(discordSRV).thenApply(resolved -> { if (resolved == null) { - return new SyncSummary(cause, someone).fail(GenericSyncResults.NOT_LINKED); + return new SyncSummary<>(this, cause, someone).fail(GenericSyncResults.NOT_LINKED); } - SyncSummary summary = new SyncSummary<>(cause, resolved); + SyncSummary summary = new SyncSummary<>(this, cause, resolved); for (C config : gameConfigs) { SyncDirection direction = config.direction; if (direction == SyncDirection.MINECRAFT_TO_DISCORD) { @@ -218,7 +236,7 @@ public abstract class AbstractSyncModule< continue; } - summary.appendResult(config, applyGameIfNot(config, resolved.playerUUID(), state)); + summary.appendResult(config, applyGameIfDoesNotMatch(config, resolved.playerUUID(), newState)); // If the sync is bidirectional, also sync anything else linked to the same Minecraft id if (direction == SyncDirection.DISCORD_TO_MINECRAFT) { @@ -231,11 +249,11 @@ public abstract class AbstractSyncModule< } for (C gameConfig : discordConfigs) { - if (gameConfig.discordId() == discordId) { + if (Objects.equals(gameConfig.discordId(), discordId)) { continue; } - summary.appendResult(gameConfig, applyDiscordIfNot(gameConfig, resolved.userId(), state)); + summary.appendResult(gameConfig, applyDiscordIfDoesNotMatch(gameConfig, resolved.userId(), newState)); } } return summary; @@ -246,7 +264,7 @@ public abstract class AbstractSyncModule< }); } - protected CompletableFuture> gameChanged(ISyncCause cause, Someone someone, G gameId, S state) { + protected CompletableFuture> gameChanged(ISyncCause cause, Someone someone, G gameId, S newState) { List discordConfigs = configsForGame.get(gameId); if (discordConfigs == null) { return CompletableFuture.completedFuture(null); @@ -254,10 +272,10 @@ public abstract class AbstractSyncModule< return someone.withLinkedAccounts(discordSRV).thenApply(resolved -> { if (resolved == null) { - return new SyncSummary(cause, someone).fail(GenericSyncResults.NOT_LINKED); + return new SyncSummary<>(this, cause, someone).fail(GenericSyncResults.NOT_LINKED); } - SyncSummary summary = new SyncSummary<>(cause, resolved); + SyncSummary summary = new SyncSummary<>(this, cause, resolved); for (C config : discordConfigs) { SyncDirection direction = config.direction; if (direction == SyncDirection.DISCORD_TO_MINECRAFT) { @@ -266,7 +284,7 @@ public abstract class AbstractSyncModule< continue; } - summary.appendResult(config, applyDiscordIfNot(config, resolved.userId(), state)); + summary.appendResult(config, applyDiscordIfDoesNotMatch(config, resolved.userId(), newState)); // If the sync is bidirectional, also sync anything else linked to the same Discord id if (direction == SyncDirection.MINECRAFT_TO_DISCORD) { @@ -279,11 +297,11 @@ public abstract class AbstractSyncModule< } for (C gameConfig : gameConfigs) { - if (gameConfig.gameId() == gameId) { + if (Objects.equals(gameConfig.gameId(), gameId)) { continue; } - summary.appendResult(gameConfig, applyGameIfNot(gameConfig, resolved.playerUUID(), state)); + summary.appendResult(gameConfig, applyGameIfDoesNotMatch(gameConfig, resolved.playerUUID(), newState)); } } return summary; @@ -297,11 +315,11 @@ public abstract class AbstractSyncModule< public CompletableFuture> resyncAll(ISyncCause cause, Someone someone) { return someone.withLinkedAccounts(discordSRV).thenApply(resolved -> { if (resolved == null) { - return new SyncSummary(cause, someone).fail(GenericSyncResults.NOT_LINKED); + return new SyncSummary<>(this, cause, someone).fail(GenericSyncResults.NOT_LINKED); } - SyncSummary summary = new SyncSummary<>(cause, resolved); - List configs = configs(); + SyncSummary summary = new SyncSummary<>(this, cause, resolved); + Set configs = syncs.keySet(); for (C config : configs) { summary.appendResult(config, resync(config, resolved)); @@ -317,10 +335,10 @@ public abstract class AbstractSyncModule< protected CompletableFuture> resync(ISyncCause cause, C config, Someone someone) { return someone.withLinkedAccounts(discordSRV).thenApply(resolved -> { if (resolved == null) { - return new SyncSummary(cause, someone).fail(GenericSyncResults.NOT_LINKED); + return new SyncSummary<>(this, cause, someone).fail(GenericSyncResults.NOT_LINKED); } - return new SyncSummary(cause, resolved) + return new SyncSummary(this, cause, resolved) .appendResult(config, resync(config, resolved)); }).whenComplete((summary, t) -> { if (summary != null) { @@ -340,10 +358,9 @@ public abstract class AbstractSyncModule< S gameState = gameGet.join(); S discordState = discordGet.join(); - boolean bothState; - if ((bothState = (gameState != null)) == (discordState != null)) { - // Already in sync - return CompletableFuture.completedFuture((ISyncResult) (bothState ? GenericSyncResults.BOTH_TRUE : GenericSyncResults.BOTH_FALSE)); + ISyncResult alreadyInSyncResult = doesStateMatch(gameState, discordState); + if (alreadyInSyncResult != null) { + return CompletableFuture.completedFuture(alreadyInSyncResult); } SyncSide side = config.tieBreaker; @@ -392,45 +409,71 @@ public abstract class AbstractSyncModule< private String formatResults(SyncSummary summary, List results) { int count = results.size(); - return summary.who().toString() + return summary.who() + " (sync cause: " + summary.cause() + ")" + (count == 1 ? ": " : "\n") + String.join("\n", results); } private void logSummary(SyncSummary summary) { summary.resultFuture().whenComplete((results, t) -> { + Throwable throwableToLog = null; if (t != null) { - logger().error("Failed to " + syncName() + " " + summary.who(), t); - return; + while (t instanceof CompletionException) { + t = t.getCause(); + } + if (t instanceof SyncFail) { + SyncFail fail = (SyncFail) t; + summary.fail(fail.getResult()); + throwableToLog = fail.getCause(); + } else { + logger().error("Failed to " + syncName() + " " + summary.who() + " (sync cause: " + summary.cause() + ")", t); + return; + } } ISyncResult allFailReason = summary.allFailReason(); if (allFailReason != null) { String reason = allFailReason.format(gameTerm(), discordTerm()); - logger().debug("Failed to " + syncName() + " " + summary.who() + ": " + reason); + String message = "Failed to " + syncName() + " " + summary.who() + " (sync cause: " + summary.cause() + "): " + reason; + if (!allFailReason.isSuccess()) { + logger().error(message, throwableToLog); + } else { + logger().debug(message, throwableToLog); + } return; } - List logResults = new ArrayList<>(); - List auditResults = new ArrayList<>(); + Map> groupedResults = new LinkedHashMap<>(); for (Map.Entry entry : results.entrySet()) { C config = entry.getKey(); ISyncResult result = entry.getValue(); - String log = config.describe(); - if (StringUtils.isEmpty(log)) { - log += ": "; - } - log += result.format(gameTerm(), discordTerm()); + groupedResults.computeIfAbsent(result, key -> new ArrayList<>()).add(config.describe()); + } - logResults.add(log); + List successResults = new ArrayList<>(); + List failResults = new ArrayList<>(); + for (Map.Entry> entry : groupedResults.entrySet()) { + ISyncResult result = entry.getKey(); + String line = result.format(gameTerm(), discordTerm()) + + ": [" + String.join(", ", entry.getValue()) + "]"; if (result.isSuccess()) { - auditResults.add(log); + successResults.add(line); + } else { + failResults.add(line); } } - logger().debug(formatResults(summary, logResults)); - discordSRV.logger().writeLogForCurrentDay(logName(), formatResults(summary, auditResults)); + boolean anySuccess = !successResults.isEmpty(); + boolean anyFail = !failResults.isEmpty(); + String partially = anySuccess && anyFail ? " partially" : ""; + if (anySuccess) { + logger().debug(syncName() + partially + " succeeded for " + formatResults(summary, successResults)); + } + if (anyFail) { + logger().error(syncName() + partially + " failed for " + formatResults(summary, failResults)); + } + discordSRV.logger().writeLogForCurrentDay(logFileName(), formatResults(summary, successResults)); }); } diff --git a/common/src/main/java/com/discordsrv/common/sync/SyncFail.java b/common/src/main/java/com/discordsrv/common/sync/SyncFail.java index a93461ad..f3041c19 100644 --- a/common/src/main/java/com/discordsrv/common/sync/SyncFail.java +++ b/common/src/main/java/com/discordsrv/common/sync/SyncFail.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync; import com.discordsrv.common.sync.result.ISyncResult; diff --git a/common/src/main/java/com/discordsrv/common/sync/SyncSummary.java b/common/src/main/java/com/discordsrv/common/sync/SyncSummary.java index 62c2252a..c5fa0114 100644 --- a/common/src/main/java/com/discordsrv/common/sync/SyncSummary.java +++ b/common/src/main/java/com/discordsrv/common/sync/SyncSummary.java @@ -1,5 +1,24 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync; +import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.generic.AbstractSyncConfig; import com.discordsrv.common.future.util.CompletableFutureUtil; import com.discordsrv.common.someone.Someone; @@ -9,16 +28,19 @@ import com.discordsrv.common.sync.result.ISyncResult; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; public class SyncSummary> { + private final AbstractSyncModule syncModule; private final ISyncCause cause; private final Someone who; private ISyncResult allFailReason; private final Map> results = new ConcurrentHashMap<>(); - public SyncSummary(ISyncCause cause, Someone who) { + public SyncSummary(AbstractSyncModule syncModule, ISyncCause cause, Someone who) { + this.syncModule = syncModule; this.cause = cause; this.who = who; } @@ -55,7 +77,25 @@ public class SyncSummary> { .thenApply((__) -> { Map results = new HashMap<>(); for (Map.Entry> entry : this.results.entrySet()) { - results.put(entry.getKey(), entry.getValue().join()); + results.put(entry.getKey(), entry.getValue().exceptionally(t -> { + while (t instanceof CompletionException) { + t = t.getCause(); + } + Throwable throwableToLog = t; + ISyncResult result = null; + if (t instanceof SyncFail) { + throwableToLog = t.getCause(); + result = ((SyncFail) t).getResult(); + } + + if (throwableToLog != null) { + syncModule.logger().error( + "Error in " + syncModule.syncName() + " " + + entry.getKey().describe() + " for " + who() + + " (sync cause: " + cause() + ")", throwableToLog); + } + return result; + }).join()); } return results; }); diff --git a/common/src/main/java/com/discordsrv/common/sync/cause/GenericSyncCauses.java b/common/src/main/java/com/discordsrv/common/sync/cause/GenericSyncCauses.java index c0951a24..16be1b1a 100644 --- a/common/src/main/java/com/discordsrv/common/sync/cause/GenericSyncCauses.java +++ b/common/src/main/java/com/discordsrv/common/sync/cause/GenericSyncCauses.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync.cause; public enum GenericSyncCauses implements ISyncCause { diff --git a/common/src/main/java/com/discordsrv/common/sync/cause/ISyncCause.java b/common/src/main/java/com/discordsrv/common/sync/cause/ISyncCause.java index 59a7d140..fee769f3 100644 --- a/common/src/main/java/com/discordsrv/common/sync/cause/ISyncCause.java +++ b/common/src/main/java/com/discordsrv/common/sync/cause/ISyncCause.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync.cause; public interface ISyncCause { diff --git a/common/src/main/java/com/discordsrv/common/sync/result/GenericSyncResults.java b/common/src/main/java/com/discordsrv/common/sync/result/GenericSyncResults.java index fd456eb0..abef3c44 100644 --- a/common/src/main/java/com/discordsrv/common/sync/result/GenericSyncResults.java +++ b/common/src/main/java/com/discordsrv/common/sync/result/GenericSyncResults.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync.result; public enum GenericSyncResults implements ISyncResult { @@ -18,6 +36,10 @@ public enum GenericSyncResults implements ISyncResult { ; + public static GenericSyncResults both(boolean value) { + return value ? BOTH_TRUE : BOTH_FALSE; + } + private final String message; private final boolean success; diff --git a/common/src/main/java/com/discordsrv/common/sync/result/ISyncResult.java b/common/src/main/java/com/discordsrv/common/sync/result/ISyncResult.java index 67daf121..679b4662 100644 --- a/common/src/main/java/com/discordsrv/common/sync/result/ISyncResult.java +++ b/common/src/main/java/com/discordsrv/common/sync/result/ISyncResult.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2024 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 . + */ + package com.discordsrv.common.sync.result; import com.discordsrv.api.placeholder.util.Placeholders;