currently refactoring a lot

This commit is contained in:
kento2 2026-07-04 16:29:05 +02:00
parent a32805a623
commit 700b5d5d10
20 changed files with 406 additions and 292 deletions

93
.idea/workspace.xml generated
View file

@ -5,52 +5,26 @@
</component>
<component name="ChangeListManager">
<list default="true" id="56c685a1-f609-4ffd-a951-849beb2a7f8d" name="Changes" comment="currently refactoring a lot">
<change afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Task.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/TaskImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Tasks.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsCommand.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsGetLiteral.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsSetLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/de/kentoj/scrow/DatabaseConvention.kt" beforeDir="false" afterPath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/de/kentoj/scrow/DatabaseConvention.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/TaskState.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/EconomyService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/misc/EconomyService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/PlayerManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/misc/PlayerManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendRequestService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendRequestService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendshipService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendshipService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownDeployServerLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownDeployServerLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownDestroyServerLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownDestroyServerLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownListServersLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/crown/CrownListServersLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/CoinsCommand.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/InMemoryEconomyService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/InMemoryEconomyService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/MongoEconomyService.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/PostgresEconomyService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/PostgresEconomyService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Task.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Task.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/TaskImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/TaskImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Tasks.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/scheduler/Tasks.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsGetLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsGetLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsSetLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/economy/command/CoinsSetLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendAddLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendAddLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRemoveLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRemoveLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRequestsLiteral.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRequestsLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/InMemoryFriendshipService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/InMemoryFriendshipService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/MongoFriendshipService.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/PostgresFriendshipService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/PostgresFriendshipService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/InMemoryFriendRequestService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/InMemoryFriendRequestService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestService.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/PostgresFriendRequestService.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/PostgresFriendRequestService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/playermanager/PlayerManagerImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/playermanager/PlayerManagerImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/server/LobbyCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/server/LobbyCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/server/PlayCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/server/PlayCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-lib/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/core-lib/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-lib/src/main/java/de/kentoj/scrowlib/servermanager/InstanceManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-lib/src/main/java/de/kentoj/scrowlib/servermanager/InstanceManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-lib/src/main/java/de/kentoj/scrowlib/servermanager/InstanceManagerImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-lib/src/main/java/de/kentoj/scrowlib/servermanager/InstanceManagerImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-velocity-api/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/core-velocity-api/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/command/OnlineCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/command/OnlineCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/MsgCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/MsgCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/ReplyCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/privmsg/ReplyCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/server/ServerRegisterWatchdog.java" beforeDir="false" afterPath="$PROJECT_DIR$/core-velocity-impl/src/main/java/de/kentoj/scrow/corevelocity/server/ServerRegisterWatchdog.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -149,6 +123,9 @@
<component name="HighlightingSettingsPerFile">
<setting file="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.projectreactor/reactor-core/3.8.5/ed6c3f423d84bf282e50a4f7432a8d65af321efb/reactor-core-3.8.5-sources.jar!/reactor/core/publisher/Mono.java" root0="SKIP_INSPECTION" />
</component>
<component name="ProblemsViewState">
<option name="autoscrollToSource" value="true" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 0
@ -161,31 +138,31 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Gradle.Download Sources.executor&quot;: &quot;Run&quot;,
&quot;Gradle.core [build].executor&quot;: &quot;Debug&quot;,
&quot;Gradle.core [clean].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core [publishAllPublicationsToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core [publishCoreLibPublicationToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core:core-bukkit-api [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core:core-bukkit-api [clean].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core:core-lib [publishAllPublicationsToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core:core-lib [publishCoreLibPublicationToMavenLocal].executor&quot;: &quot;Run&quot;,
&quot;Gradle.core:core-lib [publishCoreLibPublicationToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/a/src/scrow&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.15&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.6268199&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Gradle.Download Sources.executor": "Run",
"Gradle.core [build].executor": "Run",
"Gradle.core [clean].executor": "Run",
"Gradle.core [publishAllPublicationsToScrowRepository].executor": "Run",
"Gradle.core [publishCoreLibPublicationToScrowRepository].executor": "Run",
"Gradle.core:core-bukkit-api [build].executor": "Run",
"Gradle.core:core-bukkit-api [clean].executor": "Run",
"Gradle.core:core-lib [publishAllPublicationsToScrowRepository].executor": "Run",
"Gradle.core:core-lib [publishCoreLibPublicationToMavenLocal].executor": "Run",
"Gradle.core:core-lib [publishCoreLibPublicationToScrowRepository].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "/home/a/src/scrow",
"project.structure.last.edited": "Project",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.6268199",
"settings.editor.selected.configurable": "preferences.pluginManager"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="CreateClassDialog.RecentsKey">
<recent name="de.kentoj.scrow.bukkit.economy.command" />

View file

@ -1,13 +1,15 @@
package de.kentoj.scrow.bukkit.scheduler;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public interface Task<T> {
CompletableFuture<T> asFuture();
CompletableFuture<T> toFuture();
default Task<?> runSync(Runnable task) {
return mapSync(__ -> {
@ -48,4 +50,17 @@ public interface Task<T> {
}
<S> Task<S> mapAsync(Function<T, S> mapper);
Task<T> yieldIf(Predicate<T> precondition);
default T await() {
try {
return toFuture().get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,8 +1,10 @@
package de.kentoj.scrow.bukkit.scheduler;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Bukkit;
import java.util.concurrent.CompletableFuture;
@ -10,47 +12,85 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.*;
/**
* A Task runs entirely async, when sync is used, a subtask is run on the main thread and the async thread
* blocks until the subtask returns.
*
* @param <T> type of computed result
*/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class TaskImpl<T> implements Task<T> {
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final Supplier<T> compute;
private final Supplier<TaskState<T>> compute;
private TaskState<T> cachedResult = null;
@Override
public CompletableFuture<T> asFuture() {
return CompletableFuture.supplyAsync(compute, EXECUTOR);
public CompletableFuture<T> toFuture() {
return CompletableFuture.supplyAsync(() -> state().getValue(), EXECUTOR);
}
/**
* Maps the last result on the main thread
*/
public <S> Task<S> mapSync(Function<T, S> mapper) {
Supplier<S> supplier = () -> {
return new TaskImpl<>(() -> {
var s = state();
if (s.isYielded()) return yielded(s);
try {
return Bukkit.getScheduler().callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(compute.get())).get();
return TaskState.completed(Bukkit.getScheduler()
.callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(s.getValue()))
.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
};
return new TaskImpl<>(supplier);
});
}
/**
* Maps the last result async
*/
public <S> Task<S> mapAsync(Function<T, S> mapper) {
return new TaskImpl<>(() -> mapper.apply(compute.get()));
return new TaskImpl<>(() -> {
var s = state();
if (s.isYielded()) return yielded(s);
return TaskState.completed(mapper.apply(state().getValue()));
});
}
@Override
public Task<T> yieldIf(Predicate<T> precondition) {
var s = state();
if (!s.isYielded() && precondition.test(s.getValue())) {
return new TaskImpl<>(() -> TaskState.yielded(s.getValue()));
}
return this;
}
@SuppressWarnings("unchecked")
private <S> TaskState<S> yielded(TaskState<T> state) {
try {
return (TaskState<S>) state;
} catch (ClassCastException ex) {
log.error("cannot yield when type of next task differs", ex);
throw ex;
}
}
private TaskState<T> state() {
if (cachedResult == null)
cachedResult = compute.get();
return cachedResult;
}
}

View file

@ -0,0 +1,20 @@
package de.kentoj.scrow.bukkit.scheduler;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
@Getter
@RequiredArgsConstructor
public class TaskState<T> {
private final @Nullable T value;
private final boolean yielded;
public static <T> TaskState<T> yielded(@Nullable T value) {
return new TaskState<>(value, true);
}
public static <T> TaskState<T> completed(@Nullable T value) {
return new TaskState<>(value, false);
}
}

View file

@ -5,13 +5,15 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.Bukkit;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@NoArgsConstructor(access = AccessLevel.NONE)
public class Tasks {
public static <T> Task<T> supplyAsync(Supplier<T> supplier) {
return new TaskImpl<>(supplier);
return new TaskImpl<>(() -> TaskState.completed(supplier.get()));
}
public static Task<?> runAsync(Runnable task) {
@ -24,7 +26,9 @@ public class Tasks {
public static <T> Task<T> supplySync(Supplier<T> supplier) {
return new TaskImpl<>(() -> {
try {
return Bukkit.getScheduler().callSyncMethod(ScrowAPI.plugin, supplier::get).get();
return TaskState.completed(Bukkit.getScheduler()
.callSyncMethod(ScrowAPI.plugin, supplier::get)
.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
@ -40,4 +44,11 @@ public class Tasks {
return null;
});
}
public static void awaitAll(Task<?>... tasks) {
var cfs = Arrays.stream(tasks)
.map(Task::toFuture)
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join();
}
}

View file

@ -8,18 +8,15 @@ import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.ScrowAPISurface;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@NoArgsConstructor(access = AccessLevel.NONE)
@ -50,7 +47,7 @@ public class CoinsCommand implements CommandExecutor<CommandSender> {
ScrowAPI.getEconomyService().getCoins(player.getUniqueId()))
.<Result<Object, String>>mapSync(amount -> {
player.sendMessage(style.ok("You have " + amount + "$"));
return Results.success(Optional.empty());
}).asFuture();
return Results.success(new Object());
}).toFuture();
}
}

View file

@ -14,7 +14,6 @@ import lombok.Getter;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class CoinsGetLiteral implements CommandExecutor<CommandSender> {
@ -43,8 +42,8 @@ public class CoinsGetLiteral implements CommandExecutor<CommandSender> {
ScrowAPI.getEconomyService().getCoins(player.getUniqueId()))
.<Result<Object, String>>mapSync(amount -> {
ctx.getSender().sendMessage(style.ok(player.getName() + " has " + amount + "$"));
return Results.success(Optional.empty());
}).asFuture();
return Results.success(new Object());
}).toFuture();
}
}

View file

@ -15,7 +15,6 @@ import lombok.Getter;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class CoinsSetLiteral implements CommandExecutor<CommandSender> {
@ -47,7 +46,7 @@ public class CoinsSetLiteral implements CommandExecutor<CommandSender> {
ScrowAPI.getEconomyService().setCoins(player.getUniqueId(), amount))
.<Result<Object, String>>supplySync(() -> {
ctx.getSender().sendMessage(style.ok(player.getName() + " now has " + amount + "$"));
return Results.success(Optional.empty());
}).asFuture();
return Results.success(new Object());
}).toFuture();
}
}

View file

@ -1,106 +1,122 @@
package de.kentoj.scrow.bukkit.friends.command;
import de.kentoj.kencommandapi.api.node.CommandNode;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.api.Results;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.friendship.FriendshipImpl;
import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl;
import de.kentoj.scrow.bukkit.scheduler.Task;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;
import java.time.Instant;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class FriendAddLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> rootNode;
private final CommandNode<CommandSender> literal;
private final CommandArgument<CommandSender, OfflinePlayer> playerArg;
private final ScrowMessageStyle style;
public FriendAddLiteral() {
public FriendAddLiteral(ScrowMessageStyle style) {
this.style = style;
playerArg = CommandArgument.arg("player", OfflinePlayerArgumentType.getInstance());
rootNode = CommandNode.node("add");
rootNode.addArgument(playerArg);
rootNode.setExecutor(this);
literal = CommandNode.node("add");
literal.addArgument(playerArg);
literal.setExecutor(this);
}
@Override
public CompletableFuture<Result<Object,String>> execute(CommandContext<CommandSender> ctx) {
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
var self = (Player) ctx.getSender();
var other = ctx.getArg(playerArg);
return findAction(self, other)
.flatMap(action -> executeAction(self, other, action))
.toFuture();
return Tasks.supplyAsync(() -> findAction(self, other)
.mapSuccess(action -> {
if (action == AddAction.SEND_REQUEST) sendRequest(self, other);
else if (action == AddAction.ACCEPT_REQUEST) acceptRequest(self, other);
return new Object();
})
).toFuture();
}
private Mono<@NotNull Result> executeAction(Player self, OfflinePlayer other, AddAction action) {
return switch (action) {
case ERR_CAN_NOT_ADD_SELF -> Mono.just(Result.error("You can not add yourself"));
case ERR_ALREADY_FRIENDS -> Mono.just(Result.error("You are already friends with " + other.getName()));
case ERR_ALREADY_REQUESTED ->
Mono.just(Result.error("You already requested " + other.getName() + " to be friends with you"));
case SEND_REQUEST -> sendRequest(self, other);
case ACCEPT_REQUEST -> acceptRequest(self, other);
};
}
private Mono<@NotNull Result> sendRequest(Player self, OfflinePlayer other) {
return ScrowAPI.getFriendRequestService().saveFriendRequest(new FriendRequestImpl(self.getUniqueId(), other.getUniqueId(), Instant.now()))
.map(success -> {
if (!success) return Result.error("You already sent " + other.getName() + " a friend request");
self.sendMessage("Successfully sent " + other.getName() + " a friend request.");
private void sendRequest(Player self, OfflinePlayer other) {
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.saveFriendRequest(new FriendRequestImpl(self.getUniqueId(), other.getUniqueId())))
.runSync(() -> {
self.sendMessage(style.ok("Successfully sent " + other.getName() + " a friend request."));
var otherPlayer = other.getPlayer();
if (otherPlayer != null)
otherPlayer.sendMessage(" wants to be friends with you. Use '/f add " + self.getName() + "' to accept.");
return Result.success();
otherPlayer.sendMessage(style.ok(" wants to be friends with you. Use '/f add " + self.getName() + "' to accept."));
});
}
private Mono<@NotNull Result> acceptRequest(Player self, OfflinePlayer other) {
return Mono.when(
ScrowAPI.getFriendRequestService().deleteFriendRequest(self.getUniqueId(), other.getUniqueId()),
ScrowAPI.getFriendRequestService().deleteFriendRequest(other.getUniqueId(), self.getUniqueId()),
ScrowAPI.getFriendshipService().saveFriendship(new FriendshipImpl(self.getUniqueId(), other.getUniqueId(), Instant.now()))
)
.doOnNext(__ -> {
self.sendMessage("You are now friends with " + other.getName());
private void acceptRequest(Player self, OfflinePlayer other) {
Tasks.runAsync(() -> Tasks.awaitAll(
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.deleteFriendRequest(self.getUniqueId(), other.getUniqueId())),
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.deleteFriendRequest(other.getUniqueId(), self.getUniqueId())),
Tasks.runAsync(() -> ScrowAPI.getFriendshipService()
.saveFriendship(new FriendshipImpl(self.getUniqueId(), other.getUniqueId(), Instant.now()))
)))
.runSync(() -> {
self.sendMessage(style.ok("You are now friends with " + other.getName()));
if (other.getPlayer() != null)
other.getPlayer().sendMessage("You are now friends with " + self.getName());
})
.then(Mono.just(Result.success()));
other.getPlayer().sendMessage(style.ok("You are now friends with " + self.getName()));
});
}
private Mono<@NotNull AddAction> findAction(Player self, OfflinePlayer other) {
if (self.getUniqueId() == other.getUniqueId()) return Mono.just(AddAction.ERR_CAN_NOT_ADD_SELF);
return ScrowAPI.getFriendshipService().getFriendship(self.getUniqueId(), other.getUniqueId())
.map(f -> AddAction.ERR_ALREADY_FRIENDS)
.switchIfEmpty(
ScrowAPI.getFriendRequestService().getFriendRequest(self.getUniqueId(), other.getUniqueId())
.map(r -> AddAction.ERR_ALREADY_REQUESTED)
.switchIfEmpty(
ScrowAPI.getFriendRequestService().getFriendRequest(other.getUniqueId(), self.getUniqueId())
.map(r -> AddAction.ACCEPT_REQUEST)
.switchIfEmpty(Mono.just(AddAction.SEND_REQUEST))
)
);
private Result<AddAction, String> findAction(Player self, OfflinePlayer other) {
if (self.getUniqueId() == other.getUniqueId())
return Results.failure("You cannot add yourself.");
var isAlreadyFriendTask = checkAlreadyFriends(self, other);
var isAlreadySent = checkAlreadySent(self.getUniqueId(), other.getUniqueId());
var isShouldAccept = checkShouldAccept(self, other);
Tasks.awaitAll(isAlreadyFriendTask, isAlreadyFriendTask, isShouldAccept);
if (isAlreadyFriendTask.await())
return Results.failure("You are already friends with " + other.getName() + ".");
if (isAlreadySent.await())
return Results.failure("You've already sent " + other + " a friend request.");
if (isShouldAccept.await())
return Results.success(AddAction.ACCEPT_REQUEST);
return Results.success(AddAction.SEND_REQUEST);
}
private static Task<Boolean> checkShouldAccept(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequest(other.getUniqueId(), self.getUniqueId()))
.mapSync(Objects::nonNull);
}
private static Task<Boolean> checkAlreadyFriends(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendshipService().getFriendship(self.getUniqueId(), other.getUniqueId()))
.mapSync(Objects::nonNull);
}
private static Task<Boolean> checkAlreadySent(UUID self, UUID other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequest(self, other))
.mapSync(Objects::nonNull);
}
private enum AddAction {
ERR_ALREADY_FRIENDS,
ERR_ALREADY_REQUESTED,
ERR_CAN_NOT_ADD_SELF,
SEND_REQUEST,
ACCEPT_REQUEST
}

View file

@ -12,10 +12,10 @@ import org.bukkit.command.CommandSender;
public class FriendCommand {
public static RootCommandNode<CommandSender> create() {
var style = new ScrowMessageStyle("Friends", NamedTextColor.GREEN);
var rootNode = CommandNode.rootNode(style, "f", "friend");
rootNode.addLiteral(new FriendListLiteral(style).getRootNode());
rootNode.addLiteral(new FriendAddLiteral(style).getRootNode());
RootCommandNode<CommandSender> rootNode = CommandNode.rootNode(style, "f", "friend");
rootNode.addLiteral(new FriendListLiteral(style).getLiteral());
rootNode.addLiteral(new FriendAddLiteral(style).getLiteral());
rootNode.addLiteral(new FriendRemoveLiteral(style).getRootNode());
rootNode.addLiteral(new FriendRequestsLiteral(style).getRootNode());
rootNode.addLiteral(new FriendRequestsLiteral(style).getLiteral());
}
}

View file

@ -5,58 +5,51 @@ import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static java.util.Objects.*;
import static net.kyori.adventure.text.Component.text;
public class FriendListLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> rootNode;
private final MessageStyle style;
private final CommandNode<CommandSender> literal;
private final ScrowMessageStyle style;
public FriendListLiteral(MessageStyle style) {
public FriendListLiteral(ScrowMessageStyle style) {
this.style = style;
this.rootNode = CommandNode.node("list");
rootNode.setExecutor(this);
this.literal = CommandNode.node("list");
literal.setExecutor(this);
}
@Override
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
var sender = (Player) ctx.getSender();
var ownUid = sender.getUniqueId();
return CompletableFuture.supplyAsync(() ->
ScrowAPI.getFriendshipService().getFriendships(ownUid))
.thenApply(friendships -> ScrowAPI.getScheduler().sync(() -> {
var list = friendships.stream()
.map(friendship -> Bukkit.getOfflinePlayer(friendship.getUuids().getOther(ownUid)))
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendshipService().getFriendships(sender.getUniqueId()))
.<Result<Object, String>>mapSync(friendships -> {
var friends = friendships.stream()
.map(friendship -> Bukkit.getOfflinePlayer(friendship.getUuids().getOther(sender.getUniqueId())))
.sorted(this::compareByOnlineStatus)
.toList();
if (list.isEmpty()) return Results.failure("You have no friends. Use /friend add <player>");
if (friends.isEmpty()) return Results.failure("You have no friends. Use /friend add <player>");
var message = "§7You have §3" + list.size() + "§7" + ("§" +
.append(Component.text(list.size() == 1 ? " friend:" : " friends:"));
for (var friend : list) {
message = message.appendNewline();
var status = Component.text("(").append(
friend.isOnline()
? Component.text("online").color(NamedTextColor.GRAY)
: Component.text("offline").color(NamedTextColor.RED))
.append(Component.text(")"));
message = message.append(Component.text("").color(NamedTextColor.AQUA));
}
sender.sendMessage(msg);
return Result.success();
});
sender.sendMessage(formatList(friends));
return Results.success(new Object());
}).toFuture();
}
private int compareByOnlineStatus(OfflinePlayer p1, OfflinePlayer p2) {
@ -64,4 +57,20 @@ public class FriendListLiteral implements CommandExecutor<CommandSender> {
if (!p1.isOnline() && p2.isOnline()) return -1;
return 0;
}
private Component formatList(List<OfflinePlayer> friends) {
var message = style.ok(MiniMessage.miniMessage().deserialize("Your friends:"));
for (var friend : friends) {
var status = text("(").append(friend.isOnline()
? text("online").color(NamedTextColor.GRAY)
: text("offline").color(NamedTextColor.RED))
.append(text(")"));
message = message
.appendNewline()
.append(text("").color(NamedTextColor.AQUA))
.append(text(requireNonNull(friend.getName())))
.append(status);
}
return message;
}
}

View file

@ -9,22 +9,22 @@ import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class FriendRemoveLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> rootNode;
private final CommandArgument<CommandSender, OfflinePlayer> playerArg;
private final ScrowMessageStyle style;
private final Scheduler scheduler = ScrowAPI.getScheduler();
public FriendRemoveLiteral() {
public FriendRemoveLiteral(ScrowMessageStyle style) {
this.style = style;
this.playerArg = CommandArgument.arg("player", OfflinePlayerArgumentType.getInstance());
this.rootNode = CommandNode.node("remove");
this.rootNode.addArgument(playerArg);
@ -40,8 +40,8 @@ public class FriendRemoveLiteral implements CommandExecutor<CommandSender> {
.<Result<Object, String>>mapSync(deleted -> {
if (!deleted)
return Results.failure("You are not friends with " + friend.getName());
ctx.getSender().sendMessage("You have ended the friendship with " + friend.getName());
return Results.success(Optional.empty());
}).asFuture();
ctx.getSender().sendMessage(style.ok("You have ended the friendship with " + friend.getName()));
return Results.success(new Object());
}).toFuture();
}
}

View file

@ -1,45 +1,65 @@
package de.kentoj.scrow.bukkit.friends.command;
import de.kentoj.kencommandapi.api.node.CommandNode;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.api.Results;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text;
public class FriendRequestsLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> rootNode;
private final CommandNode<CommandSender> literal;
private final ScrowMessageStyle style;
public FriendRequestsLiteral() {
this.rootNode = CommandNode.node("requests", "list-requests");
rootNode.setExecutor(this);
public FriendRequestsLiteral(ScrowMessageStyle style) {
this.style = style;
literal = CommandNode.node("requests", "list-requests");
literal.setExecutor(this);
}
@Override
public CompletableFuture<Result<Object,String>> execute(CommandContext<CommandSender> ctx) {
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
var sender = (Player) ctx.getSender();
return ScrowAPI.getFriendRequestService().getFriendRequestsTo(sender.getUniqueId())
.collectList()
.map(list -> {
if (list.isEmpty()) return Result.error("You have no friend requests");
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequestsTo(sender.getUniqueId()))
.<Result<Object, String>>mapAsync(list -> {
if (list.isEmpty()) return Results.failure("You have no friend requests");
var requesters = list.stream().sorted(this::compareByAge).map(fr -> Bukkit.getOfflinePlayer(fr.getFrom()).getName());
var msg = "§3§lFriends§r§7 ☆ You have §3" + list.size() + "§7 open friend request";
if (list.size() > 1) msg += "s";
msg += ":§r\n";
for (FriendRequest friendRequest : list) {
msg += "§c≫ §7" + friendRequest.getFrom() + "\n";
}
sender.sendMessage(msg);
return Result.success();
sender.sendMessage(formatList(requesters.toList()));
return Results.success(new Object());
}).toFuture();
}
private int compareByAge(FriendRequest a, FriendRequest b) {
return a.getCreatedAt().isAfter(b.getCreatedAt()) ? 1 : -1;
}
private Component formatList(List<String> requesters) {
var message = style.ok(MiniMessage.miniMessage().deserialize("Open friend requests:"));
for (String requester : requesters) {
message = message
.appendNewline()
.append(text("").color(NamedTextColor.AQUA))
.append(Component.text(requester));
}
return message;
}
}

View file

@ -1,7 +1,5 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.FriendshipService;
import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair;
@ -16,27 +14,27 @@ public class InMemoryFriendshipService implements FriendshipService {
private final Map<OrderedUUIDPair, Friendship> friendships = new HashMap<>();
@Override
public Result<Friendship, String> getFriendship(UUID uuid1, UUID uuid2) {
return Results.success(friendships.get(new OrderedUUIDPair(uuid1, uuid2)));
public Friendship getFriendship(UUID uuid1, UUID uuid2) {
return friendships.get(new OrderedUUIDPair(uuid1, uuid2));
}
@Override
public Result<List<Friendship>, String> getFriendships(UUID playerId) {
return Results.success(friendships.entrySet().stream()
public List<Friendship> getFriendships(UUID playerId) {
return friendships.entrySet().stream()
.filter(ent ->
ent.getKey().getFirst().equals(playerId) || ent.getKey().getSecond().equals(playerId)
)
.map(Map.Entry::getValue)
.toList());
.toList();
}
@Override
public Result<Boolean, String> saveFriendship(Friendship friendship) {
return Results.success(friendships.putIfAbsent(friendship.getUuids(), friendship) == null);
public boolean saveFriendship(Friendship friendship) {
return friendships.putIfAbsent(friendship.getUuids(), friendship) == null;
}
@Override
public Result<Boolean, String> deleteFriendship(UUID uuid1, UUID uuid2) {
return Results.success(friendships.remove(new OrderedUUIDPair(uuid1, uuid2)) != null);
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
return friendships.remove(new OrderedUUIDPair(uuid1, uuid2)) != null;
}
}

View file

@ -1,23 +1,31 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.FriendshipService;
import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair;
import de.kentoj.scrowlib.connection.DatabaseConnectionFactory;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.statement.StatementContext;
import org.jetbrains.annotations.Nullable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class PostgresFriendshipService implements FriendshipService {
private final DatabaseConnectionFactory dbConFactory;
private final Jdbi jdbi;
public PostgresFriendshipService(DatabaseConnectionFactory dbConFactory) {
this.dbConFactory = dbConFactory;
this.dbConFactory.create().sql("""
public PostgresFriendshipService() {
checkNotNull(ScrowAPI.getJdbi());
jdbi = ScrowAPI.getJdbi();
jdbi.useHandle(h -> h.execute("""
CREATE TABLE IF NOT EXISTS friendships (
first_uuid UUID NOT NULL,
second_uuid UUID NOT NULL,
@ -26,47 +34,39 @@ public class PostgresFriendshipService implements FriendshipService {
CONSTRAINT friendship_no_self_friendship
CHECK (first_uuid <> second_uuid)
)
""").then().block();
"""));
}
@Override
public Result<Friendship, String> getFriendship(UUID uuid1, UUID uuid2) {
public @Nullable Friendship getFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
return dbConFactory.create().sql("""
return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at
FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.map(row -> (Friendship) new FriendshipImpl(
row.get("first_uuid", UUID.class),
row.get("second_uuid", UUID.class),
row.get("created_at", Instant.class)
))
.one();
.map(this::parseFriendship)
.findFirst().orElse(null));
}
@Override
public Result<List<Friendship>, String> getFriendships(UUID playerId) {
return dbConFactory.create().sql("""
public List<Friendship> getFriendships(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at
FROM friendships
WHERE first_uuid = :playerId OR second_uuid = :playerId
""")
.bind("playerId", playerId)
.map((row, meta) -> (Friendship) new FriendshipImpl(
row.get("first_uuid", UUID.class),
row.get("second_uuid", UUID.class),
row.get("created_at", Instant.class)
))
.all();
.map(this::parseFriendship)
.list());
}
@Override
public Result<Boolean, String> saveFriendship(Friendship friendship) {
return dbConFactory.create().sql("""
public boolean saveFriendship(Friendship friendship) {
return jdbi.withHandle(h -> h.createUpdate("""
INSERT INTO friendships (first_uuid, second_uuid, created_at)
VALUES (:first, :second, :createdAt)
ON CONFLICT (first_uuid, second_uuid) DO NOTHING
@ -74,23 +74,26 @@ public class PostgresFriendshipService implements FriendshipService {
.bind("first", friendship.getUuids().getFirst())
.bind("second", friendship.getUuids().getSecond())
.bind("createdAt", friendship.getCreatedAt())
.fetch()
.rowsUpdated()
.map(rows -> rows > 0);
.execute() > 0);
}
@Override
public Result<Boolean, String> deleteFriendship(UUID uuid1, UUID uuid2) {
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
return dbConFactory.create().sql("""
return jdbi.withHandle(h -> h.createUpdate("""
DELETE FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.fetch()
.rowsUpdated()
.map(rows -> rows > 0);
.execute() > 0);
}
private Friendship parseFriendship(ResultSet rs, StatementContext __) throws SQLException {
return new FriendshipImpl(
rs.getObject("first_uuid", UUID.class),
rs.getObject("second_uuid", UUID.class),
rs.getObject("created_at", Instant.class)
);
}
}

View file

@ -1,14 +1,20 @@
package de.kentoj.scrow.bukkit.friends.request;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.time.Instant;
import java.util.UUID;
@Value
@AllArgsConstructor
public class FriendRequestImpl implements FriendRequest {
UUID from;
UUID to;
Instant createdAt;
public FriendRequestImpl(UUID from, UUID to) {
this(from, to, Instant.now());
}
}

View file

@ -1,24 +1,30 @@
package de.kentoj.scrow.bukkit.friends.request;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequestService;
import de.kentoj.scrowlib.connection.DatabaseConnectionFactory;
import io.r2dbc.spi.Readable;
import org.jetbrains.annotations.NotNull;
import org.springframework.r2dbc.core.DatabaseClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.statement.StatementContext;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class PostgresFriendRequestService implements FriendRequestService {
private final DatabaseConnectionFactory conFactory;
private final Jdbi jdbi;
public PostgresFriendRequestService(DatabaseConnectionFactory conFactory) {
this.conFactory = conFactory;
conFactory.create().sql("""
public PostgresFriendRequestService() {
checkNotNull(ScrowAPI.getJdbi());
jdbi = ScrowAPI.getJdbi();
jdbi.useHandle(h -> h.execute("""
CREATE TABLE IF NOT EXISTS friend-requests (
from_uuid UUID NOT NULL,
to_uuid UUID NOT NULL,
@ -27,12 +33,12 @@ public class PostgresFriendRequestService implements FriendRequestService {
CONSTRAINT friendship_no_self_friendship
CHECK (first_uuid <> second_uuid)
)
""").then().block();
"""));
}
@Override
public Result<FriendRequest, String> getFriendRequest(UUID from, UUID to) {
return conFactory.create().sql("""
public @Nullable FriendRequest getFriendRequest(UUID from, UUID to) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friend-requests
WHERE from_uuid = :from AND to_uuid = :to
@ -40,65 +46,62 @@ public class PostgresFriendRequestService implements FriendRequestService {
.bind("from", from)
.bind("to", to)
.map(this::parseFriendRequest)
.one();
.findFirst()
.orElse(null));
}
@Override
public Result<Boolean, String> saveFriendRequest(FriendRequest friendRequest) {
return conFactory.create().sql("""
public boolean saveFriendRequest(FriendRequest friendRequest) {
return jdbi.withHandle(h -> h.createUpdate("""
INSERT INTO friend-requests (from_uuid, to_uuid, created_at)
VALUES (:from, :to, :created_at)
""")
.bind("from", friendRequest.getFrom())
.bind("to", friendRequest.getTo())
.fetch()
.rowsUpdated()
.map(cnt -> cnt > 0);
.execute() > 0);
}
@Override
public Result<List<FriendRequest>, String> getFriendRequestsTo(UUID playerId) {
return conFactory.create().sql("""
public List<FriendRequest> getFriendRequestsTo(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friend-requests
WHERE to_uuid = :to
""")
.bind("to", playerId)
.map(this::parseFriendRequest)
.all();
.collectIntoList());
}
@Override
public Result<List<FriendRequest>, String> getFriendRequestsFrom(UUID playerId) {
return conFactory.create().sql("""
public List<FriendRequest> getFriendRequestsFrom(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friend-requests
WHERE from_uuid = :from
""")
.bind("from", playerId)
.map(this::parseFriendRequest)
.all();
.collectIntoList());
}
@Override
public Result<Boolean, String> deleteFriendRequest(UUID from, UUID to) {
return conFactory.create().sql("""
public boolean deleteFriendRequest(UUID from, UUID to) {
return jdbi.withHandle(h -> h.createUpdate("""
DELETE
FROM friend-requests
WHERE from_uuid = :from AND to_uuid = :to
""")
.bind("from", from)
.bind("to", to)
.fetch()
.rowsUpdated()
.map(deleted -> deleted > 0);
.execute() > 0);
}
private FriendRequest parseFriendRequest(Readable row) {
private FriendRequest parseFriendRequest(ResultSet rs, StatementContext __) throws SQLException {
return new FriendRequestImpl(
row.get("from_uuid", UUID.class),
row.get("to_uuid", UUID.class),
row.get("created_at", Instant.class)
rs.getObject("from_uuid", UUID.class),
rs.getObject("to_uuid", UUID.class),
rs.getObject("created_at", Instant.class)
);
}
}

View file

@ -24,6 +24,6 @@ public class LobbyCommand implements CommandExecutor<CommandSender> {
@Override
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
Bukkit.dispatchCommand(ctx.getSender(), "play lobby");
return CompletableFuture.completedFuture(Results.success(null));
return CompletableFuture.completedFuture(Results.success(new Object()));
}
}

View file

@ -8,6 +8,7 @@ import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import lombok.Getter;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -31,8 +32,8 @@ public class PlayCommand implements CommandExecutor<CommandSender> {
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
var gamemode = ctx.getArg(gamemodeArg);
var player = (Player) ctx.getSender();
return CompletableFuture.supplyAsync(() -> ScrowAPI.getInstanceManager().getInstances()
.flatMapSuccess(instances -> {
return Tasks.supplyAsync(() -> ScrowAPI.getInstanceManager().getInstances()
.mapSuccess(instances -> {
var instance = instances.stream()
.filter(inst -> inst.getTemplate().equals(gamemode))
.findFirst()

View file

@ -1,23 +1,23 @@
package de.kentoj.scrowlib.servermanager;
import com.leakyabstractions.result.api.Result;
import org.jspecify.annotations.Nullable;
import java.util.List;
public interface InstanceManager {
/**
* @return Mono with handle of deployed server
*/
Result<ServerInstance, String> deployInstance(String template);
ServerInstance deployInstance(String template);
Result<Object, String> destroyInstance(String handle);
/**
* @return if the instance was found
*/
boolean destroyInstance(String handle);
/**
* @return Flux with handles of running servers
*/
Result<List<ServerInstance>, String> getInstances();
List<ServerInstance> getInstances();
Result<@Nullable ServerInstance, String> getInstance(String handle);
@Nullable
ServerInstance getInstance(String handle);
}