gradle time

This commit is contained in:
kento2 2026-07-05 17:44:01 +02:00
parent 7040a6ce1e
commit 9421ef511e
47 changed files with 482 additions and 226 deletions

View file

@ -4,8 +4,6 @@ import de.kentoj.scrow.bukkit.economy.command.CoinsCommand;
import de.kentoj.scrow.bukkit.friends.command.FriendCommand;
import de.kentoj.scrow.bukkit.instancemanager.cache.InstanceCache;
import de.kentoj.scrow.bukkit.instancemanager.command.InstanceCommand;
import de.kentoj.scrow.bukkit.region.RegionPlayerTracker;
import de.kentoj.scrow.bukkit.region.RegionTask;
import de.kentoj.scrow.bukkit.instancemanager.command.LobbyCommand;
import de.kentoj.scrow.bukkit.instancemanager.command.PlayCommand;
import org.bukkit.plugin.java.JavaPlugin;
@ -30,8 +28,6 @@ public class CoreImplPlugin extends JavaPlugin {
ScrowAPI.getCommandApi().register(new LobbyCommand().getRootNode());
}
new RegionTask(new RegionPlayerTracker()).runTaskTimer(this, 10, 10);
// TODO/FIXME
//registerServer();
}

View file

@ -1,14 +1,17 @@
package de.kentoj.scrow.bukkit;
import de.kentoj.kencommandapi.CommandAPI;
import de.kentoj.scrow.bukkit.economy.InMemoryEconomyService;
import de.kentoj.scrow.bukkit.economy.PostgresEconomyService;
import de.kentoj.scrow.bukkit.friends.friendship.InMemoryFriendshipService;
import de.kentoj.scrow.bukkit.friends.friendship.PostgresFriendshipService;
import de.kentoj.scrow.bukkit.friends.request.InMemoryFriendRequestService;
import de.kentoj.scrow.bukkit.friends.request.PostgresFriendRequestService;
import de.kentoj.scrow.bukkit.playermanager.PlayerManagerImpl;
import de.kentoj.scrowlib.economy.InMemoryEconomyService;
import de.kentoj.scrowlib.economy.PostgresEconomyService;
import de.kentoj.scrowlib.friends.friendship.InMemoryFriendshipService;
import de.kentoj.scrowlib.friends.friendship.PostgresFriendshipService;
import de.kentoj.scrowlib.friends.request.InMemoryFriendRequestService;
import de.kentoj.scrowlib.friends.request.PostgresFriendRequestService;
import de.kentoj.scrow.bukkit.network.NetworkPlayerWatchdog;
import de.kentoj.scrowlib.player.NetworkPlayerImpl;
import de.kentoj.scrow.bukkit.region.RegionManagerImpl;
import de.kentoj.scrow.bukkit.region.RegionPlayerTracker;
import de.kentoj.scrow.bukkit.region.RegionTask;
import de.kentoj.scrowlib.instancemanager.InstanceManagerImpl;
import de.kentoj.scrowlib.messaging.MessageBroker;
import de.kentoj.scrowlib.utils.EnvUtils;
@ -17,12 +20,9 @@ import io.nats.client.Options;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.plugin.Plugin;
import org.jdbi.v3.core.ConnectionFactory;
import org.jdbi.v3.core.Jdbi;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
@NoArgsConstructor(access = AccessLevel.NONE)
@ -53,7 +53,7 @@ public class ScrowAPISurface {
ScrowAPI.setJdbi(Jdbi.create(EnvUtils.envOrThrow("HOST_POSTGRES"), props));
}
ScrowAPI.setPlayerManager(new PlayerManagerImpl());
ScrowAPI.setPlayerFactory(playerId -> new NetworkPlayerImpl(ScrowAPI.getMessageBroker(), playerId));
ScrowAPI.setRegionManager(new RegionManagerImpl());
ScrowAPI.setCommandApi(new CommandAPI(plugin));
ScrowAPI.setInstanceManager(new InstanceManagerImpl(ScrowAPI.getMessageBroker()));
@ -63,6 +63,9 @@ public class ScrowAPISurface {
new PostgresFriendRequestService() : new InMemoryFriendRequestService());
ScrowAPI.setFriendshipService(ScrowAPI.getJdbi() != null ?
new PostgresFriendshipService() : new InMemoryFriendshipService());
new RegionTask(new RegionPlayerTracker()).runTaskTimer(plugin, 10, 10);
new NetworkPlayerWatchdog().listen();
}
public static void destroyScrowAPI() {

View file

@ -1,40 +0,0 @@
package de.kentoj.scrow.bukkit.economy;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.misc.EconomyService;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class InMemoryEconomyService implements EconomyService {
private final Map<UUID, Integer> coins = new HashMap<>();
@Override
public int getCoins(UUID playerId) {
return coins.getOrDefault(playerId, 0);
}
@Override
public void setCoins(UUID playerId, int amount) {
Preconditions.checkArgument(amount >= 0, "amount can not be negative");
coins.put(playerId, amount);
}
@Override
public void depositCoins(UUID playerId, int amount) {
Preconditions.checkArgument(amount >= 0, "amount can not be negative");
var cur = coins.getOrDefault(playerId, 0);
coins.put(playerId, cur + amount);
}
@Override
public boolean tryWithdrawCoins(UUID playerId, int amount) {
Preconditions.checkArgument(amount >= 0, "amount can not be negative");
var cur = coins.getOrDefault(playerId, 0);
if (cur < amount) return false;
coins.put(playerId, cur - amount);
return true;
}
}

View file

@ -1,86 +0,0 @@
package de.kentoj.scrow.bukkit.economy;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.misc.EconomyService;
import org.jdbi.v3.core.Jdbi;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
// TODO log operations to separate table
public class PostgresEconomyService implements EconomyService {
private final Jdbi jdbi;
public PostgresEconomyService() {
checkNotNull(ScrowAPI.getJdbi());
this.jdbi = ScrowAPI.getJdbi();
jdbi.withHandle(h -> h.execute("""
CREATE TABLE IF NOT EXISTS economy (
playerId UUID NOT NULL,
balance INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(playerId)
)
"""));
}
@Override
public int getCoins(UUID playerId) {
return jdbi.withHandle(h -> h
.createQuery("SELECT balance FROM economy WHERE playerId = :playerId")
.bind("playerId", playerId)
.map(row -> row.getColumn("balance", Integer.class))
.findOne()
.orElse(0));
}
@Override
public void setCoins(UUID playerId, int amount) {
checkArgument(amount >= 0, "amount cannot be negative");
jdbi.withHandle(h -> h
.createUpdate("""
INSERT INTO economy (playerId, balance)
VALUES (:playerId, :amount)
ON CONFLICT (playerId)
DO UPDATE
SET balance = :amount
WHERE economy.playerId = :playerId
""")
.bind("playerId", playerId)
.bind("amount", amount)
.execute());
}
@Override
public void depositCoins(UUID playerId, int amount) {
checkArgument(amount >= 0, "amount cannot be negative");
jdbi.withHandle(h -> h.
createUpdate("""
INSERT INTO economy (playerId, balance)
VALUES (:playerId, :amount)
ON CONFLICT (playerId)
DO UPDATE
SET balance = economy.balance + EXCLUDED.balance
WHERE economy.playerId = :playerId
""")
.bind("playerId", playerId)
.bind("amount", amount)
.execute());
}
@Override
public boolean tryWithdrawCoins(UUID playerId, int amount) {
checkArgument(amount >= 0, "amount cannot be negative");
return jdbi.withHandle(h -> h
.createUpdate("""
UPDATE economy
SET balance = balance - :amount
WHERE playerId = :playerId
""")
.bind("playerId", playerId)
.bind("amount", amount)
.execute() > 0);
}
}

View file

@ -8,13 +8,16 @@ import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.friendship.FriendshipImpl;
import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl;
import de.kentoj.scrowlib.friends.FriendRequestService;
import de.kentoj.scrowlib.friends.FriendshipService;
import de.kentoj.scrowlib.friends.friendship.FriendshipImpl;
import de.kentoj.scrowlib.friends.request.FriendRequestImpl;
import de.kentoj.scrow.bukkit.scheduler.Task;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -31,6 +34,8 @@ public class FriendAddLiteral implements CommandExecutor<CommandSender> {
private final CommandNode<CommandSender> literal;
private final CommandArgument<CommandSender, OfflinePlayer> playerArg;
private final ScrowMessageStyle style;
private final FriendRequestService requestService = ScrowAPI.getFriendRequestService();
private final FriendshipService friendsService = ScrowAPI.getFriendshipService();
public FriendAddLiteral(ScrowMessageStyle style) {
this.style = style;
@ -54,30 +59,28 @@ public class FriendAddLiteral implements CommandExecutor<CommandSender> {
}
private void sendRequest(Player self, OfflinePlayer other) {
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.saveFriendRequest(new FriendRequestImpl(self.getUniqueId(), other.getUniqueId())))
Tasks.runAsync(() ->
requestService.saveFriendRequest(new FriendRequestImpl(self.getUniqueId(), other.getUniqueId())))
.runSync(() -> {
self.sendMessage(style.ok("Successfully sent " + other.getName() + " a friend request."));
var otherPlayer = other.getPlayer();
if (otherPlayer != null)
otherPlayer.sendMessage(style.ok(" wants to be friends with you. Use '/f add " + self.getName() + "' to accept."));
});
var msg = Component.text(self.getName() + " sent you a friend request.")
.append(FriendCommand.friendRequestOptions(self.getName(), true));
ScrowAPI.getPlayer(other).sendMessage(style.ok(msg));
})
.await();
}
private void acceptRequest(Player self, OfflinePlayer other) {
Tasks.runAsync(() -> Tasks.awaitAll(
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.deleteFriendRequest(self.getUniqueId(), other.getUniqueId())),
Tasks.runAsync(() -> ScrowAPI.getFriendRequestService()
.deleteFriendRequest(other.getUniqueId(), self.getUniqueId())),
Tasks.runAsync(() -> ScrowAPI.getFriendshipService()
.saveFriendship(new FriendshipImpl(self.getUniqueId(), other.getUniqueId(), Instant.now()))
Tasks.runAsync(() -> requestService.deleteFriendRequest(self.getUniqueId(), other.getUniqueId())),
Tasks.runAsync(() -> requestService.deleteFriendRequest(other.getUniqueId(), self.getUniqueId())),
Tasks.runAsync(() -> friendsService.saveFriendship(new FriendshipImpl(self.getUniqueId(), other.getUniqueId(), Instant.now()))
)))
.runSync(() -> {
self.sendMessage(style.ok("You are now friends with " + other.getName() + "."));
if (other.getPlayer() != null)
other.getPlayer().sendMessage(style.ok("You are now friends with " + self.getName() + "."));
});
ScrowAPI.getPlayer(other).sendMessage(style.ok("You are now friends with " + self.getName() + "."));
})
.await();
}
private Result<AddAction, String> findAction(Player self, OfflinePlayer other) {
@ -92,27 +95,24 @@ public class FriendAddLiteral implements CommandExecutor<CommandSender> {
if (isAlreadyFriendTask.await())
return Results.failure("You are already friends with " + other.getName() + ".");
if (isAlreadySent.await())
return Results.failure("You've already sent " + other + " a friend request.");
return Results.failure("You've already sent " + other.getName() + " a friend request.");
if (isShouldAccept.await())
return Results.success(AddAction.ACCEPT_REQUEST);
return Results.success(AddAction.SEND_REQUEST);
}
private static Task<Boolean> checkShouldAccept(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequest(other.getUniqueId(), self.getUniqueId()))
private Task<Boolean> checkShouldAccept(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() -> requestService.getFriendRequest(other.getUniqueId(), self.getUniqueId()))
.mapSync(Objects::nonNull);
}
private static Task<Boolean> checkAlreadyFriends(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendshipService().getFriendship(self.getUniqueId(), other.getUniqueId()))
private Task<Boolean> checkAlreadyFriends(Player self, OfflinePlayer other) {
return Tasks.supplyAsync(() -> friendsService.getFriendship(self.getUniqueId(), other.getUniqueId()))
.mapSync(Objects::nonNull);
}
private static Task<Boolean> checkAlreadySent(UUID self, UUID other) {
return Tasks.supplyAsync(() ->
ScrowAPI.getFriendRequestService().getFriendRequest(self, other))
private Task<Boolean> checkAlreadySent(UUID self, UUID other) {
return Tasks.supplyAsync(() -> requestService.getFriendRequest(self, other))
.mapSync(Objects::nonNull);
}

View file

@ -5,18 +5,37 @@ import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender;
@NoArgsConstructor(access = AccessLevel.NONE)
public class FriendCommand {
public static RootCommandNode<CommandSender> create() {
var style = new ScrowMessageStyle("Friends", NamedTextColor.GREEN);
var style = new ScrowMessageStyle("Friends", NamedTextColor.AQUA);
RootCommandNode<CommandSender> rootNode = CommandNode.rootNode(style, "f", "friend");
rootNode.addLiteral(new FriendListLiteral(style).getLiteral());
rootNode.addLiteral(new FriendAddLiteral(style).getLiteral());
rootNode.addLiteral(new FriendRemoveLiteral(style).getRootNode());
rootNode.addLiteral(new FriendRemoveLiteral(style).getLiteral());
rootNode.addLiteral(new FriendRequestDeclineLiteral(style).getLiteral());
rootNode.addLiteral(new FriendRequestsLiteral(style).getLiteral());
return rootNode;
}
public static Component friendRequestOptions(String requestSenderName, boolean runInstantly) {
return Component.text(" ")
.append(Component.text("[accept]")
.hoverEvent(HoverEvent.showText(Component.text("click to accept")))
.clickEvent(ClickEvent.runCommand("/f add " + requestSenderName))
.color(NamedTextColor.GREEN))
.append(Component.text(" "))
.append(Component.text("[decline]")
.hoverEvent(HoverEvent.showText(Component.text("click to decline")))
.clickEvent(runInstantly
? ClickEvent.runCommand("/f decline " + requestSenderName)
: ClickEvent.suggestCommand("/f decline " + requestSenderName))
.color(NamedTextColor.RED));
}
}

View file

@ -20,7 +20,7 @@ import org.bukkit.entity.Player;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static java.util.Objects.*;
import static java.util.Objects.requireNonNull;
import static net.kyori.adventure.text.Component.text;
public class FriendListLiteral implements CommandExecutor<CommandSender> {
@ -61,14 +61,13 @@ public class FriendListLiteral implements CommandExecutor<CommandSender> {
private Component formatList(List<OfflinePlayer> friends) {
var message = style.ok(MiniMessage.miniMessage().deserialize("Your friends:"));
for (var friend : friends) {
var status = text("(").append(friend.isOnline()
? text("online").color(NamedTextColor.GRAY)
: text("offline").color(NamedTextColor.RED))
.append(text(")"));
var status = MiniMessage.miniMessage().deserialize(friend.isOnline()
? " <gray>(<green>online<gray>)"
: " <gray>(<red>offline<gray>)");
message = message
.appendNewline()
.append(text("").color(NamedTextColor.AQUA))
.append(text(requireNonNull(friend.getName())))
.append(text(" ").color(style.getPrefixColor()))
.append(text(requireNonNull(friend.getName())).color(NamedTextColor.GRAY))
.append(status);
}
return message;

View file

@ -19,16 +19,16 @@ import java.util.concurrent.CompletableFuture;
public class FriendRemoveLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> rootNode;
private final CommandNode<CommandSender> literal;
private final CommandArgument<CommandSender, OfflinePlayer> playerArg;
private final ScrowMessageStyle style;
public FriendRemoveLiteral(ScrowMessageStyle style) {
this.style = style;
this.playerArg = CommandArgument.arg("player", OfflinePlayerArgumentType.getInstance());
this.rootNode = CommandNode.node("remove");
this.rootNode.addArgument(playerArg);
this.rootNode.setExecutor(this);
this.literal = CommandNode.node("remove");
this.literal.addArgument(playerArg);
this.literal.setExecutor(this);
}
@Override

View file

@ -0,0 +1,53 @@
package de.kentoj.scrow.bukkit.friends.command;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrowlib.friends.FriendRequestService;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class FriendRequestDeclineLiteral implements CommandExecutor<CommandSender> {
@Getter
private final CommandNode<CommandSender> literal;
private final CommandArgument<CommandSender, OfflinePlayer> playerArg;
private final ScrowMessageStyle style;
private final FriendRequestService requestService = ScrowAPI.getFriendRequestService();
public FriendRequestDeclineLiteral(ScrowMessageStyle style) {
this.style = style;
playerArg = CommandArgument.arg("player", OfflinePlayerArgumentType.getInstance());
literal = CommandNode.node("reject", "decline");
literal.addArgument(playerArg);
literal.setExecutor(this);
}
@Override
public CompletableFuture<Result<Object, String>> execute(CommandContext<CommandSender> ctx) {
var self = (Player) ctx.getSender();
var other = ctx.getArg(playerArg);
return Tasks.supplyAsync(() ->
requestService.deleteFriendRequest(other.getUniqueId(), self.getUniqueId()))
.<Result<Object, String>>mapSync(deleted -> {
if (!deleted)
return Results.failure("There's no incoming friend request from " + other.getName() + ".");
self.sendMessage(style.ok("Friend request declined."));
ScrowAPI.getPlayer(other).sendMessage(style.ok(self.getName() + " has declined your friend request."));
return Results.success(new Object());
}).toFuture();
}
}

View file

@ -6,7 +6,7 @@ import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrowlib.friends.FriendRequest;
import de.kentoj.scrow.bukkit.scheduler.Tasks;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
@ -57,8 +57,9 @@ public class FriendRequestsLiteral implements CommandExecutor<CommandSender> {
for (String requester : requesters) {
message = message
.appendNewline()
.append(text("").color(NamedTextColor.AQUA))
.append(Component.text(requester));
.append(text("").color(style.getPrefixColor()))
.append(Component.text(requester).color(NamedTextColor.GRAY)
.append(FriendCommand.friendRequestOptions(requester, false)));
}
return message;
}

View file

@ -1,20 +0,0 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.time.Instant;
import java.util.UUID;
@Value
@RequiredArgsConstructor
public class FriendshipImpl implements Friendship {
OrderedUUIDPair uuids;
Instant createdAt;
public FriendshipImpl(UUID uuid1, UUID uuid2, Instant createdAt) {
this(new OrderedUUIDPair(uuid1, uuid2), createdAt);
}
}

View file

@ -1,40 +0,0 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.FriendshipService;
import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class InMemoryFriendshipService implements FriendshipService {
private final Map<OrderedUUIDPair, Friendship> friendships = new HashMap<>();
@Override
public Friendship getFriendship(UUID uuid1, UUID uuid2) {
return friendships.get(new OrderedUUIDPair(uuid1, uuid2));
}
@Override
public List<Friendship> getFriendships(UUID playerId) {
return friendships.entrySet().stream()
.filter(ent ->
ent.getKey().getFirst().equals(playerId) || ent.getKey().getSecond().equals(playerId)
)
.map(Map.Entry::getValue)
.toList();
}
@Override
public boolean saveFriendship(Friendship friendship) {
return friendships.putIfAbsent(friendship.getUuids(), friendship) == null;
}
@Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
return friendships.remove(new OrderedUUIDPair(uuid1, uuid2)) != null;
}
}

View file

@ -1,99 +0,0 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.FriendshipService;
import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair;
import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.statement.StatementContext;
import org.jetbrains.annotations.Nullable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class PostgresFriendshipService implements FriendshipService {
private final Jdbi jdbi;
public PostgresFriendshipService() {
checkNotNull(ScrowAPI.getJdbi());
jdbi = ScrowAPI.getJdbi();
jdbi.useHandle(h -> h.execute("""
CREATE TABLE IF NOT EXISTS friendships (
first_uuid UUID NOT NULL,
second_uuid UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (first_uuid, second_uuid),
CONSTRAINT friendship_no_self_friendship
CHECK (first_uuid <> second_uuid)
)
"""));
}
@Override
public @Nullable Friendship getFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at
FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.map(this::parseFriendship)
.findFirst().orElse(null));
}
@Override
public List<Friendship> getFriendships(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at
FROM friendships
WHERE first_uuid = :playerId OR second_uuid = :playerId
""")
.bind("playerId", playerId)
.map(this::parseFriendship)
.list());
}
@Override
public boolean saveFriendship(Friendship friendship) {
return jdbi.withHandle(h -> h.createUpdate("""
INSERT INTO friendships (first_uuid, second_uuid, created_at)
VALUES (:first, :second, :createdAt)
ON CONFLICT (first_uuid, second_uuid) DO NOTHING
""")
.bind("first", friendship.getUuids().getFirst())
.bind("second", friendship.getUuids().getSecond())
.bind("createdAt", friendship.getCreatedAt())
.execute() > 0);
}
@Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
return jdbi.withHandle(h -> h.createUpdate("""
DELETE FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.execute() > 0);
}
private Friendship parseFriendship(ResultSet rs, StatementContext __) throws SQLException {
return new FriendshipImpl(
rs.getObject("first_uuid", UUID.class),
rs.getObject("second_uuid", UUID.class),
rs.getObject("created_at", Instant.class)
);
}
}

View file

@ -0,0 +1,19 @@
package de.kentoj.scrow.bukkit.friends.notification;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrowlib.messaging.MessageBroker;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import java.util.UUID;
public class NotificationListener {
private final MessageBroker messageBroker = ScrowAPI.getMessageBroker();
public NotificationListener() {
messageBroker.subscribe("event.player.connect", doc -> {
var player= Bukkit.getPlayer(doc.get("playerId", UUID.class));
});
}
}

View file

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

View file

@ -1,46 +0,0 @@
package de.kentoj.scrow.bukkit.friends.request;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequestService;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class InMemoryFriendRequestService implements FriendRequestService {
private final Set<FriendRequest> requests = new HashSet<>();
@Override
public List<FriendRequest> getFriendRequestsTo(UUID playerId) {
return requests.stream()
.filter(req -> req.getTo().equals(playerId))
.toList();
}
@Override
public List<FriendRequest> getFriendRequestsFrom(UUID playerId) {
return requests.stream()
.filter(req -> req.getFrom().equals(playerId))
.toList();
}
@Override
public FriendRequest getFriendRequest(UUID from, UUID to) {
return requests.stream()
.filter(req -> req.getFrom().equals(from) && req.getTo().equals(to))
.findFirst()
.orElse(null);
}
@Override
public boolean saveFriendRequest(FriendRequest friendRequest) {
return requests.add(friendRequest);
}
@Override
public boolean deleteFriendRequest(UUID from, UUID to) {
return requests.removeIf(req -> req.getFrom().equals(from) && req.getTo().equals(to));
}
}

View file

@ -1,108 +0,0 @@
package de.kentoj.scrow.bukkit.friends.request;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequestService;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.statement.StatementContext;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class PostgresFriendRequestService implements FriendRequestService {
private final Jdbi jdbi;
public PostgresFriendRequestService() {
checkNotNull(ScrowAPI.getJdbi());
jdbi = ScrowAPI.getJdbi();
jdbi.useHandle(h -> h.execute("""
CREATE TABLE IF NOT EXISTS friendrequests (
from_uuid UUID NOT NULL,
to_uuid UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (from_uuid, to_uuid),
CONSTRAINT friendship_no_self_request
CHECK (from_uuid <> to_uuid)
)
"""));
}
@Override
public @Nullable FriendRequest getFriendRequest(UUID from, UUID to) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friendrequests
WHERE from_uuid = :from AND to_uuid = :to
""")
.bind("from", from)
.bind("to", to)
.map(this::parseFriendRequest)
.findFirst()
.orElse(null));
}
@Override
public boolean saveFriendRequest(FriendRequest friendRequest) {
return jdbi.withHandle(h -> h.createUpdate("""
INSERT INTO friendrequests (from_uuid, to_uuid, created_at)
VALUES (:from, :to, :createdAt)
""")
.bind("from", friendRequest.getFrom())
.bind("to", friendRequest.getTo())
.bind("createdAt", friendRequest.getCreatedAt())
.execute() > 0);
}
@Override
public List<FriendRequest> getFriendRequestsTo(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friendrequests
WHERE to_uuid = :to
""")
.bind("to", playerId)
.map(this::parseFriendRequest)
.collectIntoList());
}
@Override
public List<FriendRequest> getFriendRequestsFrom(UUID playerId) {
return jdbi.withHandle(h -> h.createQuery("""
SELECT *
FROM friendrequests
WHERE from_uuid = :from
""")
.bind("from", playerId)
.map(this::parseFriendRequest)
.collectIntoList());
}
@Override
public boolean deleteFriendRequest(UUID from, UUID to) {
return jdbi.withHandle(h -> h.createUpdate("""
DELETE
FROM friendrequests
WHERE from_uuid = :from AND to_uuid = :to
""")
.bind("from", from)
.bind("to", to)
.execute() > 0);
}
private FriendRequest parseFriendRequest(ResultSet rs, StatementContext __) throws SQLException {
return new FriendRequestImpl(
rs.getObject("from_uuid", UUID.class),
rs.getObject("to_uuid", UUID.class),
rs.getObject("created_at", Instant.class)
);
}
}

View file

@ -47,7 +47,7 @@ public class PlayCommand implements CommandExecutor<CommandSender> {
return Results.failure("No instance for " + gamemode + " found.");
return Results.ofCallable(() -> {
ScrowAPI.getPlayerManager().sendPlayer(player.getUniqueId(), instance.getHandle());
ScrowAPI.getPlayer(player).sendToInstance(instance.getHandle());
return new Object();
}).mapFailure(Exception::getMessage);
}).toFuture();

View file

@ -0,0 +1,61 @@
package de.kentoj.scrow.bukkit.network;
import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bson.Document;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import java.util.function.BiConsumer;
@RequiredArgsConstructor
public class NetworkPlayerWatchdog {
private static final MiniMessage MM = MiniMessage.miniMessage();
public void listen() {
handle("player.sendMessage", (player, doc) -> {
var msg = MM.deserialize(doc.getString("message"));
player.sendMessage(msg);
});
// TODO test methods below
handle("player.sendActionBar", (player, doc) -> {
var msg = MM.deserialize(doc.getString("message"));
player.sendActionBar(msg);
});
handle("player.playSound", (player, doc) ->
player.playSound(toSound(doc)));
handle("player.playSound1", (player, doc) ->
player.playSound(toSound(doc), doc.getDouble("x"), doc.getDouble("y"), doc.getDouble("z")));
handle("player.stopSound", (player, doc) ->
player.stopSound(toSound(doc)));
}
private @NotNull Sound toSound(Document doc) {
@SuppressWarnings("PatternValidation")
var name = Key.key("namespace", doc.getString("name"));
var source = Sound.Source.valueOf(doc.getString("source"));
return Sound.sound(
name,
source,
doc.getDouble("volume").floatValue(),
doc.getDouble("pitch").floatValue());
}
private void handle(String subject, BiConsumer<Player, Document> handler) {
ScrowAPI.getMessageBroker().subscribe(subject, doc -> {
var player = Bukkit.getPlayer(doc.get("playerId", UUID.class));
if (player != null)
handler.accept(player, doc);
});
}
}

View file

@ -1,24 +0,0 @@
package de.kentoj.scrow.bukkit.playermanager;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.misc.PlayerManager;
import de.kentoj.scrowlib.messaging.MessageBroker;
import de.kentoj.scrowlib.utils.ResultUtils;
import lombok.RequiredArgsConstructor;
import org.bson.Document;
import java.util.UUID;
@RequiredArgsConstructor
public class PlayerManagerImpl implements PlayerManager {
private final MessageBroker messageBroker = ScrowAPI.getMessageBroker();
@Override
public void sendPlayer(UUID playerId, String instanceHandle) {
var result = messageBroker.request("players.send", new Document()
.append("playerId", playerId)
.append("handle", instanceHandle));
ResultUtils.unwrap(result, "failed to send player to " + instanceHandle);
}
}