Beginning of profiles & linked accounts

This commit is contained in:
Vankka 2022-02-01 11:33:50 +02:00
parent 88ef85c65e
commit ba1a8c196f
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
15 changed files with 639 additions and 47 deletions

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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
)

View File

@ -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));

View File

@ -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();

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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()
);
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}