mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2025-01-15 20:31:43 +01:00
Beginning of profiles & linked accounts
This commit is contained in:
parent
88ef85c65e
commit
ba1a8c196f
@ -27,10 +27,10 @@ import com.discordsrv.api.component.MinecraftComponentFactory;
|
||||
import com.discordsrv.api.discord.api.DiscordAPI;
|
||||
import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.event.bus.EventBus;
|
||||
import com.discordsrv.api.linking.LinkingBackend;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.player.DiscordSRVPlayer;
|
||||
import com.discordsrv.api.player.IPlayerProvider;
|
||||
import com.discordsrv.api.profile.IProfileManager;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -59,7 +59,11 @@ public interface DiscordSRVApi {
|
||||
@NotNull
|
||||
EventBus eventBus();
|
||||
|
||||
LinkingBackend linkingBackend();
|
||||
/**
|
||||
* The profile manager, access the profiles of players and/or users.
|
||||
* @return the instance of {@link IProfileManager}
|
||||
*/
|
||||
IProfileManager profileManager();
|
||||
|
||||
/**
|
||||
* DiscordSRV's own placeholder service.
|
||||
|
@ -21,19 +21,22 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.linking;
|
||||
package com.discordsrv.api.profile;
|
||||
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface LinkingBackend extends Module {
|
||||
public interface IProfile {
|
||||
|
||||
@Nullable
|
||||
UUID getLinkedAccount(long userId);
|
||||
UUID uniqueId();
|
||||
|
||||
@Nullable
|
||||
Long getLinkedAccount(@NotNull UUID player);
|
||||
Long userId();
|
||||
|
||||
default boolean isLinked() {
|
||||
return uniqueId() != null && userId() != null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2022 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.profile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface IProfileManager {
|
||||
|
||||
CompletableFuture<Optional<IProfile>> lookupProfile(UUID playerUUID);
|
||||
|
||||
Optional<IProfile> getProfile(UUID playerUUID);
|
||||
|
||||
CompletableFuture<Optional<IProfile>> lookupProfile(long userId);
|
||||
|
||||
Optional<IProfile> getProfile(long userId);
|
||||
}
|
@ -1,11 +1,19 @@
|
||||
import dev.vankka.dependencydownload.task.GenerateDependencyDownloadResourceTask
|
||||
|
||||
configurations {
|
||||
hikari
|
||||
h2Driver
|
||||
mysqlDriver
|
||||
compileOnly.extendsFrom hikari
|
||||
testRuntimeOnly.extendsFrom runtimeDownloadOnly
|
||||
}
|
||||
|
||||
task generateResourceForHikari(type: GenerateDependencyDownloadResourceTask) {
|
||||
var conf = configurations.hikari
|
||||
configuration = conf
|
||||
file = 'dependencies/' + conf.name + '.txt'
|
||||
relocate 'com.zaxxer.hikari', 'com.discordsrv.dependencies.com.zaxxer.hikari'
|
||||
}
|
||||
task generateResourceForH2Driver(type: GenerateDependencyDownloadResourceTask) {
|
||||
var conf = configurations.h2Driver
|
||||
configuration = conf
|
||||
@ -55,8 +63,9 @@ dependencies {
|
||||
runtimeDownloadApi 'dev.vankka:mcdiscordreserializer:4.2.4-SNAPSHOT'
|
||||
runtimeDownloadApi 'dev.vankka:enhancedlegacytext:1.0.0-SNAPSHOT'
|
||||
|
||||
// Database Drivers
|
||||
h2Driver 'com.h2database:h2:2.0.204'
|
||||
// Database
|
||||
hikari('com.zaxxer:HikariCP:4.0.3') { exclude group: 'org.slf4j' }
|
||||
h2Driver 'com.h2database:h2:2.1.210'
|
||||
mysqlDriver 'mysql:mysql-connector-java:8.0.25'
|
||||
|
||||
// Integrations
|
||||
@ -66,6 +75,7 @@ dependencies {
|
||||
processResources {
|
||||
dependsOn(
|
||||
generateRuntimeDownloadResourceForRuntimeDownloadOnly,
|
||||
generateResourceForHikari,
|
||||
generateResourceForH2Driver,
|
||||
generateResourceForMySQLDriver
|
||||
)
|
||||
|
@ -22,8 +22,8 @@ import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.event.bus.EventBus;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
import com.discordsrv.api.linking.LinkingBackend;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.api.profile.IProfileManager;
|
||||
import com.discordsrv.common.api.util.ApiInstanceUtil;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.channel.ChannelUpdaterModule;
|
||||
@ -43,6 +43,9 @@ import com.discordsrv.common.function.CheckedFunction;
|
||||
import com.discordsrv.common.function.CheckedRunnable;
|
||||
import com.discordsrv.common.groupsync.GroupSyncModule;
|
||||
import com.discordsrv.common.integration.LuckPermsIntegration;
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.linking.LinkStore;
|
||||
import com.discordsrv.common.linking.impl.MemoryLinker;
|
||||
import com.discordsrv.common.logging.adapter.DependencyLoggerAdapter;
|
||||
import com.discordsrv.common.logging.impl.DependencyLoggingHandler;
|
||||
import com.discordsrv.common.logging.impl.DiscordSRVLogger;
|
||||
@ -55,12 +58,15 @@ import com.discordsrv.common.module.type.AbstractModule;
|
||||
import com.discordsrv.common.placeholder.ComponentResultStringifier;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.placeholder.context.GlobalTextHandlingContext;
|
||||
import com.discordsrv.common.profile.ProfileManager;
|
||||
import com.discordsrv.common.storage.Storage;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@ -78,6 +84,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
|
||||
// DiscordSRVApi
|
||||
private EventBus eventBus;
|
||||
private ProfileManager profileManager;
|
||||
private PlaceholderServiceImpl placeholderService;
|
||||
private ComponentFactory componentFactory;
|
||||
private DiscordAPIImpl discordAPI;
|
||||
@ -86,6 +93,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
// DiscordSRV
|
||||
private DiscordSRVLogger logger;
|
||||
private ModuleManager moduleManager;
|
||||
|
||||
private Storage storage;
|
||||
private LinkProvider linkProvider;
|
||||
private ChannelConfigHelper channelConfig;
|
||||
private DiscordConnectionManager discordConnectionManager;
|
||||
|
||||
@ -103,6 +113,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
this.logger = new DiscordSRVLogger(this);
|
||||
this.eventBus = new EventBusImpl(this);
|
||||
this.moduleManager = new ModuleManager(this);
|
||||
this.profileManager = new ProfileManager(this);
|
||||
this.placeholderService = new PlaceholderServiceImpl(this);
|
||||
this.componentFactory = new ComponentFactory(this);
|
||||
this.discordAPI = new DiscordAPIImpl(this);
|
||||
@ -122,8 +133,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkingBackend linkingBackend() {
|
||||
return getModule(LinkingBackend.class);
|
||||
public IProfileManager profileManager() {
|
||||
return profileManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,6 +170,16 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage storage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkProvider linkProvider() {
|
||||
return linkProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfigHelper channelConfig() {
|
||||
return channelConfig;
|
||||
@ -315,6 +336,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
discordConnectionManager = new JDAConnectionManager(this);
|
||||
discordConnectionManager.connect().join();
|
||||
|
||||
linkProvider = new MemoryLinker();
|
||||
((LinkStore) linkProvider).link(UUID.fromString("6c983d46-0631-48b8-9baf-5e33eb5ffec4"), 185828288466255874L);
|
||||
|
||||
// Placeholder result stringifiers & global contexts
|
||||
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
||||
placeholderService().addGlobalContext(new GlobalTextHandlingContext(this));
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.component.ComponentFactory;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
@ -29,14 +30,15 @@ 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.linking.LinkProvider;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.impl.DiscordSRVLogger;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.player.provider.AbstractPlayerProvider;
|
||||
import com.discordsrv.common.plugin.PluginManager;
|
||||
import com.discordsrv.common.scheduler.Scheduler;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.storage.Storage;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -78,6 +80,12 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
// Logger
|
||||
DiscordSRVLogger logger();
|
||||
|
||||
// Storage
|
||||
Storage storage();
|
||||
|
||||
// Link Provider
|
||||
LinkProvider linkProvider();
|
||||
|
||||
// Config
|
||||
ConnectionConfigManager<? extends ConnectionConfig> connectionConfigManager();
|
||||
ConnectionConfig connectionConfig();
|
||||
|
@ -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.profile.IProfile;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.GroupSyncConfig;
|
||||
import com.discordsrv.common.debug.DebugGenerateEvent;
|
||||
@ -163,12 +164,14 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
// Linked account helper methods
|
||||
|
||||
private Long getLinkedAccount(UUID player) {
|
||||
return discordSRV.linkingBackend().getLinkedAccount(player);
|
||||
private CompletableFuture<Long> lookupLinkedAccount(UUID player) {
|
||||
return discordSRV.profileManager().lookupProfile(player)
|
||||
.thenApply(profile -> profile.map(IProfile::userId).orElse(null));
|
||||
}
|
||||
|
||||
private UUID getLinkedAccount(long userId) {
|
||||
return discordSRV.linkingBackend().getLinkedAccount(userId);
|
||||
private CompletableFuture<UUID> lookupLinkedAccount(long userId) {
|
||||
return discordSRV.profileManager().lookupProfile(userId)
|
||||
.thenApply(profile -> profile.map(IProfile::uniqueId).orElse(null));
|
||||
}
|
||||
|
||||
// Permission data helper methods
|
||||
@ -232,21 +235,23 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
// Resync user
|
||||
|
||||
public void resync(UUID player, GroupSyncCause cause) {
|
||||
Long userId = getLinkedAccount(player);
|
||||
lookupLinkedAccount(player).whenComplete((userId, t) -> {
|
||||
if (userId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
resync(player, userId, cause);
|
||||
});
|
||||
}
|
||||
|
||||
public void resync(long userId, GroupSyncCause cause) {
|
||||
UUID player = getLinkedAccount(userId);
|
||||
lookupLinkedAccount(userId).whenComplete((player, t) -> {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
resync(player, userId, cause);
|
||||
});
|
||||
}
|
||||
|
||||
public void resync(UUID player, long userId, GroupSyncCause cause) {
|
||||
@ -269,14 +274,15 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
for (IPlayer player : discordSRV.playerProvider().allPlayers()) {
|
||||
UUID uuid = player.uniqueId();
|
||||
Long userId = getLinkedAccount(uuid);
|
||||
lookupLinkedAccount(uuid).whenComplete((userId, t) -> {
|
||||
if (userId == null) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
resyncPair(pair, uuid, userId).whenComplete((result, t) -> logger().debug(
|
||||
resyncPair(pair, uuid, userId).whenComplete((result, t2) -> logger().debug(
|
||||
new SynchronizationSummary(uuid, cause, pair, result).toString()
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,6 +415,16 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
return;
|
||||
}
|
||||
|
||||
lookupLinkedAccount(userId).whenComplete((player, t) -> {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
roleChanged(userId, player, roleId, remove);
|
||||
});
|
||||
}
|
||||
|
||||
private void roleChanged(long userId, UUID player, long roleId, boolean remove) {
|
||||
List<GroupSyncConfig.PairConfig> pairs = rolesToPairs.get(roleId);
|
||||
if (pairs == null) {
|
||||
return;
|
||||
@ -420,11 +436,6 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID player = getLinkedAccount(userId);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<GroupSyncConfig.PairConfig, CompletableFuture<GroupSyncResult>> futures = new LinkedHashMap<>();
|
||||
for (GroupSyncConfig.PairConfig pair : pairs) {
|
||||
GroupSyncDirection direction = pair.direction();
|
||||
@ -457,7 +468,13 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
logSummary(player, GroupSyncCause.DISCORD_ROLE_CHANGE, futures);
|
||||
}
|
||||
|
||||
private void groupChanged(UUID player, String groupName, @Nullable Set<String> serverContext, GroupSyncCause cause, boolean remove) {
|
||||
private void groupChanged(
|
||||
UUID player,
|
||||
String groupName,
|
||||
@Nullable Set<String> serverContext,
|
||||
GroupSyncCause cause,
|
||||
boolean remove
|
||||
) {
|
||||
if (noPermissionProvider()) {
|
||||
return;
|
||||
}
|
||||
@ -466,13 +483,25 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
return;
|
||||
}
|
||||
|
||||
List<GroupSyncConfig.PairConfig> pairs = groupsToPairs.get(groupName);
|
||||
if (pairs == null) {
|
||||
lookupLinkedAccount(player).whenComplete((userId, t) -> {
|
||||
if (userId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long userId = getLinkedAccount(player);
|
||||
if (userId == null) {
|
||||
groupChanged(player, userId, groupName, serverContext, cause, remove);
|
||||
});
|
||||
}
|
||||
|
||||
private void groupChanged(
|
||||
UUID player,
|
||||
long userId,
|
||||
String groupName,
|
||||
@Nullable Set<String> serverContext,
|
||||
GroupSyncCause cause,
|
||||
boolean remove
|
||||
) {
|
||||
List<GroupSyncConfig.PairConfig> pairs = groupsToPairs.get(groupName);
|
||||
if (pairs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.linking;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface LinkProvider {
|
||||
|
||||
CompletableFuture<Optional<Long>> queryUserId(@NotNull UUID playerUUID);
|
||||
|
||||
default CompletableFuture<Optional<Long>> getUserId(@NotNull UUID playerUUID) {
|
||||
Optional<Long> userId = getCachedUserId(playerUUID);
|
||||
if (userId.isPresent()) {
|
||||
return CompletableFuture.completedFuture(userId);
|
||||
}
|
||||
return queryUserId(playerUUID);
|
||||
}
|
||||
|
||||
default Optional<Long> getCachedUserId(@NotNull UUID playerUUID) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
CompletableFuture<Optional<UUID>> queryPlayerUUID(long userId);
|
||||
|
||||
default CompletableFuture<Optional<UUID>> getPlayerUUID(long userId) {
|
||||
Optional<UUID> playerUUID = getCachedPlayerUUID(userId);
|
||||
if (playerUUID.isPresent()) {
|
||||
return CompletableFuture.completedFuture(playerUUID);
|
||||
}
|
||||
return queryPlayerUUID(userId);
|
||||
}
|
||||
|
||||
default Optional<UUID> getCachedPlayerUUID(long userId) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.linking;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface LinkStore {
|
||||
|
||||
CompletableFuture<Void> link(@NotNull UUID playerUUID, long userId);
|
||||
|
||||
CompletableFuture<Integer> getLinkedAccountCount();
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.linking.impl;
|
||||
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.player.event.PlayerConnectedEvent;
|
||||
import com.github.benmanes.caffeine.cache.*;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public abstract class CachedLinkProvider implements LinkProvider {
|
||||
|
||||
private static final long UNLINKED_USER = -1L;
|
||||
private static final UUID UNLINKED_UUID = new UUID(0, 0);
|
||||
|
||||
protected final DiscordSRV discordSRV;
|
||||
private final Cache<Long, UUID> userToPlayer;
|
||||
private final AsyncLoadingCache<UUID, Long> playerToUser;
|
||||
|
||||
public CachedLinkProvider(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.userToPlayer = discordSRV.caffeineBuilder().build();
|
||||
this.playerToUser = discordSRV.caffeineBuilder()
|
||||
.refreshAfterWrite(30, TimeUnit.SECONDS)
|
||||
.removalListener((RemovalListener<UUID, Long>) (key, value, cause) -> {
|
||||
if (value != null) {
|
||||
userToPlayer.invalidate(value);
|
||||
}
|
||||
})
|
||||
.buildAsync(new AsyncCacheLoader<UUID, Long>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Long> asyncLoad(@NonNull UUID key, @NonNull Executor executor) {
|
||||
return queryUserId(key).thenApply(opt -> opt.orElse(UNLINKED_USER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Long> asyncReload(
|
||||
@NonNull UUID key,
|
||||
@NonNull Long oldValue,
|
||||
@NonNull Executor executor
|
||||
) {
|
||||
if (!discordSRV.playerProvider().player(key).isPresent()) {
|
||||
// Don't keep players that aren't online in cache
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
return asyncLoad(key, executor);
|
||||
}
|
||||
});
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Long>> getUserId(@NotNull UUID playerUUID) {
|
||||
return playerToUser.get(playerUUID).thenApply(value -> {
|
||||
if (value == UNLINKED_USER) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Long> getCachedUserId(@NotNull UUID player) {
|
||||
Long value = playerToUser.synchronous().getIfPresent(player);
|
||||
return Optional.ofNullable(value == null || value == UNLINKED_USER ? null : value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<UUID>> getPlayerUUID(long userId) {
|
||||
UUID player = userToPlayer.getIfPresent(userId);
|
||||
if (player != null) {
|
||||
return CompletableFuture.completedFuture(player == UNLINKED_UUID ? Optional.empty() : Optional.of(player));
|
||||
}
|
||||
|
||||
return queryPlayerUUID(userId).thenApply(optional -> {
|
||||
if (!optional.isPresent()) {
|
||||
userToPlayer.put(userId, UNLINKED_UUID);
|
||||
return optional;
|
||||
}
|
||||
|
||||
UUID uuid = optional.get();
|
||||
userToPlayer.put(userId, uuid);
|
||||
return optional;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UUID> getCachedPlayerUUID(long discordId) {
|
||||
UUID value = userToPlayer.getIfPresent(discordId);
|
||||
return Optional.ofNullable(value == null || value == UNLINKED_UUID ? null : value);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerConnected(PlayerConnectedEvent event) {
|
||||
// Cache logged in players
|
||||
playerToUser.get(event.player().uniqueId());
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.linking.impl;
|
||||
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.linking.LinkStore;
|
||||
import org.apache.commons.collections4.BidiMap;
|
||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MemoryLinker implements LinkProvider, LinkStore {
|
||||
|
||||
private final BidiMap<UUID, Long> map = new DualHashBidiMap<>();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Long>> queryUserId(@NotNull UUID playerUUID) {
|
||||
return CompletableFuture.completedFuture(Optional.ofNullable(map.get(playerUUID)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<UUID>> queryPlayerUUID(long userId) {
|
||||
return CompletableFuture.completedFuture(Optional.ofNullable(map.getKey(userId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> link(@NotNull UUID playerUUID, long userId) {
|
||||
map.put(playerUUID, userId);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getLinkedAccountCount() {
|
||||
return CompletableFuture.completedFuture(map.size());
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.linking.impl;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.linking.LinkStore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class StorageLinker extends CachedLinkProvider implements LinkProvider, LinkStore {
|
||||
|
||||
public StorageLinker(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Long>> queryUserId(@NotNull UUID playerUUID) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Long value = discordSRV.storage().getUserId(playerUUID);
|
||||
return Optional.ofNullable(value);
|
||||
}, discordSRV.scheduler().executor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<UUID>> queryPlayerUUID(long userId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
UUID value = discordSRV.storage().getPlayerUUID(userId);
|
||||
return Optional.ofNullable(value);
|
||||
}, discordSRV.scheduler().executor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> link(@NotNull UUID playerUUID, long userId) {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> discordSRV.storage().link(playerUUID, userId),
|
||||
discordSRV.scheduler().executor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getLinkedAccountCount() {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> discordSRV.storage().getLinkedAccountCount(),
|
||||
discordSRV.scheduler().executor()
|
||||
);
|
||||
}
|
||||
}
|
@ -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.profile;
|
||||
|
||||
import com.discordsrv.api.profile.IProfile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Profile implements IProfile {
|
||||
|
||||
private final UUID uuid;
|
||||
private final Long userId;
|
||||
|
||||
public Profile(UUID uuid, Long userId) {
|
||||
this.uuid = uuid;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID uniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Long userId() {
|
||||
return userId;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.profile;
|
||||
|
||||
import com.discordsrv.api.profile.IProfileManager;
|
||||
import com.discordsrv.api.profile.IProfile;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ProfileManager implements IProfileManager {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public ProfileManager(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<IProfile>> lookupProfile(UUID playerUUID) {
|
||||
return discordSRV.linkProvider().getUserId(playerUUID)
|
||||
.thenApply(opt -> Optional.of(new Profile(playerUUID, opt.orElse(null))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IProfile> getProfile(UUID playerUUID) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<IProfile>> lookupProfile(long userId) {
|
||||
return discordSRV.linkProvider().getPlayerUUID(userId)
|
||||
.thenApply(opt -> Optional.of(new Profile(opt.orElse(null), userId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IProfile> getProfile(long userId) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.storage;
|
||||
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Blocking
|
||||
public interface Storage {
|
||||
|
||||
@Nullable
|
||||
Long getUserId(@NotNull UUID player);
|
||||
|
||||
@Nullable
|
||||
UUID getPlayerUUID(long userId);
|
||||
|
||||
void link(@NotNull UUID player, long userId);
|
||||
|
||||
int getLinkedAccountCount();
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user