1
mirror of https://github.com/CarmJos/EasyPlugin.git synced 2026-06-04 08:38:17 +08:00

feat(users): Seperated users registry interface for api usage.

This commit is contained in:
2025-05-17 03:44:07 +08:00
parent bc3dd32f85
commit 54939f0f14
21 changed files with 219 additions and 97 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>easyplugin-parent</artifactId>
<version>1.5.12</version>
<version>1.5.13</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
@@ -0,0 +1,47 @@
package cc.carm.lib.easyplugin.user;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public abstract class AbstractUserData<K> implements UserData<K> {
protected final @NotNull K key;
/**
* Used to mark the data as dropping (save and unload) status.
*/
protected boolean dropping = false;
protected AbstractUserData(@NotNull K key) {
this.key = key;
}
/**
* @param dropping true if the data is dropping, false otherwise
*/
public void setDropping(boolean dropping) {
this.dropping = dropping;
}
/**
* @return true if the data is dropping, false otherwise
*/
public boolean isDropping() {
return dropping;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractUserData<?> abstractUserData = (AbstractUserData<?>) o;
return key.equals(abstractUserData.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
}
@@ -2,31 +2,22 @@ package cc.carm.lib.easyplugin.user;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public interface UserData<K> {
public abstract class UserData<K> {
@NotNull K key();
protected final @NotNull K key;
protected UserData(@NotNull K key) {
this.key = key;
@Deprecated
default @NotNull K getKey() {
return key();
}
public @NotNull K getKey() {
return key;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserData<?> userData = (UserData<?>) o;
return key.equals(userData.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
/**
* @param dropping true if the data is dropping, false otherwise
*/
void setDropping(boolean dropping);
/**
* @return true if the data is dropping, false otherwise
*/
boolean isDropping();
}
@@ -1,15 +1,12 @@
package cc.carm.lib.easyplugin.user;
import cc.carm.lib.easyplugin.EasyPlugin;
import com.google.common.collect.ImmutableSet;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
@@ -21,7 +18,7 @@ import java.util.function.Supplier;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public abstract class UserDataManager<K, U extends UserData<K>> {
public abstract class UserDataManager<K, U extends AbstractUserData<K>> implements UserDataRegistry<K, U> {
protected final @NotNull EasyPlugin plugin;
@@ -47,6 +44,7 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
this.dataCache = cacheMap;
}
@Override
public void shutdown() {
this.executor.shutdown();
}
@@ -55,14 +53,11 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
return plugin;
}
protected @NotNull Logger getLogger() {
@Override
public @NotNull Logger getLogger() {
return getPlugin().getLogger();
}
public String serializeKey(@NotNull K key) {
return key.toString();
}
public abstract @NotNull U emptyUser(@NotNull K key);
public @NotNull U errorUser(@NotNull K key) {
@@ -73,36 +68,16 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
protected abstract void saveData(@NotNull U data) throws Exception;
public @NotNull CompletableFuture<U> load(@NotNull K key) {
return load(key, false);
}
public @NotNull CompletableFuture<U> load(@NotNull K key, boolean cache) {
return load(key, () -> cache);
}
public @NotNull Map<K, U> getDataCache() {
return dataCache;
}
@Unmodifiable
public @NotNull Set<U> list() {
return ImmutableSet.copyOf(getDataCache().values());
}
public @NotNull U get(@NotNull K key) {
return Optional.ofNullable(getNullable(key)).orElseThrow(() -> new NullPointerException("User " + key + " not found."));
}
public @Nullable U getNullable(@NotNull K key) {
return getDataCache().get(key);
}
public @NotNull Optional<@Nullable U> getOptional(@NotNull K key) {
return Optional.ofNullable(getNullable(key));
}
@Override
public @NotNull CompletableFuture<U> load(@NotNull K key, @NotNull Supplier<Boolean> cacheCondition) {
U cached = getNullable(key);
if (cached != null) {
return CompletableFuture.supplyAsync(() -> cached); // Return cached data async.
}
return CompletableFuture.supplyAsync(() -> {
String identifier = serializeKey(key);
@@ -124,11 +99,12 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
}
}, executor).thenApply((data) -> {
if (cacheCondition.get()) dataCache.put(key, data);
if (cacheCondition.get() && !data.isDropping()) dataCache.put(key, data);
return data;
});
}
@Override
public @NotNull CompletableFuture<Boolean> save(@NotNull U user) {
return CompletableFuture.supplyAsync(() -> {
String identifier = serializeKey(user.getKey());
@@ -148,17 +124,18 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
}, executor);
}
public @NotNull CompletableFuture<Boolean> unload(@NotNull K key) {
return unload(key, true);
}
@Override
public @NotNull CompletableFuture<Boolean> unload(@NotNull K key, boolean save) {
U data = getNullable(key);
if (data == null) return CompletableFuture.completedFuture(false);
data.setDropping(true); // Mark the data as unloading.
if (save) {
return save(data).thenApply(result -> {
this.dataCache.remove(key);
// Check if the data is still unloading,
// which cloud be interrupted by the next load.
if (data.isDropping()) {
this.dataCache.remove(key);
}
return result;
});
} else {
@@ -168,6 +145,7 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
}
@Override
public @NotNull CompletableFuture<Boolean> modify(@NotNull K key, @NotNull Consumer<U> consumer) {
U cached = getNullable(key);
if (cached != null) {
@@ -176,13 +154,14 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
return true;
}, executor);
} else {
return load(key, false).thenApply((data) -> {
return load(key, true).thenApply((data) -> {
consumer.accept(data);
return data;
}).thenCompose(this::save);
}).thenCompose(data -> unload(key, true));
}
}
@Override
public <V> @NotNull CompletableFuture<V> peek(@NotNull K key, @NotNull Function<U, V> function) {
U cached = getNullable(key);
if (cached != null) {
@@ -192,10 +171,6 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
}
}
public @NotNull CompletableFuture<Map<K, U>> loadOnline(@NotNull Function<Player, ? extends K> function) {
return loadGroup(Bukkit.getOnlinePlayers(), function, OfflinePlayer::isOnline);
}
public <T> @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<? extends T> users,
@NotNull Function<? super T, ? extends K> function,
@NotNull Predicate<T> cacheCondition) {
@@ -218,15 +193,7 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
return task.thenApply(Collections::unmodifiableMap);
}
public @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<K> allKeys,
@NotNull Predicate<K> cacheCondition) {
return loadGroup(allKeys, Function.identity(), cacheCondition);
}
public @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<K> allKeys) {
return loadGroup(allKeys, (v) -> false);
}
@Override
public void saveAll() {
if (getDataCache().isEmpty()) return;
for (U u : getDataCache().values()) {
@@ -239,6 +206,7 @@ public abstract class UserDataManager<K, U extends UserData<K>> {
}
}
@Override
public int unloadAll(boolean save) {
if (save) saveAll();
int size = getDataCache().size();
@@ -0,0 +1,95 @@
package cc.carm.lib.easyplugin.user;
import com.google.common.collect.ImmutableSet;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.logging.Logger;
public interface UserDataRegistry<K, U extends UserData<K>> {
void shutdown();
@NotNull Logger getLogger();
@NotNull Map<K, U> cache();
default String serializeKey(@NotNull K key) {
return key.toString();
}
default @NotNull CompletableFuture<U> load(@NotNull K key) {
return load(key, false);
}
default @NotNull CompletableFuture<U> load(@NotNull K key, boolean cache) {
return load(key, () -> cache);
}
@Unmodifiable
default @NotNull Set<U> list() {
return ImmutableSet.copyOf(cache().values());
}
default @NotNull U get(@NotNull K key) {
return Optional.ofNullable(getNullable(key)).orElseThrow(() -> new NullPointerException("User " + key + " not found."));
}
default @Nullable U getNullable(@NotNull K key) {
return cache().get(key);
}
default @NotNull Optional<@Nullable U> getOptional(@NotNull K key) {
return Optional.ofNullable(getNullable(key));
}
@NotNull CompletableFuture<U> load(@NotNull K key, @NotNull Supplier<Boolean> cacheCondition);
@NotNull CompletableFuture<Boolean> save(@NotNull U user);
default @NotNull CompletableFuture<Boolean> unload(@NotNull K key) {
return unload(key, true);
}
@NotNull CompletableFuture<Boolean> unload(@NotNull K key, boolean save);
@NotNull CompletableFuture<Boolean> modify(@NotNull K key, @NotNull Consumer<U> consumer);
<V> @NotNull CompletableFuture<V> peek(@NotNull K key, @NotNull Function<U, V> function);
default @NotNull CompletableFuture<Map<K, U>> loadOnline(@NotNull Function<Player, ? extends K> function) {
return loadGroup(Bukkit.getOnlinePlayers(), function, OfflinePlayer::isOnline);
}
<T> @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<? extends T> users,
@NotNull Function<? super T, ? extends K> function,
@NotNull Predicate<T> cacheCondition);
default @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<K> allKeys,
@NotNull Predicate<K> cacheCondition) {
return loadGroup(allKeys, Function.identity(), cacheCondition);
}
default @NotNull CompletableFuture<Map<K, U>> loadGroup(@NotNull Collection<K> allKeys) {
return loadGroup(allKeys, (v) -> false);
}
void saveAll();
int unloadAll(boolean save);
}