From 58929ce9577709b711baf7d8663a6bdb9a8e411a Mon Sep 17 00:00:00 2001 From: Vankka Date: Sun, 10 Apr 2022 16:52:30 +0300 Subject: [PATCH] Resync command --- .../game/command/DiscordSRVCommand.java | 6 +- .../command/subcommand/ResyncCommand.java | 113 ++++++++++++++++++ .../common/groupsync/GroupSyncModule.java | 29 +++-- .../groupsync/SynchronizationSummary.java | 2 +- 4 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 common/src/main/java/com/discordsrv/common/command/game/command/subcommand/ResyncCommand.java diff --git a/common/src/main/java/com/discordsrv/common/command/game/command/DiscordSRVCommand.java b/common/src/main/java/com/discordsrv/common/command/game/command/DiscordSRVCommand.java index 857b5f89..e56d1c84 100644 --- a/common/src/main/java/com/discordsrv/common/command/game/command/DiscordSRVCommand.java +++ b/common/src/main/java/com/discordsrv/common/command/game/command/DiscordSRVCommand.java @@ -23,10 +23,7 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.command.game.abstraction.GameCommand; import com.discordsrv.common.command.game.abstraction.GameCommandArguments; import com.discordsrv.common.command.game.abstraction.GameCommandExecutor; -import com.discordsrv.common.command.game.command.subcommand.DebugCommand; -import com.discordsrv.common.command.game.command.subcommand.LinkCommand; -import com.discordsrv.common.command.game.command.subcommand.ReloadCommand; -import com.discordsrv.common.command.game.command.subcommand.VersionCommand; +import com.discordsrv.common.command.game.command.subcommand.*; import com.discordsrv.common.command.game.sender.ICommandSender; import com.discordsrv.common.component.util.ComponentUtil; @@ -42,6 +39,7 @@ public class DiscordSRVCommand implements GameCommandExecutor { .then(DebugCommand.get(discordSRV)) .then(LinkCommand.get(discordSRV)) .then(ReloadCommand.get(discordSRV)) + .then(ResyncCommand.get(discordSRV)) .then(VersionCommand.get(discordSRV)); } return INSTANCE; diff --git a/common/src/main/java/com/discordsrv/common/command/game/command/subcommand/ResyncCommand.java b/common/src/main/java/com/discordsrv/common/command/game/command/subcommand/ResyncCommand.java new file mode 100644 index 00000000..9647fc6c --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/command/game/command/subcommand/ResyncCommand.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +package com.discordsrv.common.command.game.command.subcommand; + +import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.command.game.abstraction.GameCommand; +import com.discordsrv.common.command.game.abstraction.GameCommandArguments; +import com.discordsrv.common.command.game.abstraction.GameCommandExecutor; +import com.discordsrv.common.command.game.sender.ICommandSender; +import com.discordsrv.common.future.util.CompletableFutureUtil; +import com.discordsrv.common.groupsync.GroupSyncModule; +import com.discordsrv.common.groupsync.enums.GroupSyncCause; +import com.discordsrv.common.groupsync.enums.GroupSyncResult; +import com.discordsrv.common.player.IPlayer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class ResyncCommand implements GameCommandExecutor { + + private static GameCommand INSTANCE; + + public static GameCommand get(DiscordSRV discordSRV) { + if (INSTANCE == null) { + INSTANCE = GameCommand.literal("resync") + .requiredPermission("discordsrv.admin.resync") + .executor(new ResyncCommand(discordSRV)); + } + + return INSTANCE; + } + + private final DiscordSRV discordSRV; + + public ResyncCommand(DiscordSRV discordSRV) { + this.discordSRV = discordSRV; + } + + @Override + public void execute(ICommandSender sender, GameCommandArguments arguments) { + GroupSyncModule module = discordSRV.getModule(GroupSyncModule.class); + if (module == null) { + sender.sendMessage(Component.text("GroupSync module has not initialized correctly.", NamedTextColor.RED)); + return; + } + + sender.sendMessage(Component.text("Synchronizing online players", NamedTextColor.GRAY)); + long startTime = System.currentTimeMillis(); + + CompletableFutureUtil.combine(resyncOnlinePlayers(module)) + .whenComplete((results, t) -> { + EnumMap resultCounts = new EnumMap<>(GroupSyncResult.class); + int total = 0; + for (Set result : results) { + for (GroupSyncResult singleResult : result) { + total++; + resultCounts.computeIfAbsent(singleResult, key -> new AtomicInteger(0)).getAndIncrement(); + } + } + String resultHover; + if (total == 0) { + resultHover = "Nothing done"; + } else { + resultHover = total + " result" + (total == 1 ? "" : "s") + ":\n\n" + + resultCounts.entrySet().stream() + .map(entry -> entry.getKey().toString() + ": " + entry.getValue().get()) + .collect(Collectors.joining("\n")); + } + + long time = System.currentTimeMillis() - startTime; + sender.sendMessage( + Component.text("Synchronization completed in ", NamedTextColor.GRAY) + .append(Component.text(time + "ms", NamedTextColor.GREEN)) + .append(Component.text(" (", NamedTextColor.GRAY)) + .append(Component.text(total, NamedTextColor.GREEN)) + .append(Component.text(" result" + (total == 1 ? "" : "s") + ")", NamedTextColor.GRAY)) + .hoverEvent(HoverEvent.showText(Component.text(resultHover))) + ); + }); + } + + private List>> resyncOnlinePlayers(GroupSyncModule module) { + List>> futures = new ArrayList<>(); + for (IPlayer player : discordSRV.playerProvider().allPlayers()) { + futures.add(module.resync(player.uniqueId(), GroupSyncCause.COMMAND)); + } + return futures; + } +} 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 3f1e2c23..57ddf1b6 100644 --- a/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java +++ b/common/src/main/java/com/discordsrv/common/groupsync/GroupSyncModule.java @@ -23,6 +23,7 @@ import com.discordsrv.api.discord.api.entity.guild.DiscordRole; import com.discordsrv.api.discord.events.member.role.DiscordMemberRoleAddEvent; import com.discordsrv.api.discord.events.member.role.DiscordMemberRoleRemoveEvent; import com.discordsrv.api.event.bus.Subscribe; +import com.discordsrv.api.module.type.PermissionDataProvider; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.GroupSyncConfig; import com.discordsrv.common.debug.DebugGenerateEvent; @@ -34,8 +35,8 @@ import com.discordsrv.common.groupsync.enums.GroupSyncResult; import com.discordsrv.common.groupsync.enums.GroupSyncSide; import com.discordsrv.common.logging.NamedLogger; import com.discordsrv.common.module.type.AbstractModule; -import com.discordsrv.api.module.type.PermissionDataProvider; import com.discordsrv.common.player.IPlayer; +import com.discordsrv.common.player.event.PlayerConnectedEvent; import com.github.benmanes.caffeine.cache.Cache; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; @@ -237,29 +238,29 @@ public class GroupSyncModule extends AbstractModule { // Resync user - public void resync(UUID player, GroupSyncCause cause) { - lookupLinkedAccount(player).whenComplete((userId, t) -> { + public CompletableFuture> resync(UUID player, GroupSyncCause cause) { + return lookupLinkedAccount(player).thenCompose(userId -> { if (userId == null) { - return; + return CompletableFuture.completedFuture(Collections.emptySet()); } - resync(player, userId, cause); + return CompletableFutureUtil.combine(resync(player, userId, cause)); }); } - public void resync(long userId, GroupSyncCause cause) { - lookupLinkedAccount(userId).whenComplete((player, t) -> { + public CompletableFuture> resync(long userId, GroupSyncCause cause) { + return lookupLinkedAccount(userId).thenCompose(player -> { if (player == null) { - return; + return CompletableFuture.completedFuture(Collections.emptySet()); } - resync(player, userId, cause); + return CompletableFutureUtil.combine(resync(player, userId, cause)); }); } - public void resync(UUID player, long userId, GroupSyncCause cause) { + public Collection> resync(UUID player, long userId, GroupSyncCause cause) { if (noPermissionProvider() || (!discordSRV.playerProvider().player(player).isPresent() && !supportsOffline())) { - return; + return Collections.emptyList(); } Map> futures = new LinkedHashMap<>(); @@ -268,6 +269,7 @@ public class GroupSyncModule extends AbstractModule { } logSummary(player, cause, futures); + return futures.values(); } private void resyncPair(GroupSyncConfig.PairConfig pair, GroupSyncCause cause) { @@ -378,6 +380,11 @@ public class GroupSyncModule extends AbstractModule { // Listeners & methods to indicate something changed + @Subscribe + public void onPlayerConnected(PlayerConnectedEvent event) { + resync(event.player().uniqueId(), GroupSyncCause.GAME_JOIN); + } + @Subscribe public void onDiscordMemberRoleAdd(DiscordMemberRoleAddEvent event) { event.getRoles().forEach(role -> roleChanged(event.getMember().getId(), role.getId(), false)); diff --git a/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java b/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java index 1b3dba75..4cfc74b9 100644 --- a/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java +++ b/common/src/main/java/com/discordsrv/common/groupsync/SynchronizationSummary.java @@ -48,7 +48,7 @@ public class SynchronizationSummary { public String toString() { int count = pairs.size(); StringBuilder message = new StringBuilder( - "Group synchronization (of " + count + " pairs) for " + player + " (" + cause + ")"); + "Group synchronization (of " + count + " pair" + (count == 1 ? "" : "s") + ") for " + player + " (" + cause + ")"); for (Map.Entry> entry : pairs.entrySet()) { message.append(count == 1 ? ": " : "\n")