This commit is contained in:
kento2 2026-08-05 21:40:52 +02:00
parent c60f19cafc
commit 2ea2eb5d1e
114 changed files with 671 additions and 609 deletions

View file

@ -2,4 +2,6 @@ build --java_language_version=25
build --java_runtime_version=25 build --java_runtime_version=25
build --tool_java_language_version=25 build --tool_java_language_version=25
build --tool_java_runtime_version=25 build --tool_java_runtime_version=25
build --define=maven_repo=https://repo.lab0x13.site/snapshots build --define=maven_repo=https://git.lab0x13.site/api/packages/scrow/maven
build --remote_cache=https://cache.lab0x13.site
build --remote_upload_local_results=false

5
.gitignore vendored
View file

@ -5,8 +5,6 @@ build/
!**/src/test/**/build/ !**/src/test/**/build/
.kotlin .kotlin
run/
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea/modules.xml .idea/modules.xml
.idea/jarRepositories.xml .idea/jarRepositories.xml
@ -50,4 +48,5 @@ bin/
/bazel-core /bazel-core
/bazel-out /bazel-out
/bazel-testlogs /bazel-testlogs
/MODULE.bazel.lock /MODULE.bazel.lock
/run

View file

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="run dev server" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="bazel run //:dev-server" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bedrock/cross/bin/oksh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>

15
BUILD Normal file
View file

@ -0,0 +1,15 @@
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
sh_binary(
name = "dev-server",
data = ["//core-bukkit:plugin"],
env = {
"MC_VERSION": "26.2",
"BUILT_PLUGIN": "bazel-bin/core-bukkit/plugin.jar",
},
args = [
"LuckPerms.jar=https://download.luckperms.net/1652/bukkit/loader/LuckPerms-Bukkit-5.5.65.jar",
"ViaVersion.jar=https://hangarcdn.papermc.io/plugins/ViaVersion/ViaVersion/versions/5.11.0/PAPER/ViaVersion-5.11.0.jar",
"ViaBackwards.jar=https://hangarcdn.papermc.io/plugins/ViaVersion/ViaBackwards/versions/5.11.0/PAPER/ViaBackwards-5.11.0.jar",
],
srcs = ["runDevServer.sh"],
)

View file

@ -1,6 +1,7 @@
bazel_dep(name = "rules_java", version = "9.7.0") bazel_dep(name = "rules_java", version = "9.7.0")
bazel_dep(name = "rules_jvm_external", version = "7.1") bazel_dep(name = "rules_jvm_external", version = "7.1")
bazel_dep(name = "bazel_jar_jar", version = "0.1.15") bazel_dep(name = "bazel_jar_jar", version = "0.1.15")
bazel_dep(name = "rules_shell", version = "0.8.0")
git_override( git_override(
module_name = "rules_jvm_external", module_name = "rules_jvm_external",

View file

@ -1,15 +1,6 @@
# TODO # TODO
## BUILD ## BUILD
- turn structure into
```text
//core-bukkit:api
//core-bukkit:plugin
//core-velocity:api
//core-velocity:plugin
//core-lib
```
- MAKE THIS A MONOREPO
- make sure it doesn't break when using postgres/nats (i only tested with dummies) - make sure it doesn't break when using postgres/nats (i only tested with dummies)
- lwk make sure velocity plugin works at runtime - lwk make sure velocity plugin works at runtime
- runServer target: - runServer target:

View file

@ -1,20 +0,0 @@
load("@rules_jvm_external//:defs.bzl", "artifact")
load("@rules_java//java:java_library.bzl", "java_library")
java_library(
name = "core-bukkit-api",
srcs = glob(["src/main/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//core-lib:core",
artifact("io.papermc.paper:paper-api"),
artifact("org.projectlombok:lombok"),
artifact("de.kentoj.scrow:kencommandapi-bukkit"),
],
exports = [
"//core-lib:core",
artifact("de.kentoj.scrow:kencommandapi-bukkit"),
artifact("org.mongodb:bson"),
],
plugins = [ "//third_party:lombok_plugin" ],
)

View file

@ -1,46 +0,0 @@
package de.kentoj.scrow.bukkit.minigame.lobby;
import de.kentoj.scrow.bukkit.gameserver.GameServer;
import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.PlayerManager;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class LobbyMinigame implements Minigame {
@Getter
private final String gameName;
@Getter
private final ScrowMessageStyle messageStyle;
@Getter
@Setter
private GameServer gameServer = null;
@Getter
@Setter
private CompletableFuture<Void> destroyGameServer;
@Override
public int getMinParticipants() {
return 0;
}
@Override
public int getMaxParticipants() {
return 0;
}
@Override
public PlayerManager getPlayerManager() {
return null;
}
@Override
public CompletableFuture<Void> start() {
return null;
}
}

View file

@ -1,27 +0,0 @@
package de.kentoj.scrow.bukkit.region;
import lombok.Getter;
@Getter
public final class Range {
private final double min;
private final double max;
public boolean contains(double num) {
return min <= num && num <= max;
}
public double getLength() {
return max - min;
}
public Range(double a, double b) {
if (a < b) {
min = a;
max = b;
} else {
min = b;
max = a;
}
}
}

View file

@ -1,18 +0,0 @@
package de.kentoj.scrow.bukkit.region;
import org.bukkit.Location;
import java.util.List;
import java.util.UUID;
public interface Region {
/**
* A random uuid assigned to the Region at creation
*/
UUID getUuid();
boolean isInRegion(Location location);
List<ChunkPos> getChunks();
}

View file

@ -1,14 +0,0 @@
package de.kentoj.scrow.bukkit.region.event;
import de.kentoj.scrow.bukkit.region.Region;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
@RequiredArgsConstructor
@Getter
public abstract class RegionEvent extends Event {
private final Region region;
private final Player player;
}

View file

@ -1,17 +0,0 @@
load("@rules_java//java:java_binary.bzl", "java_binary")
load("@rules_jvm_external//:defs.bzl", "artifact")
java_binary(
name = "core-bukkit-plugin",
srcs = glob(["src/main/**/*.java"]),
main_class = "none",
resources = glob(["src/main/resources/**"], allow_empty = True),
deps = [
"//core-bukkit-api:core-bukkit-api",
artifact("net.luckperms:api"),
artifact("io.papermc.paper:paper-api"),
artifact("io.nats:jnats"),
artifact("net.kyori:adventure-key:5.2.0"),
],
plugins = [ "//third_party:lombok_plugin" ],
)

43
core-bukkit/BUILD Normal file
View file

@ -0,0 +1,43 @@
load("@rules_jvm_external//:defs.bzl", "artifact")
load("@rules_java//java:java_library.bzl", "java_library")
load("@rules_java//java:java_binary.bzl", "java_binary")
java_library(
name = "api",
srcs = glob(["api/main/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//core-lib:core",
artifact("io.papermc.paper:paper-api"),
artifact("org.projectlombok:lombok"),
artifact("de.kentoj.scrow:kencommandapi-bukkit"),
],
exports = [
"//core-lib:core",
artifact("de.kentoj.scrow:kencommandapi-bukkit"),
artifact("org.mongodb:bson"),
],
)
java_binary(
name = "plugin_bin",
srcs = glob(["plugin/main/**/*.java"]),
create_executable = False,
resources = glob(["plugin/main/resources/**"]),
resource_strip_prefix = "core-bukkit/plugin/main/resources",
deps = [
":api",
artifact("net.luckperms:api"),
artifact("io.papermc.paper:paper-api"),
artifact("io.nats:jnats"),
artifact("net.kyori:adventure-key:5.2.0"),
],
)
genrule(
name = "plugin",
srcs = [":plugin_bin_deploy.jar"],
outs = ["plugin.jar"],
cmd = "cp $< $@",
visibility = ["//visibility:public"],
)

View file

@ -24,35 +24,26 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ScrowAPI { public class ScrowAPI {
public ScrowAPI() {
}
/** /**
* Might be null in testing environments * Might be null in testing environments
*/ */
@Getter
private static @Nullable Jdbi jdbi; private static @Nullable Jdbi jdbi;
@ApiStatus.Internal @ApiStatus.Internal
public static Plugin plugin; public static Plugin plugin;
@Getter
private static EconomyService economyService; private static EconomyService economyService;
@Getter
private static FriendshipService friendshipService; private static FriendshipService friendshipService;
@Getter
private static FriendRequestService friendRequestService; private static FriendRequestService friendRequestService;
@Getter
private static MessageBroker messageBroker; private static MessageBroker messageBroker;
@Getter
private static InstanceManager instanceManager; private static InstanceManager instanceManager;
@Getter
private static RegionManager regionManager; private static RegionManager regionManager;
@Getter
private static CommandAPI<Player> playerCommands; private static CommandAPI<Player> playerCommands;
@Getter
private static CommandAPI<CommandSender> commands; private static CommandAPI<CommandSender> commands;
@Getter
private static PlayerPrefixProvider playerPrefixProvider; private static PlayerPrefixProvider playerPrefixProvider;
@Getter
private static final MinigameManager minigameManager = new MinigameManager(); private static final MinigameManager minigameManager = new MinigameManager();
private static NetworkPlayerFactory playerFactory; private static NetworkPlayerFactory playerFactory;
@ -123,5 +114,57 @@ public class ScrowAPI {
public static void setCommands(CommandAPI<CommandSender> commands) { public static void setCommands(CommandAPI<CommandSender> commands) {
ScrowAPI.commands = commands; ScrowAPI.commands = commands;
} }
public static @Nullable Jdbi jdbi() {
return jdbi;
}
public static Plugin plugin() {
return plugin;
}
public static EconomyService economyService() {
return economyService;
}
public static FriendshipService friendshipService() {
return friendshipService;
}
public static FriendRequestService friendRequestService() {
return friendRequestService;
}
public static MessageBroker messageBroker() {
return messageBroker;
}
public static InstanceManager instanceManager() {
return instanceManager;
}
public static RegionManager regionManager() {
return regionManager;
}
public static CommandAPI<Player> playerCommands() {
return playerCommands;
}
public static CommandAPI<CommandSender> commands() {
return commands;
}
public static PlayerPrefixProvider playerPrefixProvider() {
return playerPrefixProvider;
}
public static NetworkPlayerFactory playerFactory() {
return playerFactory;
}
public static MinigameManager minigameManager() {
return minigameManager;
}
} }

View file

@ -6,19 +6,19 @@ import java.util.concurrent.CompletableFuture;
public interface Minigame { public interface Minigame {
ScrowMessageStyle getMessageStyle(); ScrowMessageStyle messageStyle();
/** /**
* Required participant count * Required participant count
*/ */
int getMinParticipants(); int minParticipants();
/** /**
* Maximally allowed participant count * Maximally allowed participant count
*/ */
int getMaxParticipants(); int maxParticipants();
PlayerManager getPlayerManager(); PlayerManager playerManager();
CompletableFuture<Void> start(); CompletableFuture<Void> start();
} }

View file

@ -1,7 +1,6 @@
package de.kentoj.scrow.bukkit.minigame; package de.kentoj.scrow.bukkit.minigame;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -10,15 +9,18 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
@RequiredArgsConstructor
public final class PlayerManager { public final class PlayerManager {
private final Set<UUID> participants = new HashSet<>(); private final Set<UUID> participants = new HashSet<>();
private final Set<UUID> spectators = new HashSet<>(); private final Set<UUID> spectators = new HashSet<>();
private final MinigameManager minigameManager = ScrowAPI.getMinigameManager(); private final MinigameManager minigameManager = ScrowAPI.minigameManager();
private final Minigame minigame; private final Minigame minigame;
public PlayerManager(Minigame minigame) {
this.minigame = minigame;
}
public Stream<Player> getParticipants() { public Stream<Player> getParticipants() {
return participants.stream().map(Bukkit::getPlayer); return participants.stream().map(Bukkit::getPlayer);
} }

View file

@ -18,14 +18,13 @@ import org.bukkit.event.player.*;
public class LobbyPhase<T extends Minigame> extends CompositePhase<T> { public class LobbyPhase<T extends Minigame> extends CompositePhase<T> {
@Getter
private final LinearPhaseFlow subPhaseFlow = new LinearPhaseFlow("lobby"); private final LinearPhaseFlow subPhaseFlow = new LinearPhaseFlow("lobby");
public LobbyPhase(PhaseContext<T> ctx) { public LobbyPhase(PhaseContext<T> ctx) {
super(ctx); super(ctx);
PhaseContext<T> subCtx = new PhaseContext<>(ctx.game(), subPhaseFlow); PhaseContext<T> subCtx = new PhaseContext<>(ctx.game(), subPhaseFlow);
subPhaseFlow.add(new WaitForMinimumPlayersPhase(subCtx)); subPhaseFlow.add(new WaitForMinimumPlayersPhase<>(subCtx));
subPhaseFlow.add(new WaitForMaximumPlayersPhase(subCtx)); subPhaseFlow.add(new WaitForMaximumPlayersPhase<>(subCtx));
} }
@Override @Override
@ -62,4 +61,9 @@ public class LobbyPhase<T extends Minigame> extends CompositePhase<T> {
ctx.players().getAllPlayers().forEach(p -> ctx.players().getAllPlayers().forEach(p ->
p.sendMessage("§8[§c-§8] §7" + ev.getPlayer().getName())); p.sendMessage("§8[§c-§8] §7" + ev.getPlayer().getName()));
} }
@Override
public LinearPhaseFlow subPhaseFlow() {
return subPhaseFlow;
}
} }

View file

@ -5,9 +5,6 @@ import de.kentoj.scrow.bukkit.minigame.phase.Phase;
import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
class WaitForMaximumPlayersPhase<T extends Minigame> extends Phase<T> { class WaitForMaximumPlayersPhase<T extends Minigame> extends Phase<T> {
private int secsLeft; private int secsLeft;
@ -23,7 +20,7 @@ class WaitForMaximumPlayersPhase<T extends Minigame> extends Phase<T> {
} }
private void onQuit(PlayerQuitEvent ev) { private void onQuit(PlayerQuitEvent ev) {
var isEnough = ctx.players().getParticipantCount() >= ctx.game().getMinParticipants(); var isEnough = ctx.players().getParticipantCount() >= ctx.game().minParticipants();
if (!isEnough) { if (!isEnough) {
//deployedServer //deployedServer
ctx.phases().rewindPhase(); ctx.phases().rewindPhase();

View file

@ -17,7 +17,7 @@ class WaitForMinimumPlayersPhase<T extends Minigame> extends Phase<T> {
} }
private void onJoin(PlayerJoinEvent ev) { private void onJoin(PlayerJoinEvent ev) {
boolean isEnoughParticipants = ctx.players().getParticipantCount() >= ctx.game().getMinParticipants(); boolean isEnoughParticipants = ctx.players().getParticipantCount() >= ctx.game().minParticipants();
if (isEnoughParticipants) ctx.advancePhase(); if (isEnoughParticipants) ctx.advancePhase();
} }

View file

@ -9,11 +9,11 @@ public abstract class CompositePhase<T extends Minigame> extends Phase<T> {
super(ctx); super(ctx);
} }
public abstract PhaseFlow getSubPhaseFlow(); public abstract PhaseFlow subPhaseFlow();
@Override @Override
protected void onStart() { protected void onStart() {
getSubPhaseFlow().start().thenRun(super::advancePhase); subPhaseFlow().start().thenRun(super::advancePhase);
} }
@Override @Override

View file

@ -3,16 +3,18 @@ package de.kentoj.scrow.bukkit.minigame.phase;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.minigame.Minigame; import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
import lombok.RequiredArgsConstructor;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@RequiredArgsConstructor
public abstract class Phase<T extends Minigame> implements IPhase { public abstract class Phase<T extends Minigame> implements IPhase {
protected final PhaseContext<T> ctx; protected final PhaseContext<T> ctx;
private boolean isActive = false; private boolean isActive = false;
protected Phase(PhaseContext<T> ctx) {
this.ctx = ctx;
}
/** /**
* Shall call {@link PhaseFlow#advancePhase()} * Shall call {@link PhaseFlow#advancePhase()}
*/ */

View file

@ -4,7 +4,7 @@ import java.util.concurrent.CompletableFuture;
public interface PhaseFlow { public interface PhaseFlow {
String getName(); String name();
/** /**
* end the current phase, go to the next phase and start it * end the current phase, go to the next phase and start it

View file

@ -3,26 +3,28 @@ package de.kentoj.scrow.bukkit.minigame.phaseflow;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.minigame.phase.IPhase; import de.kentoj.scrow.bukkit.minigame.phase.IPhase;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow; import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@Slf4j
@RequiredArgsConstructor
public class LinearPhaseFlow implements PhaseFlow { public class LinearPhaseFlow implements PhaseFlow {
@Getter private final Logger log = LoggerFactory.getLogger(LinearPhaseFlow.class);
private final String name; private final String name;
private final List<IPhase> phases = new ArrayList<>(); private final List<IPhase> phases = new ArrayList<>();
private CompletableFuture<Void> onEnd = null; private CompletableFuture<Void> onEnd = null;
private int curInd = -1; private int curInd = -1;
public LinearPhaseFlow(String name) {
this.name = name;
}
public void add(IPhase phase) { public void add(IPhase phase) {
this.phases.add(phase); this.phases.add(phase);
} }
@ -39,7 +41,7 @@ public class LinearPhaseFlow implements PhaseFlow {
cur = current(); cur = current();
if (cur == null) { if (cur == null) {
onEnd.complete(null); onEnd.complete(null);
log.info("phase flow {} ended", getName()); log.info("phase flow {} ended", name());
return; return;
} }
startPhase(cur); startPhase(cur);
@ -78,7 +80,7 @@ public class LinearPhaseFlow implements PhaseFlow {
Preconditions.checkState(onEnd == null, "attempting to start game that is already active"); Preconditions.checkState(onEnd == null, "attempting to start game that is already active");
onEnd = new CompletableFuture<>(); onEnd = new CompletableFuture<>();
onEnd.thenRun(() -> onEnd = null); onEnd.thenRun(() -> onEnd = null);
log.info("starting phase flow {}", getName()); log.info("starting phase flow {}", name());
advancePhase(); advancePhase();
return onEnd; return onEnd;
} }
@ -106,4 +108,9 @@ public class LinearPhaseFlow implements PhaseFlow {
if (curInd < 0 || curInd >= phases.size()) return null; if (curInd < 0 || curInd >= phases.size()) return null;
return phases.get(curInd); return phases.get(curInd);
} }
@Override
public String name() {
return name;
}
} }

View file

@ -8,12 +8,16 @@ import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListeners;
import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListenersImpl; import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListenersImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class PhaseContext<T extends Minigame> { public class PhaseContext<T extends Minigame> {
private final T game; private final T game;
private final PhaseFlow phaseFlow; private final PhaseFlow phaseFlow;
private final PhaseListeners listeners = new PhaseListenersImpl(); private final PhaseListeners listeners = new PhaseListenersImpl();
public PhaseContext(T game, PhaseFlow phaseFlow) {
this.game = game;
this.phaseFlow = phaseFlow;
}
public T game() { public T game() {
return game; return game;
} }
@ -23,11 +27,11 @@ public class PhaseContext<T extends Minigame> {
} }
public PlayerManager players() { public PlayerManager players() {
return game.getPlayerManager(); return game.playerManager();
} }
public MessageStyle style() { public MessageStyle style() {
return game.getMessageStyle(); return game.messageStyle();
} }
public PhaseListeners listeners() { public PhaseListeners listeners() {

View file

@ -1,11 +1,12 @@
package de.kentoj.scrow.bukkit.region; package de.kentoj.scrow.bukkit.region;
import lombok.Getter;
import java.util.UUID; import java.util.UUID;
public abstract class AbstractRegion implements Region { public abstract class AbstractRegion implements Region {
@Getter
private final UUID uuid = UUID.randomUUID(); private final UUID uuid = UUID.randomUUID();
public UUID uuid() {
return uuid;
}
} }

View file

@ -1,13 +1,8 @@
package de.kentoj.scrow.bukkit.region; package de.kentoj.scrow.bukkit.region;
import lombok.Value;
import org.bukkit.Chunk; import org.bukkit.Chunk;
@Value public record ChunkPos(int x, int z) {
public class ChunkPos {
int x;
int z;
public static ChunkPos fromChunk(Chunk chunk) { public static ChunkPos fromChunk(Chunk chunk) {
return new ChunkPos(chunk.getX(), chunk.getZ()); return new ChunkPos(chunk.getX(), chunk.getZ());
} }

View file

@ -1,6 +1,5 @@
package de.kentoj.scrow.bukkit.region; package de.kentoj.scrow.bukkit.region;
import lombok.RequiredArgsConstructor;
import org.bukkit.Location; import org.bukkit.Location;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,11 +9,14 @@ import java.util.List;
/** /**
* region consisting of multiple regions * region consisting of multiple regions
*/ */
@RequiredArgsConstructor public class CompositeRegion extends AbstractRegion {
public class MergedRegion extends AbstractRegion {
private final Region[] regions; private final Region[] regions;
public CompositeRegion(Region[] regions) {
this.regions = regions;
}
@Override @Override
public boolean isInRegion(Location location) { public boolean isInRegion(Location location) {
for (Region region : regions) { for (Region region : regions) {
@ -24,9 +26,9 @@ public class MergedRegion extends AbstractRegion {
} }
@Override @Override
public List<ChunkPos> getChunks() { public List<ChunkPos> chunks() {
return Arrays.stream(regions) return Arrays.stream(regions)
.map(Region::getChunks) .map(Region::chunks)
.reduce(new ArrayList<>(), (list, chunks) -> { .reduce(new ArrayList<>(), (list, chunks) -> {
list.addAll(chunks); list.addAll(chunks);
return list; return list;

View file

@ -12,9 +12,9 @@ public class CuboidRegion extends AbstractRegion {
private final Range zRange; private final Range zRange;
public CuboidRegion(Location loc1, Location loc2) { public CuboidRegion(Location loc1, Location loc2) {
xRange = new Range(loc1.getX(), loc2.getX()); xRange = Range.of(loc1.getX(), loc2.getX());
yRange = new Range(loc1.getY(), loc2.getY()); yRange = Range.of(loc1.getY(), loc2.getY());
zRange = new Range(loc1.getY(), loc2.getY()); zRange = Range.of(loc1.getY(), loc2.getY());
} }
@Override @Override
@ -25,14 +25,14 @@ public class CuboidRegion extends AbstractRegion {
} }
@Override @Override
public List<ChunkPos> getChunks() { public List<ChunkPos> chunks() {
List<ChunkPos> res = new ArrayList<>(); List<ChunkPos> res = new ArrayList<>();
int minChunkX = (int) Math.floor(xRange.getMin()) << 4; int minChunkX = (int) Math.floor(xRange.min()) << 4;
int maxChunkX = (int) Math.ceil(xRange.getMax()) << 4; int maxChunkX = (int) Math.ceil(xRange.max()) << 4;
for (int chunkX = minChunkX; chunkX < maxChunkX; chunkX++) { for (int chunkX = minChunkX; chunkX < maxChunkX; chunkX++) {
int minChunkZ = (int) Math.floor(zRange.getMin()) << 4; int minChunkZ = (int) Math.floor(zRange.min()) << 4;
int maxChunkZ = (int) Math.ceil(zRange.getMax()) << 4; int maxChunkZ = (int) Math.ceil(zRange.max()) << 4;
for (int chunkZ = minChunkZ; chunkZ < maxChunkZ; chunkZ++) { for (int chunkZ = minChunkZ; chunkZ < maxChunkZ; chunkZ++) {
res.add(new ChunkPos(chunkX, chunkZ)); res.add(new ChunkPos(chunkX, chunkZ));
} }

View file

@ -1,18 +1,22 @@
package de.kentoj.scrow.bukkit.region; package de.kentoj.scrow.bukkit.region;
import lombok.RequiredArgsConstructor;
import org.bukkit.Location; import org.bukkit.Location;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@RequiredArgsConstructor
public class CylinderRegion extends AbstractRegion { public class CylinderRegion extends AbstractRegion {
private final Location center; private final Location center;
private final double height; private final double height;
private final double radius; private final double radius;
public CylinderRegion(Location center, double height, double radius) {
this.center = center;
this.height = height;
this.radius = radius;
}
@Override @Override
public boolean isInRegion(Location location) { public boolean isInRegion(Location location) {
if (location.getY() < center.getY()) return false; if (location.getY() < center.getY()) return false;
@ -23,7 +27,7 @@ public class CylinderRegion extends AbstractRegion {
} }
@Override @Override
public List<ChunkPos> getChunks() { public List<ChunkPos> chunks() {
List<ChunkPos> res = new ArrayList<>(); List<ChunkPos> res = new ArrayList<>();
int a = (int) Math.ceil(radius / 16); int a = (int) Math.ceil(radius / 16);

View file

@ -0,0 +1,18 @@
package de.kentoj.scrow.bukkit.region;
public record Range(
double min,
double max
) {
public boolean contains(double num) {
return min <= num && num <= max;
}
public double getLength() {
return max - min;
}
public static Range of(double a, double b) {
return new Range(Math.min(a, b), Math.max(a, b));
}
}

View file

@ -0,0 +1,25 @@
package de.kentoj.scrow.bukkit.region;
import org.bukkit.Location;
import java.util.List;
import java.util.UUID;
public interface Region {
/**
* A random uuid assigned to the Region at creation
*/
UUID uuid();
/**
* only called if location is in {@link Region#chunks()}
*/
boolean isInRegion(Location location);
/**
* Used for optimization: isInRegion is only called if the location is in one of these chunks
* @return Chunks that the region touches
*/
List<ChunkPos> chunks();
}

View file

@ -0,0 +1,18 @@
package de.kentoj.scrow.bukkit.region.event;
import de.kentoj.scrow.bukkit.region.Region;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerEvent;
public abstract class RegionEvent extends PlayerEvent {
private final Region region;
protected RegionEvent(Region region, Player player) {
super(player);
this.region = region;
}
public Region getRegion() {
return region;
}
}

View file

@ -1,8 +1,6 @@
package de.kentoj.scrow.bukkit.region.event; package de.kentoj.scrow.bukkit.region.event;
import de.kentoj.scrow.bukkit.region.Region; import de.kentoj.scrow.bukkit.region.Region;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -12,8 +10,6 @@ public class RegionJoinEvent extends RegionEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
@Setter
@Getter
private boolean isCancelled; private boolean isCancelled;
public RegionJoinEvent(Region region, Player player) { public RegionJoinEvent(Region region, Player player) {
@ -28,4 +24,14 @@ public class RegionJoinEvent extends RegionEvent implements Cancellable {
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }
@Override
public boolean isCancelled() {
return this.isCancelled;
}
@Override
public void setCancelled(boolean isCancelled) {
this.isCancelled = isCancelled;
}
} }

View file

@ -1,8 +1,6 @@
package de.kentoj.scrow.bukkit.region.event; package de.kentoj.scrow.bukkit.region.event;
import de.kentoj.scrow.bukkit.region.Region; import de.kentoj.scrow.bukkit.region.Region;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@ -12,8 +10,6 @@ public class RegionLeaveEvent extends RegionEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
@Setter
@Getter
private boolean isCancelled; private boolean isCancelled;
public RegionLeaveEvent(Region region, Player player) { public RegionLeaveEvent(Region region, Player player) {
@ -28,4 +24,14 @@ public class RegionLeaveEvent extends RegionEvent implements Cancellable {
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }
@Override
public boolean isCancelled() {
return this.isCancelled;
}
@Override
public void setCancelled(boolean isCancelled) {
this.isCancelled = isCancelled;
}
} }

View file

@ -19,7 +19,7 @@ public interface Task<T> {
} }
default <S> Task<S> supplySync(Supplier<S> supplier) { default <S> Task<S> supplySync(Supplier<S> supplier) {
return mapSync(__ -> supplier.get()); return mapSync(_ -> supplier.get());
} }
default Task<?> thenSync(Consumer<T> consumer) { default Task<?> thenSync(Consumer<T> consumer) {
@ -32,14 +32,14 @@ public interface Task<T> {
<S> Task<S> mapSync(Function<T, S> mapper); <S> Task<S> mapSync(Function<T, S> mapper);
default Task<?> runAsync(Runnable task) { default Task<?> runAsync(Runnable task) {
return mapAsync(__ -> { return mapAsync(_ -> {
task.run(); task.run();
return null; return null;
}); });
} }
default <S> Task<S> supplyAsync(Supplier<S> supplier) { default <S> Task<S> supplyAsync(Supplier<S> supplier) {
return mapAsync(__ -> supplier.get()); return mapAsync(_ -> supplier.get());
} }
default Task<?> thenAsync(Consumer<T> consumer) { default Task<?> thenAsync(Consumer<T> consumer) {

View file

@ -1,10 +1,9 @@
package de.kentoj.scrow.bukkit.scheduler; package de.kentoj.scrow.bukkit.scheduler;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Function; import java.util.function.Function;
@ -17,20 +16,23 @@ import java.util.function.Supplier;
* *
* @param <T> type of computed result * @param <T> type of computed result
*/ */
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class TaskImpl<T> implements Task<T> { public class TaskImpl<T> implements Task<T> {
private static final Logger log = LoggerFactory.getLogger(TaskImpl.class);
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final Supplier<TaskState<T>> compute; private final Supplier<TaskState<T>> compute;
private volatile TaskState<T> cachedResult = null; private volatile TaskState<T> cachedResult = null;
TaskImpl(Supplier<TaskState<T>> compute) {
this.compute = compute;
}
@Override @Override
public CompletableFuture<T> toFuture() { public CompletableFuture<T> toFuture() {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try {
return state().getValue(); return state().value();
} catch (Exception ex) { } catch (Exception ex) {
log.error("failed completing future", ex); log.error("failed completing future", ex);
throw new CompletionException(ex); throw new CompletionException(ex);
@ -44,11 +46,11 @@ public class TaskImpl<T> implements Task<T> {
public <S> Task<S> mapSync(Function<T, S> mapper) { public <S> Task<S> mapSync(Function<T, S> mapper) {
return new TaskImpl<>(() -> { return new TaskImpl<>(() -> {
var s = state(); var s = state();
if (s.isYielded()) return yielded(s); if (s.yielded()) return yielded(s);
try { try {
return TaskState.completed(Bukkit.getScheduler() return TaskState.completed(Bukkit.getScheduler()
.callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(s.getValue())) .callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(s.value()))
.get()); .get());
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@ -65,16 +67,16 @@ public class TaskImpl<T> implements Task<T> {
public <S> Task<S> mapAsync(Function<T, S> mapper) { public <S> Task<S> mapAsync(Function<T, S> mapper) {
return new TaskImpl<>(() -> { return new TaskImpl<>(() -> {
var s = state(); var s = state();
if (s.isYielded()) return yielded(s); if (s.yielded()) return yielded(s);
return TaskState.completed(mapper.apply(state().getValue())); return TaskState.completed(mapper.apply(state().value()));
}); });
} }
@Override @Override
public Task<T> yieldIf(Predicate<T> precondition) { public Task<T> yieldIf(Predicate<T> precondition) {
var s = state(); var s = state();
if (!s.isYielded() && precondition.test(s.getValue())) { if (!s.yielded() && precondition.test(s.value())) {
return new TaskImpl<>(() -> TaskState.yielded(s.getValue())); return new TaskImpl<>(() -> TaskState.yielded(s.value()));
} }
return this; return this;
} }

View file

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

View file

@ -10,8 +10,11 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Supplier; import java.util.function.Supplier;
@NoArgsConstructor(access = AccessLevel.NONE)
public class Tasks { public class Tasks {
private Tasks() {
}
public static <T> Task<T> supplyAsync(Supplier<T> supplier) { public static <T> Task<T> supplyAsync(Supplier<T> supplier) {
return new TaskImpl<>(() -> TaskState.completed(supplier.get())); return new TaskImpl<>(() -> TaskState.completed(supplier.get()));
} }

View file

@ -26,31 +26,21 @@ public final class CoreImplPlugin extends JavaPlugin {
var cachedPrefixProvider = new CachedPrefixProvider(); var cachedPrefixProvider = new CachedPrefixProvider();
ScrowAPISurface.initScrowAPI(this, cachedPrefixProvider); ScrowAPISurface.initScrowAPI(this, cachedPrefixProvider);
ScrowAPI.getPlayerCommands().register(new CoinsCommand().getRootLiteral()); ScrowAPI.playerCommands().register(new CoinsCommand().rootLiteral());
ScrowAPI.getPlayerCommands().register(FriendCommand.create()); ScrowAPI.playerCommands().register(new FriendCommand().rootLiteral());
var instanceCache = new InstanceCache(); var instanceCache = new InstanceCache();
ScrowAPI.getCommands().register(InstanceCommand.create(instanceCache)); ScrowAPI.commands().register(new InstanceCommand(instanceCache).rootLiteral());
ScrowAPI.getPlayerCommands().register(new PlayCommand().getRootLiteral()); ScrowAPI.playerCommands().register(new PlayCommand().rootLiteral());
ScrowAPI.getPlayerCommands().register(new LobbyCommand().getRootLiteral()); ScrowAPI.playerCommands().register(new LobbyCommand().rootLiteral());
var luckperms = LuckPermsProvider.get(); var luckperms = LuckPermsProvider.get();
new LuckPermsPrefixSetter(luckperms, cachedPrefixProvider).init(this); new LuckPermsPrefixSetter(luckperms, cachedPrefixProvider).init(this);
getServer().getPluginManager().registerEvents(new ChatStyleListener(cachedPrefixProvider), this); getServer().getPluginManager().registerEvents(new ChatStyleListener(cachedPrefixProvider), this);
getServer().getPluginManager().registerEvents(new TabStyleListener(cachedPrefixProvider), this); getServer().getPluginManager().registerEvents(new TabStyleListener(cachedPrefixProvider), this);
// TODO/FIXME // TODO/FIXME
//registerServer(); //registerServer();
} }
private void registerServer() {
try (var file = FileChannel.open(Path.of("/proc/self/fd/4"), StandardOpenOption.WRITE)) {
//noinspection ResultOfMethodCallIgnored
file.write(ByteBuffer.wrap(new byte[]{'\n'}));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override @Override
public void onDisable() { public void onDisable() {
ScrowAPISurface.destroyScrowAPI(); ScrowAPISurface.destroyScrowAPI();

View file

@ -2,7 +2,6 @@ package de.kentoj.scrow.bukkit;
import de.kentoj.kencommandapi.CommandAPI; import de.kentoj.kencommandapi.CommandAPI;
import de.kentoj.scrow.bukkit.internal.player.NetworkPlayerWatchdog; import de.kentoj.scrow.bukkit.internal.player.NetworkPlayerWatchdog;
import de.kentoj.scrow.bukkit.internal.player.prefix.CachedPrefixProvider;
import de.kentoj.scrow.bukkit.internal.region.RegionManagerImpl; import de.kentoj.scrow.bukkit.internal.region.RegionManagerImpl;
import de.kentoj.scrow.bukkit.internal.region.RegionPlayerTracker; import de.kentoj.scrow.bukkit.internal.region.RegionPlayerTracker;
import de.kentoj.scrow.bukkit.internal.region.RegionTask; import de.kentoj.scrow.bukkit.internal.region.RegionTask;
@ -21,19 +20,21 @@ import io.nats.client.Nats;
import io.nats.client.Options; import io.nats.client.Options;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.jdbi.v3.core.Jdbi; import org.jdbi.v3.core.Jdbi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Properties; import java.util.Properties;
@Slf4j
@NoArgsConstructor(access = AccessLevel.NONE) @NoArgsConstructor(access = AccessLevel.NONE)
public class ScrowAPISurface { public class ScrowAPISurface {
private static final Logger log = LoggerFactory.getLogger(ScrowAPISurface.class);
public static void initScrowAPI(Plugin plugin, PlayerPrefixProvider prefixProvider) { public static void initScrowAPI(Plugin plugin, PlayerPrefixProvider prefixProvider) {
ScrowAPI.setCorePlugin(plugin); ScrowAPI.setCorePlugin(plugin);
@ -67,17 +68,17 @@ public class ScrowAPISurface {
log.error("HOST_POSTGRES not set -> Using in-memory database"); log.error("HOST_POSTGRES not set -> Using in-memory database");
} }
ScrowAPI.setPlayerFactory(playerId -> new NetworkPlayerImpl(ScrowAPI.getMessageBroker(), playerId)); ScrowAPI.setPlayerFactory(playerId -> new NetworkPlayerImpl(ScrowAPI.messageBroker(), playerId));
ScrowAPI.setRegionManager(new RegionManagerImpl()); ScrowAPI.setRegionManager(new RegionManagerImpl());
ScrowAPI.setCommands(new CommandAPI<>(plugin, CommandSender.class)); ScrowAPI.setCommands(new CommandAPI<>(plugin, CommandSender.class));
ScrowAPI.setPlayerCommands(new CommandAPI<>(plugin, Player.class)); ScrowAPI.setPlayerCommands(new CommandAPI<>(plugin, Player.class));
ScrowAPI.setInstanceManager(new InstanceManagerImpl(ScrowAPI.getMessageBroker())); ScrowAPI.setInstanceManager(new InstanceManagerImpl(ScrowAPI.messageBroker()));
ScrowAPI.setEconomyService(ScrowAPI.getJdbi() != null ? ScrowAPI.setEconomyService(ScrowAPI.jdbi() != null ?
new PostgresEconomyService(ScrowAPI.getJdbi()) : new InMemoryEconomyService()); new PostgresEconomyService(ScrowAPI.jdbi()) : new InMemoryEconomyService());
ScrowAPI.setFriendRequestService(ScrowAPI.getJdbi() != null ? ScrowAPI.setFriendRequestService(ScrowAPI.jdbi() != null ?
new PostgresFriendRequestService(ScrowAPI.getJdbi()) : new InMemoryFriendRequestService()); new PostgresFriendRequestService(ScrowAPI.jdbi()) : new InMemoryFriendRequestService());
ScrowAPI.setFriendshipService(ScrowAPI.getJdbi() != null ? ScrowAPI.setFriendshipService(ScrowAPI.jdbi() != null ?
new PostgresFriendshipService(ScrowAPI.getJdbi()) : new InMemoryFriendshipService()); new PostgresFriendshipService(ScrowAPI.jdbi()) : new InMemoryFriendshipService());
ScrowAPI.setPlayerPrefixProvider(prefixProvider); ScrowAPI.setPlayerPrefixProvider(prefixProvider);
new RegionTask(new RegionPlayerTracker()).runTaskTimer(plugin, 10, 10); new RegionTask(new RegionPlayerTracker()).runTaskTimer(plugin, 10, 10);

View file

@ -15,7 +15,7 @@ import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public final class CoinsCommand { public final class CoinsCommand {
@Getter
private final RootLiteral<Player> rootLiteral; private final RootLiteral<Player> rootLiteral;
private final MessageStyle style; private final MessageStyle style;
@ -24,18 +24,22 @@ public final class CoinsCommand {
rootLiteral = Literal.<Player>builder("coins", "bal", "balance") rootLiteral = Literal.<Player>builder("coins", "bal", "balance")
.withPermission("command.coins.get.self") .withPermission("command.coins.get.self")
.withExecutor(this::execute) .withExecutor(this::execute)
.withLiteral(new CoinsSetLiteral(style).getLiteral()) .withLiteral(new CoinsSetLiteral(style).literal())
.withLiteral(new CoinsGetLiteral(style).getLiteral()) .withLiteral(new CoinsGetLiteral(style).literal())
.asRootLiteral(style); .asRootLiteral(style);
} }
public CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) { public CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) {
var player = ctx.getSender(); var player = ctx.getSender();
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getEconomyService().getCoins(player.getUniqueId())) ScrowAPI.economyService().getCoins(player.getUniqueId()))
.<Result<Object, String>>mapSync(amount -> { .<Result<Object, String>>mapSync(amount -> {
player.sendMessage(style.ok("You have " + amount + "$")); player.sendMessage(style.ok("You have " + amount + "$"));
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public RootLiteral<Player> rootLiteral() {
return rootLiteral;
}
} }

View file

@ -18,7 +18,6 @@ import java.util.concurrent.CompletableFuture;
public final class CoinsGetLiteral { public final class CoinsGetLiteral {
@Getter
private final Literal<Player> literal; private final Literal<Player> literal;
private final MessageStyle style; private final MessageStyle style;
private final CommandArgumentSpec<Player, OfflinePlayer> playerArg; private final CommandArgumentSpec<Player, OfflinePlayer> playerArg;
@ -39,10 +38,14 @@ public final class CoinsGetLiteral {
private CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) { private CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) {
var player = ctx.getArg(playerArg); var player = ctx.getArg(playerArg);
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getEconomyService().getCoins(player.getUniqueId())) ScrowAPI.economyService().getCoins(player.getUniqueId()))
.<Result<Object, String>>mapSync(amount -> { .<Result<Object, String>>mapSync(amount -> {
ctx.getSender().sendMessage(style.ok(player.getName() + " has " + amount + "$")); ctx.getSender().sendMessage(style.ok(player.getName() + " has " + amount + "$"));
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -6,28 +6,23 @@ import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.argument.types.IntegerArgumentType; import de.kentoj.kencommandapi.api.argument.types.IntegerArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandContext; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.literal.Literal; import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.LiteralBuilder;
import de.kentoj.kencommandapi.api.platform.MessageStyle; import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType; import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import lombok.Getter;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public final class CoinsSetLiteral { public final class CoinsSetLiteral {
@Getter
private final Literal<Player> literal; private final Literal<Player> literal;
private final MessageStyle style; private final MessageStyle style;
private final CommandArgumentSpec<Player, OfflinePlayer> playerArg; private final CommandArgumentSpec<Player, OfflinePlayer> playerArg;
private final CommandArgumentSpec<Player, Integer> amountArg; private final CommandArgumentSpec<Player, Integer> amountArg;
public CoinsSetLiteral(MessageStyle style) { CoinsSetLiteral(MessageStyle style) {
this.style = style; this.style = style;
playerArg = CommandArgumentSpec.builder("player", new OfflinePlayerArgumentType<Player>()) playerArg = CommandArgumentSpec.builder("player", new OfflinePlayerArgumentType<Player>())
.withDefaultValue(CommandContext::getSender) .withDefaultValue(CommandContext::getSender)
@ -46,10 +41,14 @@ public final class CoinsSetLiteral {
var player = ctx.getArg(playerArg); var player = ctx.getArg(playerArg);
var amount = ctx.getArg(amountArg); var amount = ctx.getArg(amountArg);
return Tasks.runAsync(() -> return Tasks.runAsync(() ->
ScrowAPI.getEconomyService().setCoins(player.getUniqueId(), amount)) ScrowAPI.economyService().setCoins(player.getUniqueId(), amount))
.<Result<Object, String>>supplySync(() -> { .<Result<Object, String>>supplySync(() -> {
ctx.getSender().sendMessage(style.ok(player.getName() + " now has " + amount + "$")); ctx.getSender().sendMessage(style.ok(player.getName() + " now has " + amount + "$"));
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -24,17 +24,15 @@ import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class FriendAddLiteral { public final class FriendAddLiteral {
@Getter
private final Literal<Player> literal; private final Literal<Player> literal;
private final CommandArgumentSpec<Player, OfflinePlayer> playerArg; private final CommandArgumentSpec<Player, OfflinePlayer> playerArg;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
private final FriendRequestService requestService = ScrowAPI.friendRequestService();
private final FriendshipService friendsService = ScrowAPI.friendshipService();
private final FriendRequestService requestService = ScrowAPI.getFriendRequestService(); FriendAddLiteral(ScrowMessageStyle style) {
private final FriendshipService friendsService = ScrowAPI.getFriendshipService();
public FriendAddLiteral(ScrowMessageStyle style) {
this.style = style; this.style = style;
playerArg = CommandArgumentSpec.builder("player", new OfflinePlayerArgumentType<Player>()) playerArg = CommandArgumentSpec.builder("player", new OfflinePlayerArgumentType<Player>())
.build(); .build();
@ -114,6 +112,10 @@ public class FriendAddLiteral {
.mapSync(Objects::nonNull); .mapSync(Objects::nonNull);
} }
public Literal<Player> literal() {
return literal;
}
private enum AddAction { private enum AddAction {
SEND_REQUEST, SEND_REQUEST,
ACCEPT_REQUEST ACCEPT_REQUEST

View file

@ -11,17 +11,18 @@ import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@NoArgsConstructor(access = AccessLevel.NONE) public final class FriendCommand {
public class FriendCommand {
public static RootLiteral<Player> create() { private final RootLiteral<Player> rootLiteral;
public FriendCommand() {
var style = new ScrowMessageStyle("Friends", NamedTextColor.AQUA); var style = new ScrowMessageStyle("Friends", NamedTextColor.AQUA);
return Literal.<Player>builder("f", "friend") rootLiteral = Literal.<Player>builder("f", "friend")
.withLiteral(new FriendListLiteral(style).getLiteral()) .withLiteral(new FriendListLiteral(style).literal())
.withLiteral(new FriendAddLiteral(style).getLiteral()) .withLiteral(new FriendAddLiteral(style).literal())
.withLiteral(new FriendRemoveLiteral(style).getLiteral()) .withLiteral(new FriendRemoveLiteral(style).literal())
.withLiteral(new FriendRequestDeclineLiteral(style).getLiteral()) .withLiteral(new FriendRequestDeclineLiteral(style).literal())
.withLiteral(new FriendRequestsLiteral(style).getLiteral()) .withLiteral(new FriendRequestsLiteral(style).literal())
.asRootLiteral(style); .asRootLiteral(style);
} }
@ -39,4 +40,8 @@ public class FriendCommand {
: ClickEvent.suggestCommand("/f decline " + requestSenderName)) : ClickEvent.suggestCommand("/f decline " + requestSenderName))
.color(NamedTextColor.RED)); .color(NamedTextColor.RED));
} }
public RootLiteral<Player> rootLiteral() {
return rootLiteral;
}
} }

View file

@ -7,7 +7,6 @@ import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@ -22,22 +21,24 @@ import java.util.concurrent.CompletableFuture;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.text;
@RequiredArgsConstructor
public final class FriendListLiteral { public final class FriendListLiteral {
private final Literal<Player> literal;
@Getter
private final Literal<Player> literal = Literal.<Player>builder("list")
.withExecutor(this::displayFriendList)
.build();
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
FriendListLiteral(ScrowMessageStyle style) {
this.style = style;
literal = Literal.<Player>builder("list")
.withExecutor(this::displayFriendList)
.build();
}
private CompletableFuture<Result<Object, String>> displayFriendList(CommandContext<Player> ctx) { private CompletableFuture<Result<Object, String>> displayFriendList(CommandContext<Player> ctx) {
var sender = ctx.getSender(); var sender = ctx.getSender();
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getFriendshipService().getFriendships(sender.getUniqueId())) ScrowAPI.friendshipService().getFriendships(sender.getUniqueId()))
.<Result<Object, String>>mapSync(friendships -> { .<Result<Object, String>>mapSync(friendships -> {
var friends = friendships.stream() var friends = friendships.stream()
.map(friendship -> Bukkit.getOfflinePlayer(friendship.getUuids().getOther(sender.getUniqueId()))) .map(friendship -> Bukkit.getOfflinePlayer(friendship.uuids().getOther(sender.getUniqueId())))
.sorted(this::compareByOnlineStatus) .sorted(this::compareByOnlineStatus)
.toList(); .toList();
if (friends.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>");
@ -61,10 +62,14 @@ public final class FriendListLiteral {
: " <gray>(<red>offline<gray>)"); : " <gray>(<red>offline<gray>)");
message = message message = message
.appendNewline() .appendNewline()
.append(text("").color(style.getPrefixColor())) .append(text("").color(style.prefixColor()))
.append(text(requireNonNull(friend.getName())).color(NamedTextColor.GRAY)) .append(text(requireNonNull(friend.getName())).color(NamedTextColor.GRAY))
.append(status); .append(status);
} }
return message; return message;
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -15,9 +15,8 @@ import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class FriendRemoveLiteral { public final class FriendRemoveLiteral {
@Getter
private final Literal<Player> literal; private final Literal<Player> literal;
private final CommandArgumentSpec<Player, OfflinePlayer> playerArg; private final CommandArgumentSpec<Player, OfflinePlayer> playerArg;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
@ -35,7 +34,7 @@ public class FriendRemoveLiteral {
private CompletableFuture<Result<Object, String>> removeFriend(CommandContext<Player> ctx) { private CompletableFuture<Result<Object, String>> removeFriend(CommandContext<Player> ctx) {
var friend = ctx.getArg(playerArg); var friend = ctx.getArg(playerArg);
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getFriendshipService().deleteFriendship(friend.getUniqueId(), ctx.getSender().getUniqueId())) ScrowAPI.friendshipService().deleteFriendship(friend.getUniqueId(), ctx.getSender().getUniqueId()))
.<Result<Object, String>>mapSync(deleted -> { .<Result<Object, String>>mapSync(deleted -> {
if (!deleted) if (!deleted)
return Results.failure("You are not friends with " + friend.getName() + "."); return Results.failure("You are not friends with " + friend.getName() + ".");
@ -43,4 +42,8 @@ public class FriendRemoveLiteral {
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -17,14 +17,12 @@ import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor public final class FriendRequestDeclineLiteral {
public class FriendRequestDeclineLiteral {
@Getter
private final Literal<Player> literal; private final Literal<Player> literal;
private final CommandArgumentSpec<Player, OfflinePlayer> playerArg; private final CommandArgumentSpec<Player, OfflinePlayer> playerArg;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
private final FriendRequestService requestService = ScrowAPI.getFriendRequestService(); private final FriendRequestService requestService = ScrowAPI.friendRequestService();
FriendRequestDeclineLiteral(ScrowMessageStyle style) { FriendRequestDeclineLiteral(ScrowMessageStyle style) {
this.style = style; this.style = style;
@ -49,4 +47,8 @@ public class FriendRequestDeclineLiteral {
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -5,12 +5,9 @@ import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.invocation.CommandContext; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.literal.Literal; import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrowlib.friends.FriendRequest;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.AccessLevel; import de.kentoj.scrowlib.friends.FriendRequest;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
@ -22,29 +19,33 @@ import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.text;
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) public final class FriendRequestsLiteral {
public class FriendRequestsLiteral {
@Getter private final Literal<Player> literal;
private final Literal<Player> literal = Literal.<Player>builder("requests")
.withExecutor(this::displayFriendRequests)
.build();
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
FriendRequestsLiteral(ScrowMessageStyle style) {
this.style = style;
literal = Literal.<Player>builder("requests")
.withExecutor(this::displayFriendRequests)
.build();
}
private CompletableFuture<Result<Object, String>> displayFriendRequests(CommandContext<Player> ctx) { private CompletableFuture<Result<Object, String>> displayFriendRequests(CommandContext<Player> ctx) {
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequestsTo(ctx.getSender().getUniqueId())) ScrowAPI.friendRequestService().getFriendRequestsTo(ctx.getSender().getUniqueId()))
.<Result<Object, String>>mapAsync(list -> { .<Result<Object, String>>mapAsync(list -> {
if (list.isEmpty()) return Results.failure("You have no friend requests."); 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 requesters = list.stream()
.sorted(this::compareByAge)
.map(fr -> Bukkit.getOfflinePlayer(fr.from()).getName());
ctx.getSender().sendMessage(formatList(requesters.toList())); ctx.getSender().sendMessage(formatList(requesters.toList()));
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
private int compareByAge(FriendRequest a, FriendRequest b) { private int compareByAge(FriendRequest a, FriendRequest b) {
return a.getCreatedAt().isAfter(b.getCreatedAt()) ? 1 : -1; return a.createdAt().isAfter(b.createdAt()) ? 1 : -1;
} }
private Component formatList(List<String> requesters) { private Component formatList(List<String> requesters) {
@ -52,10 +53,14 @@ public class FriendRequestsLiteral {
for (String requester : requesters) { for (String requester : requesters) {
message = message message = message
.appendNewline() .appendNewline()
.append(text("").color(style.getPrefixColor())) .append(text("").color(style.prefixColor()))
.append(Component.text(requester).color(NamedTextColor.GRAY) .append(Component.text(requester).color(NamedTextColor.GRAY)
.append(FriendCommand.friendRequestOptions(requester, false))); .append(FriendCommand.friendRequestOptions(requester, false)));
} }
return message; return message;
} }
public Literal<Player> literal() {
return literal;
}
} }

View file

@ -2,7 +2,6 @@ package de.kentoj.scrow.bukkit.command.instance;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrowlib.instancemanager.ServerInstance; import de.kentoj.scrowlib.instancemanager.ServerInstance;
import de.kentoj.scrowlib.messaging.MessageBroker;
import org.bson.Document; import org.bson.Document;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -12,18 +11,15 @@ import java.util.concurrent.ConcurrentMap;
public class InstanceCache { public class InstanceCache {
private final MessageBroker messageBroker = ScrowAPI.getMessageBroker();
private final ConcurrentMap<String, ServerInstance> cache = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ServerInstance> cache = new ConcurrentHashMap<>();
public InstanceCache() { public InstanceCache() {
messageBroker.subscribe("event.instance.up", doc -> { ScrowAPI.messageBroker().subscribe("event.instance.up", doc -> {
var inst = toInstance(doc); var inst = toInstance(doc);
cache.put(inst.getHandle(), inst); cache.put(inst.handle(), inst);
});
messageBroker.subscribe("event.instance.down", doc -> {
cache.remove(doc.getString("handle"));
}); });
ScrowAPI.messageBroker().subscribe("event.instance.down", doc ->
cache.remove(doc.getString("handle")));
} }
public Collection<ServerInstance> getInstances() { public Collection<ServerInstance> getInstances() {

View file

@ -8,14 +8,20 @@ import lombok.NoArgsConstructor;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@NoArgsConstructor(access = AccessLevel.NONE) public final class InstanceCommand {
public class InstanceCommand {
public static RootLiteral<CommandSender> create(InstanceCache instanceCache) { private RootLiteral<CommandSender> rootLiteral;
public InstanceCommand(InstanceCache instanceCache) {
var style = new ScrowMessageStyle("server-manager", NamedTextColor.RED); var style = new ScrowMessageStyle("server-manager", NamedTextColor.RED);
return Literal.<CommandSender>builder("instance", "server-manager") rootLiteral = Literal.<CommandSender>builder("instance", "server-manager")
.withLiteral(new InstanceListLiteral(style).getLiteral()) .withLiteral(new InstanceListLiteral(style).literal())
.withLiteral(new InstanceDeployLiteral(style).getLiteral()) .withLiteral(new InstanceDeployLiteral(style).literal())
.withLiteral(new InstanceDestroyLiteral(instanceCache, style).getLiteral()) .withLiteral(new InstanceDestroyLiteral(instanceCache, style).literal())
.asRootLiteral(style); .asRootLiteral(style);
} }
public RootLiteral<CommandSender> rootLiteral() {
return rootLiteral;
}
} }

View file

@ -9,14 +9,12 @@ import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class InstanceDeployLiteral { public final class InstanceDeployLiteral {
@Getter
private final Literal<CommandSender> literal; private final Literal<CommandSender> literal;
private final CommandArgumentSpec<CommandSender, String> templateArg; private final CommandArgumentSpec<CommandSender, String> templateArg;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
@ -36,11 +34,15 @@ public class InstanceDeployLiteral {
var template = ctx.getArg(templateArg); var template = ctx.getArg(templateArg);
ctx.getSender().sendMessage(style.ok("Deploying instance... this may take a while.")); ctx.getSender().sendMessage(style.ok("Deploying instance... this may take a while."));
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getInstanceManager().deployInstance(template)) ScrowAPI.instanceManager().deployInstance(template))
.<Result<Object, String>>mapSync(instance -> { .<Result<Object, String>>mapSync(instance -> {
ctx.getSender().sendMessage(style.ok("Deployed instance of " + template + " with handle " + instance.getHandle() + ".")); ctx.getSender().sendMessage(style.ok("Deployed instance of " + template + " with handle " + instance.handle() + "."));
// FIXME handle unknown server // FIXME handle unknown server
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<CommandSender> literal() {
return literal;
}
} }

View file

@ -10,14 +10,12 @@ import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import de.kentoj.scrowlib.instancemanager.ServerInstance; import de.kentoj.scrowlib.instancemanager.ServerInstance;
import lombok.Getter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class InstanceDestroyLiteral { public final class InstanceDestroyLiteral {
@Getter
private final Literal<CommandSender> literal; private final Literal<CommandSender> literal;
private final CommandArgumentSpec<CommandSender, String> handleArg; private final CommandArgumentSpec<CommandSender, String> handleArg;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
@ -28,7 +26,7 @@ public class InstanceDestroyLiteral {
handleArg = CommandArgumentSpec.builder("handle", new StringArgumentType<CommandSender>()) handleArg = CommandArgumentSpec.builder("handle", new StringArgumentType<CommandSender>())
.withSuggestionProvider(_ -> .withSuggestionProvider(_ ->
cache.getInstances().stream() cache.getInstances().stream()
.map(ServerInstance::getHandle) .map(ServerInstance::handle)
.toList()) .toList())
.build(); .build();
literal = Literal.<CommandSender>builder("destroy") literal = Literal.<CommandSender>builder("destroy")
@ -39,7 +37,7 @@ public class InstanceDestroyLiteral {
private CompletableFuture<Result<Object, String>> destroyInstance(CommandContext<CommandSender> ctx) { private CompletableFuture<Result<Object, String>> destroyInstance(CommandContext<CommandSender> ctx) {
var handle = ctx.getArg(handleArg); var handle = ctx.getArg(handleArg);
return Tasks.supplyAsync(() -> ScrowAPI.getInstanceManager().destroyInstance(handle)) return Tasks.supplyAsync(() -> ScrowAPI.instanceManager().destroyInstance(handle))
.<Result<Object, String>>mapSync(found -> { .<Result<Object, String>>mapSync(found -> {
if (!found) if (!found)
return Results.failure("No instance with that handle found."); return Results.failure("No instance with that handle found.");
@ -47,4 +45,8 @@ public class InstanceDestroyLiteral {
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<CommandSender> literal() {
return literal;
}
} }

View file

@ -7,15 +7,13 @@ import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class InstanceListLiteral { public final class InstanceListLiteral {
@Getter
private final Literal<CommandSender> literal; private final Literal<CommandSender> literal;
private final ScrowMessageStyle style; private final ScrowMessageStyle style;
@ -28,15 +26,19 @@ public class InstanceListLiteral {
private CompletableFuture<Result<Object, String>> displayInstanceList(CommandContext<CommandSender> ctx) { private CompletableFuture<Result<Object, String>> displayInstanceList(CommandContext<CommandSender> ctx) {
return Tasks.supplyAsync(() -> return Tasks.supplyAsync(() ->
ScrowAPI.getInstanceManager().getInstances()) ScrowAPI.instanceManager().instances())
.<Result<Object, String>>mapSync(instances -> { .<Result<Object, String>>mapSync(instances -> {
var msg = Component.text("Running instances:"); var msg = Component.text("Running instances:");
for (var inst : instances) { for (var inst : instances) {
msg = msg.appendNewline() msg = msg.appendNewline()
.append(Component.text(" - " + inst.getHandle() + " on port " + inst.getPort())); .append(Component.text(" - " + inst.handle() + " on port " + inst.port()));
} }
ctx.getSender().sendMessage(style.ok(msg)); ctx.getSender().sendMessage(style.ok(msg));
return Results.success(new Object()); return Results.success(new Object());
}).toFuture(); }).toFuture();
} }
public Literal<CommandSender> literal() {
return literal;
}
} }

View file

@ -11,15 +11,22 @@ import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public class LobbyCommand { public final class LobbyCommand {
@Getter private final RootLiteral<Player> rootLiteral;
private final RootLiteral<Player> rootLiteral = Literal.<Player>builder("lobby", "l", "hub")
.withSyncExecutor(this::sendToLobby) public LobbyCommand() {
.asRootLiteral(ScrowMessageStyle.GENERIC); rootLiteral = Literal.<Player>builder("lobby", "l", "hub")
.withSyncExecutor(this::sendToLobby)
.asRootLiteral(ScrowMessageStyle.GENERIC);
}
private Result<Object, String> sendToLobby(CommandContext<Player> ctx) { private Result<Object, String> sendToLobby(CommandContext<Player> ctx) {
Bukkit.dispatchCommand(ctx.getSender(), "play lobby"); Bukkit.dispatchCommand(ctx.getSender(), "play lobby");
return Results.success(new Object()); return Results.success(new Object());
} }
public RootLiteral<Player> rootLiteral() {
return rootLiteral;
}
} }

View file

@ -5,21 +5,17 @@ import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandContext; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.literal.Literal; import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral; import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.scheduler.Tasks; import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle; import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class PlayCommand { public final class PlayCommand {
@Getter
private final RootLiteral<Player> rootLiteral; private final RootLiteral<Player> rootLiteral;
private final CommandArgumentSpec<Player, String> gamemodeArg; private final CommandArgumentSpec<Player, String> gamemodeArg;
@ -34,19 +30,23 @@ public class PlayCommand {
private CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) { private CompletableFuture<Result<Object, String>> execute(CommandContext<Player> ctx) {
var gamemode = ctx.getArg(gamemodeArg); var gamemode = ctx.getArg(gamemodeArg);
return Tasks.supplyAsync(() -> ScrowAPI.getInstanceManager().getInstances()) return Tasks.supplyAsync(() -> ScrowAPI.instanceManager().instances())
.mapAsync(instances -> { .mapAsync(instances -> {
var instance = instances.stream() var instance = instances.stream()
.filter(inst -> inst.getTemplate().equals(gamemode)) .filter(inst -> inst.template().equals(gamemode))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if (instance == null) if (instance == null)
return Results.failure("No instance for " + gamemode + " found."); return Results.failure("No instance for " + gamemode + " found.");
return Results.ofCallable(() -> { return Results.ofCallable(() -> {
ScrowAPI.getPlayer(ctx.getSender()).sendToInstance(instance.getHandle()); ScrowAPI.getPlayer(ctx.getSender()).sendToInstance(instance.handle());
return new Object(); return new Object();
}).mapFailure(Exception::getMessage); }).mapFailure(Exception::getMessage);
}).toFuture(); }).toFuture();
} }
public RootLiteral<Player> rootLiteral() {
return rootLiteral;
}
} }

View file

@ -13,7 +13,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@RequiredArgsConstructor
public class NetworkPlayerWatchdog { public class NetworkPlayerWatchdog {
private static final MiniMessage MM = MiniMessage.miniMessage(); private static final MiniMessage MM = MiniMessage.miniMessage();
@ -52,7 +51,7 @@ public class NetworkPlayerWatchdog {
} }
private void handle(String subject, BiConsumer<Player, Document> handler) { private void handle(String subject, BiConsumer<Player, Document> handler) {
ScrowAPI.getMessageBroker().subscribe(subject, doc -> { ScrowAPI.messageBroker().subscribe(subject, doc -> {
var player = Bukkit.getPlayer(doc.get("playerId", UUID.class)); var player = Bukkit.getPlayer(doc.get("playerId", UUID.class));
if (player != null) if (player != null)
handler.accept(player, doc); handler.accept(player, doc);

View file

@ -1,6 +1,5 @@
package de.kentoj.scrow.bukkit.internal.player.prefix; package de.kentoj.scrow.bukkit.internal.player.prefix;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.luckperms.api.LuckPerms; import net.luckperms.api.LuckPerms;
@ -16,7 +15,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
@RequiredArgsConstructor
public class LuckPermsPrefixSetter implements Listener { public class LuckPermsPrefixSetter implements Listener {
private static final MiniMessage MM = MiniMessage.miniMessage(); private static final MiniMessage MM = MiniMessage.miniMessage();
@ -24,6 +22,11 @@ public class LuckPermsPrefixSetter implements Listener {
private final LuckPerms luckPerms; private final LuckPerms luckPerms;
private final CachedPrefixProvider cache; private final CachedPrefixProvider cache;
public LuckPermsPrefixSetter(LuckPerms luckPerms, CachedPrefixProvider cache) {
this.luckPerms = luckPerms;
this.cache = cache;
}
@SuppressWarnings("resource") @SuppressWarnings("resource")
public void init(Plugin plugin) { public void init(Plugin plugin) {
luckPerms.getEventBus().subscribe(UserDataRecalculateEvent.class, this::onDataChange); luckPerms.getEventBus().subscribe(UserDataRecalculateEvent.class, this::onDataChange);
@ -51,12 +54,12 @@ public class LuckPermsPrefixSetter implements Listener {
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent ev) { public void onPlayerQuit(PlayerQuitEvent ev) {
var playerId = ev.getPlayer().getUniqueId(); var playerId = ev.getPlayer().getUniqueId();
cache.setPrefix(playerId, (Component) null); cache.setPrefix(playerId, null);
} }
private void updatePrefix(UUID playerId, @Nullable String rawPrefix) { private void updatePrefix(UUID playerId, @Nullable String rawPrefix) {
if (rawPrefix == null) { if (rawPrefix == null) {
cache.setPrefix(playerId, (Component) null); cache.setPrefix(playerId, null);
return; return;
} }
var prefixComponent = MM.deserialize(rawPrefix); var prefixComponent = MM.deserialize(rawPrefix);

View file

@ -14,7 +14,7 @@ public class RegionManagerImpl implements RegionManager {
@Override @Override
public List<Region> getRegions(ChunkPos chunkPos) { public List<Region> getRegions(ChunkPos chunkPos) {
long key = toKey(chunkPos.getX(), chunkPos.getZ()); long key = toKey(chunkPos.x(), chunkPos.z());
var uuids = chunkIndex.get(key); var uuids = chunkIndex.get(key);
if (uuids == null) return Collections.emptyList(); if (uuids == null) return Collections.emptyList();
return uuids.stream().map(regions::get).toList(); return uuids.stream().map(regions::get).toList();
@ -27,23 +27,23 @@ public class RegionManagerImpl implements RegionManager {
@Override @Override
public void registerRegion(Region region) { public void registerRegion(Region region) {
for (ChunkPos chunkPos : region.getChunks()) { for (ChunkPos chunkPos : region.chunks()) {
var key = toKey(chunkPos.getX(), chunkPos.getZ()); var key = toKey(chunkPos.x(), chunkPos.z());
chunkIndex.computeIfAbsent(key, __ -> new ArrayList<>()) chunkIndex.computeIfAbsent(key, __ -> new ArrayList<>())
.add(region.getUuid()); .add(region.uuid());
} }
regions.put(region.getUuid(), region); regions.put(region.uuid(), region);
} }
@Override @Override
public void unregisterRegion(Region region) { public void unregisterRegion(Region region) {
for (ChunkPos chunkPos : region.getChunks()) { for (ChunkPos chunkPos : region.chunks()) {
var key = toKey(chunkPos.getX(), chunkPos.getZ()); var key = toKey(chunkPos.x(), chunkPos.z());
var list = chunkIndex.get(key); var list = chunkIndex.get(key);
if (list == null) return; if (list == null) return;
list.remove(region.getUuid()); list.remove(region.uuid());
} }
regions.remove(region.getUuid()); regions.remove(region.uuid());
} }
public long toKey(int x, int z) { public long toKey(int x, int z) {

View file

@ -14,25 +14,25 @@ public class RegionPlayerTracker {
private final Map<UUID, List<UUID>> playersInRegion = new HashMap<>(); private final Map<UUID, List<UUID>> playersInRegion = new HashMap<>();
public void registerInRegion(Region region, Player player) { public void registerInRegion(Region region, Player player) {
playersInRegion.computeIfAbsent(region.getUuid(), __ -> new ArrayList<>()) playersInRegion.computeIfAbsent(region.uuid(), __ -> new ArrayList<>())
.add(player.getUniqueId()); .add(player.getUniqueId());
} }
public void unregisterInRegion(Region region, Player player) { public void unregisterInRegion(Region region, Player player) {
var uuids = playersInRegion.get(region.getUuid()); var uuids = playersInRegion.get(region.uuid());
if (uuids == null) return; if (uuids == null) return;
uuids.remove(player.getUniqueId()); uuids.remove(player.getUniqueId());
} }
public Map<Region, Stream<Player>> getRegionsWithPlayers() { public Map<Region, Stream<Player>> getRegionsWithPlayers() {
return playersInRegion.entrySet().stream().collect(Collectors.toMap( return playersInRegion.entrySet().stream().collect(Collectors.toMap(
ent -> ScrowAPI.getRegionManager().getRegion(ent.getKey()), ent -> ScrowAPI.regionManager().getRegion(ent.getKey()),
ent -> ent.getValue().stream().map(Bukkit::getPlayer) ent -> ent.getValue().stream().map(Bukkit::getPlayer)
)); ));
} }
public boolean isInRegion(Region region, Player player) { public boolean isInRegion(Region region, Player player) {
var uuids = playersInRegion.get(region.getUuid()); var uuids = playersInRegion.get(region.uuid());
if (uuids == null) return false; if (uuids == null) return false;
return uuids.contains(player.getUniqueId()); return uuids.contains(player.getUniqueId());
} }

View file

@ -9,11 +9,14 @@ import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@RequiredArgsConstructor
public class RegionTask extends BukkitRunnable { public class RegionTask extends BukkitRunnable {
private final RegionPlayerTracker playerTracker; private final RegionPlayerTracker playerTracker;
public RegionTask(RegionPlayerTracker playerTracker) {
this.playerTracker = playerTracker;
}
@Override @Override
public void run() { public void run() {
playerTracker.getRegionsWithPlayers().forEach((region, players) -> playerTracker.getRegionsWithPlayers().forEach((region, players) ->
@ -28,7 +31,7 @@ public class RegionTask extends BukkitRunnable {
); );
for (var player : Bukkit.getOnlinePlayers()) { for (var player : Bukkit.getOnlinePlayers()) {
var regions = ScrowAPI.getRegionManager().getRegions(ChunkPos.fromChunk(player.getLocation().getChunk())); var regions = ScrowAPI.regionManager().getRegions(ChunkPos.fromChunk(player.getLocation().getChunk()));
for (Region region : regions) { for (Region region : regions) {
var tracked = playerTracker.isInRegion(region, player); var tracked = playerTracker.isInRegion(region, player);
var inside = region.isInRegion(player.getLocation()); var inside = region.isInRegion(player.getLocation());

View file

@ -2,17 +2,19 @@ package de.kentoj.scrow.bukkit.style;
import de.kentoj.scrowlib.player.PlayerPrefixProvider; import de.kentoj.scrowlib.player.PlayerPrefixProvider;
import io.papermc.paper.event.player.AsyncChatEvent; import io.papermc.paper.event.player.AsyncChatEvent;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@RequiredArgsConstructor public final class ChatStyleListener implements Listener {
public class ChatStyleListener implements Listener {
private final PlayerPrefixProvider prefixProvider; private final PlayerPrefixProvider prefixProvider;
public ChatStyleListener(PlayerPrefixProvider prefixProvider) {
this.prefixProvider = prefixProvider;
}
@EventHandler @EventHandler
public void onChat(AsyncChatEvent ev) { public void onChat(AsyncChatEvent ev) {
var prefix = prefixProvider.getPrefix(ev.getPlayer().getUniqueId()); var prefix = prefixProvider.getPrefix(ev.getPlayer().getUniqueId());

View file

@ -7,8 +7,10 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@NoArgsConstructor(access = AccessLevel.NONE)
public class StyleHelper { public class StyleHelper {
private StyleHelper() {}
public static Component formatName(Component playerName, @Nullable Component prefix) { public static Component formatName(Component playerName, @Nullable Component prefix) {
Component result = prefix != null Component result = prefix != null
? prefix.append(Component.text("").color(NamedTextColor.DARK_GRAY)) ? prefix.append(Component.text("").color(NamedTextColor.DARK_GRAY))

View file

@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
public class TabStyleListener implements Listener { public final class TabStyleListener implements Listener {
private final PlayerPrefixProvider prefixProvider; private final PlayerPrefixProvider prefixProvider;

View file

@ -19,5 +19,4 @@ java_library(
artifact("io.nats:jnats"), artifact("io.nats:jnats"),
], ],
exports = shared_deps, exports = shared_deps,
plugins = [ "//third_party:lombok_plugin" ],
) )

View file

@ -1,28 +1,21 @@
package de.kentoj.scrowlib.convention; package de.kentoj.scrowlib.convention;
import de.kentoj.kencommandapi.api.platform.MessageStyle; import de.kentoj.kencommandapi.api.platform.MessageStyle;
import lombok.Getter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.format.TextDecoration;
public class ScrowMessageStyle implements MessageStyle { public record ScrowMessageStyle(
Component prefix,
String prefixText,
TextColor prefixColor
) implements MessageStyle {
public static ScrowMessageStyle GENERIC = new ScrowMessageStyle("Scrow", TextColor.color(0xe5377f)); public static ScrowMessageStyle GENERIC = new ScrowMessageStyle("Scrow", TextColor.color(0xe5377f));
@Getter
private final Component prefix;
@Getter
private final String prefixText;
@Getter
private final TextColor prefixColor;
public ScrowMessageStyle(String prefixText, TextColor prefixColor) { public ScrowMessageStyle(String prefixText, TextColor prefixColor) {
this.prefixText = prefixText; this(Component.text(prefixText).color(prefixColor), prefixText, prefixColor);
this.prefixColor = prefixColor;
this.prefix = Component.text(prefixText)
.color(prefixColor);
} }
@Override @Override

View file

@ -4,7 +4,7 @@ import java.time.Instant;
import java.util.UUID; import java.util.UUID;
public interface FriendRequest { public interface FriendRequest {
UUID getFrom(); UUID from();
UUID getTo(); UUID to();
Instant getCreatedAt(); Instant createdAt();
} }

View file

@ -4,7 +4,7 @@ import java.time.Instant;
public interface Friendship { public interface Friendship {
OrderedUUIDPair getUuids(); OrderedUUIDPair uuids();
Instant getCreatedAt(); Instant createdAt();
} }

View file

@ -1,25 +1,24 @@
package de.kentoj.scrowlib.friends; package de.kentoj.scrowlib.friends;
import lombok.Getter;
import java.util.UUID; import java.util.UUID;
@Getter public record OrderedUUIDPair(
public class OrderedUUIDPair { UUID first,
private final UUID first; UUID second
private final UUID second; ) {
public static OrderedUUIDPair of(UUID a, UUID b) {
public OrderedUUIDPair(UUID uuid1, UUID uuid2) { UUID first, second;
if (uuid1.compareTo(uuid2) < 0) { if (a.compareTo(b) < 0) {
this.first = uuid1; first = a;
this.second = uuid2; second = b;
} else { } else {
this.first = uuid2; first = b;
this.second = uuid1; second = a;
} }
return new OrderedUUIDPair(first, second);
} }
public UUID getOther(UUID self) { public UUID getOther(UUID self) {
return getFirst().equals(self) ? getSecond() : getFirst(); return first().equals(self) ? second() : first();
} }
} }

View file

@ -3,18 +3,15 @@ package de.kentoj.scrowlib.friends.friendship;
import de.kentoj.scrowlib.friends.Friendship; import de.kentoj.scrowlib.friends.Friendship;
import de.kentoj.scrowlib.friends.OrderedUUIDPair; import de.kentoj.scrowlib.friends.OrderedUUIDPair;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
@Value public record FriendshipImpl(
@RequiredArgsConstructor OrderedUUIDPair uuids,
public class FriendshipImpl implements Friendship { Instant createdAt
OrderedUUIDPair uuids; ) implements Friendship {
Instant createdAt;
public FriendshipImpl(UUID uuid1, UUID uuid2, Instant createdAt) { public FriendshipImpl(UUID uuid1, UUID uuid2, Instant createdAt) {
this(new OrderedUUIDPair(uuid1, uuid2), createdAt); this(OrderedUUIDPair.of(uuid1, uuid2), createdAt);
} }
} }

View file

@ -15,14 +15,14 @@ public class InMemoryFriendshipService implements FriendshipService {
@Override @Override
public Friendship getFriendship(UUID uuid1, UUID uuid2) { public Friendship getFriendship(UUID uuid1, UUID uuid2) {
return friendships.get(new OrderedUUIDPair(uuid1, uuid2)); return friendships.get(OrderedUUIDPair.of(uuid1, uuid2));
} }
@Override @Override
public List<Friendship> getFriendships(UUID playerId) { public List<Friendship> getFriendships(UUID playerId) {
return friendships.entrySet().stream() return friendships.entrySet().stream()
.filter(ent -> .filter(ent ->
ent.getKey().getFirst().equals(playerId) || ent.getKey().getSecond().equals(playerId) ent.getKey().first().equals(playerId) || ent.getKey().second().equals(playerId)
) )
.map(Map.Entry::getValue) .map(Map.Entry::getValue)
.toList(); .toList();
@ -30,11 +30,11 @@ public class InMemoryFriendshipService implements FriendshipService {
@Override @Override
public boolean saveFriendship(Friendship friendship) { public boolean saveFriendship(Friendship friendship) {
return friendships.putIfAbsent(friendship.getUuids(), friendship) == null; return friendships.putIfAbsent(friendship.uuids(), friendship) == null;
} }
@Override @Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) { public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
return friendships.remove(new OrderedUUIDPair(uuid1, uuid2)) != null; return friendships.remove(OrderedUUIDPair.of(uuid1, uuid2)) != null;
} }
} }

View file

@ -33,15 +33,15 @@ public class PostgresFriendshipService implements FriendshipService {
@Override @Override
public @Nullable Friendship getFriendship(UUID uuid1, UUID uuid2) { public @Nullable Friendship getFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2); var uuids = OrderedUUIDPair.of(uuid1, uuid2);
return jdbi.withHandle(h -> h.createQuery(""" return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at SELECT first_uuid, second_uuid, created_at
FROM friendships FROM friendships
WHERE first_uuid = :first AND second_uuid = :second WHERE first_uuid = :first AND second_uuid = :second
""") """)
.bind("first", uuids.getFirst()) .bind("first", uuids.first())
.bind("second", uuids.getSecond()) .bind("second", uuids.second())
.map(this::parseFriendship) .map(this::parseFriendship)
.findFirst().orElse(null)); .findFirst().orElse(null));
} }
@ -65,21 +65,21 @@ public class PostgresFriendshipService implements FriendshipService {
VALUES (:first, :second, :createdAt) VALUES (:first, :second, :createdAt)
ON CONFLICT (first_uuid, second_uuid) DO NOTHING ON CONFLICT (first_uuid, second_uuid) DO NOTHING
""") """)
.bind("first", friendship.getUuids().getFirst()) .bind("first", friendship.uuids().first())
.bind("second", friendship.getUuids().getSecond()) .bind("second", friendship.uuids().second())
.bind("createdAt", friendship.getCreatedAt()) .bind("createdAt", friendship.createdAt())
.execute() > 0); .execute() > 0);
} }
@Override @Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) { public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2); var uuids = OrderedUUIDPair.of(uuid1, uuid2);
return jdbi.withHandle(h -> h.createUpdate(""" return jdbi.withHandle(h -> h.createUpdate("""
DELETE FROM friendships DELETE FROM friendships
WHERE first_uuid = :first AND second_uuid = :second WHERE first_uuid = :first AND second_uuid = :second
""") """)
.bind("first", uuids.getFirst()) .bind("first", uuids.first())
.bind("second", uuids.getSecond()) .bind("second", uuids.second())
.execute() > 0); .execute() > 0);
} }

View file

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

View file

@ -15,21 +15,21 @@ public class InMemoryFriendRequestService implements FriendRequestService {
@Override @Override
public List<FriendRequest> getFriendRequestsTo(UUID playerId) { public List<FriendRequest> getFriendRequestsTo(UUID playerId) {
return requests.stream() return requests.stream()
.filter(req -> req.getTo().equals(playerId)) .filter(req -> req.to().equals(playerId))
.toList(); .toList();
} }
@Override @Override
public List<FriendRequest> getFriendRequestsFrom(UUID playerId) { public List<FriendRequest> getFriendRequestsFrom(UUID playerId) {
return requests.stream() return requests.stream()
.filter(req -> req.getFrom().equals(playerId)) .filter(req -> req.from().equals(playerId))
.toList(); .toList();
} }
@Override @Override
public FriendRequest getFriendRequest(UUID from, UUID to) { public FriendRequest getFriendRequest(UUID from, UUID to) {
return requests.stream() return requests.stream()
.filter(req -> req.getFrom().equals(from) && req.getTo().equals(to)) .filter(req -> req.from().equals(from) && req.to().equals(to))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
} }
@ -41,6 +41,6 @@ public class InMemoryFriendRequestService implements FriendRequestService {
@Override @Override
public boolean deleteFriendRequest(UUID from, UUID to) { public boolean deleteFriendRequest(UUID from, UUID to) {
return requests.removeIf(req -> req.getFrom().equals(from) && req.getTo().equals(to)); return requests.removeIf(req -> req.from().equals(from) && req.to().equals(to));
} }
} }

View file

@ -13,7 +13,6 @@ import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@Slf4j
public class PostgresFriendRequestService implements FriendRequestService { public class PostgresFriendRequestService implements FriendRequestService {
private final Jdbi jdbi; private final Jdbi jdbi;
@ -52,9 +51,9 @@ public class PostgresFriendRequestService implements FriendRequestService {
INSERT INTO friendrequests (from_uuid, to_uuid, created_at) INSERT INTO friendrequests (from_uuid, to_uuid, created_at)
VALUES (:from, :to, :createdAt) VALUES (:from, :to, :createdAt)
""") """)
.bind("from", friendRequest.getFrom()) .bind("from", friendRequest.from())
.bind("to", friendRequest.getTo()) .bind("to", friendRequest.to())
.bind("createdAt", friendRequest.getCreatedAt()) .bind("createdAt", friendRequest.createdAt())
.execute() > 0); .execute() > 0);
} }

View file

@ -24,7 +24,7 @@ public interface InstanceManager {
* *
* @return list of running instances * @return list of running instances
*/ */
List<ServerInstance> getInstances(); List<ServerInstance> instances();
/** /**
* Runs blocking * Runs blocking

View file

@ -8,11 +8,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
@RequiredArgsConstructor
public class InstanceManagerImpl implements InstanceManager { public class InstanceManagerImpl implements InstanceManager {
private final MessageBroker messageBroker; private final MessageBroker messageBroker;
public InstanceManagerImpl(MessageBroker messageBroker) {
this.messageBroker = messageBroker;
}
@Override @Override
public ServerInstance deployInstance(String template) { public ServerInstance deployInstance(String template) {
var document = messageBroker.request("instance.deploy", new Document("template", template)); var document = messageBroker.request("instance.deploy", new Document("template", template));
@ -31,7 +34,7 @@ public class InstanceManagerImpl implements InstanceManager {
} }
@Override @Override
public List<ServerInstance> getInstances() { public List<ServerInstance> instances() {
var document = messageBroker.request("instance.list", new Document()); var document = messageBroker.request("instance.list", new Document());
return document.getList("instances", Document.class) return document.getList("instances", Document.class)
.stream() .stream()

View file

@ -1,21 +1,17 @@
package de.kentoj.scrowlib.instancemanager; package de.kentoj.scrowlib.instancemanager;
import com.google.common.base.Preconditions;
import lombok.Value;
import org.bson.Document; import org.bson.Document;
@Value import static com.google.common.base.Preconditions.checkNotNull;
public class ServerInstance {
String handle;
String template;
int port;
public ServerInstance(String handle, String template, int port) { public record ServerInstance(
Preconditions.checkNotNull(template); String handle,
Preconditions.checkNotNull(handle); String template,
this.template = template; int port
this.handle = handle; ) {
this.port = port; public ServerInstance {
checkNotNull(template);
checkNotNull(handle);
} }
static ServerInstance fromDocument(Document document) { static ServerInstance fromDocument(Document document) {

View file

@ -1,7 +1,8 @@
package de.kentoj.scrowlib.messaging; package de.kentoj.scrowlib.messaging;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document; import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -10,9 +11,10 @@ import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Consumer; import java.util.function.Consumer;
@Slf4j
public class InMemoyMessageBroker implements MessageBroker { public class InMemoyMessageBroker implements MessageBroker {
private final Logger log = LoggerFactory.getLogger(InMemoyMessageBroker.class);
private final Map<String, List<Consumer<Document>>> subscribers = new HashMap<>(); private final Map<String, List<Consumer<Document>>> subscribers = new HashMap<>();
private final Map<String, RequestHandler> handlers = new HashMap<>(); private final Map<String, RequestHandler> handlers = new HashMap<>();
@ -25,7 +27,7 @@ public class InMemoyMessageBroker implements MessageBroker {
@Override @Override
public void subscribe(String subject, Consumer<Document> consumer) { public void subscribe(String subject, Consumer<Document> consumer) {
subscribers.computeIfAbsent(subject, __ -> new ArrayList<>()) subscribers.computeIfAbsent(subject, _ -> new ArrayList<>())
.add(consumer); .add(consumer);
} }

View file

@ -15,6 +15,7 @@ public interface MessageBroker {
void publish(String subject, Document document); void publish(String subject, Document document);
/** /**
* May block until subscription registered.
* subscribes to a subject, consumer may run asynchronously * subscribes to a subject, consumer may run asynchronously
* *
* @param subject <a href="https://docs.nats.io/nats-concepts/subjects">subject</a> to publish to * @param subject <a href="https://docs.nats.io/nats-concepts/subjects">subject</a> to publish to

View file

@ -5,8 +5,9 @@ import com.leakyabstractions.result.core.Results;
import de.kentoj.scrowlib.utils.DocumentRepository; import de.kentoj.scrowlib.utils.DocumentRepository;
import io.nats.client.Connection; import io.nats.client.Connection;
import io.nats.client.Dispatcher; import io.nats.client.Dispatcher;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document; import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -14,9 +15,10 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
@Slf4j
public class NatsMessageBroker implements MessageBroker { public class NatsMessageBroker implements MessageBroker {
private final Logger log = LoggerFactory.getLogger(NatsMessageBroker.class);
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final Connection nats; private final Connection nats;
private final Dispatcher dispatcher; private final Dispatcher dispatcher;

View file

@ -2,14 +2,14 @@ package de.kentoj.scrowlib.messaging;
import com.leakyabstractions.result.api.Result; import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results; import com.leakyabstractions.result.core.Results;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bson.Document; import org.bson.Document;
@NoArgsConstructor(access = AccessLevel.NONE)
class ResultRepository { class ResultRepository {
private ResultRepository() {
}
static Document toDocument(Result<Document, String> result) { static Document toDocument(Result<Document, String> result) {
return result.hasSuccess() return result.hasSuccess()
? new Document("data", result.getSuccess().orElseThrow()) ? new Document("data", result.getSuccess().orElseThrow())

View file

@ -4,6 +4,5 @@ import java.util.UUID;
@FunctionalInterface @FunctionalInterface
public interface NetworkPlayerFactory { public interface NetworkPlayerFactory {
NetworkPlayer create(UUID uuid); NetworkPlayer create(UUID uuid);
} }

View file

@ -12,13 +12,17 @@ import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@RequiredArgsConstructor
public class NetworkPlayerImpl implements NetworkPlayer { public class NetworkPlayerImpl implements NetworkPlayer {
private static final MiniMessage MM = MiniMessage.miniMessage(); private static final MiniMessage MM = MiniMessage.miniMessage();
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final MessageBroker messageBroker; private final MessageBroker messageBroker;
private final UUID playerId; private final UUID playerId;
public NetworkPlayerImpl(MessageBroker messageBroker, UUID playerId) {
this.messageBroker = messageBroker;
this.playerId = playerId;
}
@Override @Override
public void sendMessage(@NotNull Component message) { public void sendMessage(@NotNull Component message) {
requestAsync("player.sendMessage", new Document("message", MM.serialize(message))); requestAsync("player.sendMessage", new Document("message", MM.serialize(message)));

View file

@ -1,21 +1,22 @@
package de.kentoj.scrowlib.utils; package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@NoArgsConstructor(access = AccessLevel.NONE)
public class ComponentUtils { public class ComponentUtils {
private static final Pattern URL_REGEX = Pattern.compile("(https?://)?[a-z0-9]+(\\.[a-z0-9]+)*(\\.[a-z0-9]{1,10})((/+)[^/ ]*)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern URL_REGEX = Pattern.compile("(https?://)?[a-z0-9]+(\\.[a-z0-9]+)*(\\.[a-z0-9]{1,10})((/+)[^/ ]*)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
private static final TextReplacementConfig REPLACER = TextReplacementConfig.builder() private static final TextReplacementConfig REPLACER = TextReplacementConfig.builder()
.match(URL_REGEX) .match(URL_REGEX)
.replacement((c) -> c.clickEvent(ClickEvent.openUrl(c.content().startsWith("http") ? c.content() : "https://" + c.content()))) .replacement((c) -> c.clickEvent(ClickEvent.openUrl(c.content().startsWith("http") ? c.content() : "https://" + c.content())))
.build(); .build();
private ComponentUtils() {
}
public static Component resolveURLS(Component component) { public static Component resolveURLS(Component component) {
return component.replaceText(REPLACER); return component.replaceText(REPLACER);

View file

@ -1,13 +1,13 @@
package de.kentoj.scrowlib.utils; package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bson.Document; import org.bson.Document;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DocumentRepository { public class DocumentRepository {
private DocumentRepository() {
}
@ApiStatus.Obsolete @ApiStatus.Obsolete
public static Document fromBytes(byte[] bytes) { public static Document fromBytes(byte[] bytes) {
return Document.parse(new String(bytes)); return Document.parse(new String(bytes));

View file

@ -1,10 +1,10 @@
package de.kentoj.scrowlib.utils; package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.NONE)
public class EnvUtils { public class EnvUtils {
private EnvUtils() {
}
/** /**
* @throws IllegalStateException if the var is not set * @throws IllegalStateException if the var is not set
*/ */

View file

@ -1,29 +0,0 @@
plugins {
id("java")
alias(libs.plugins.scrow.publish)
alias(libs.plugins.scrow.basedependencies)
alias(libs.plugins.scrow.javalibrary)
alias(libs.plugins.scrow.database)
}
group = "de.kentoj.scrow"
dependencies {
api(project(":core-lib"))
api(libs.velocity.api)
api(libs.scrow.commandapi.velocity) {
isChanging = true
}
}
publishing {
publications {
create<MavenPublication>("ScrowVelocityApi") {
groupId = project.group.toString()
artifactId = project.name
version = project.version.toString()
from(components["java"])
}
}
}

View file

@ -1,15 +0,0 @@
load("@rules_java//java:java_binary.bzl", "java_binary")
load("@rules_jvm_external//:defs.bzl", "artifact")
java_binary(
name = "core-velocity-impl",
srcs = glob(["src/main/**/*.java"]),
main_class = "none",
resources = glob(["src/main/resources/**"], allow_empty = True),
deps = [
"//core-velocity-api",
artifact("com.velocitypowered:velocity-api"),
artifact("io.nats:jnats"),
],
plugins = [ "//third_party:lombok_plugin" ],
)

View file

@ -1,43 +0,0 @@
plugins {
id("java")
alias(libs.plugins.shadow)
alias(libs.plugins.scrow.basedependencies)
alias(libs.plugins.scrow.database)
}
group = "de.kentoj.scrow"
dependencies {
implementation(project(":core-velocity-api"))
annotationProcessor(libs.velocity.api)
implementation(libs.nats)
implementation(libs.postgres)
}
tasks.build {
dependsOn("shadowJar")
}
val generateBuildInfo by tasks.registering {
val outputDir = layout.buildDirectory.dir("generated/sources/buildInfo")
outputs.dir(outputDir)
doLast {
val file = outputDir.get()
.file("de/kentoj/scrow/generated/BuildInfo.java")
.asFile
file.parentFile.mkdirs()
file.writeText(
"""
package de.kentoj.scrow.generated;
public final class BuildInfo {
private BuildInfo() {}
public static final String VERSION = "${project.version}";
}
""".trimIndent()
)
}
}
sourceSets.main {
java.srcDir(generateBuildInfo)
}

Some files were not shown because too many files have changed in this diff Show more