diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendsService.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendsService.java index 060dc50..202475b 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendsService.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/FriendsService.java @@ -27,7 +27,5 @@ public interface FriendsService { /** * @param from id of player sending the friend request * @param to id of the player the initiator wants to befriend - * @throws FriendRequestUsageException */ - Mono<@NotNull Friendship> sendFriendRequest(UUID from, UUID to); } diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/OrderedUUIDPair.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/OrderedUUIDPair.java index e472f73..a9450fd 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/OrderedUUIDPair.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/friends/OrderedUUIDPair.java @@ -11,7 +11,7 @@ public class OrderedUUIDPair { private final UUID second; public OrderedUUIDPair(UUID uuid1, UUID uuid2) { - Preconditions.checkArgument(uuid1 != uuid2); + Preconditions.checkArgument(uuid1 != uuid2, "UUIDPair may consist of identical UUIDs"); if (uuid1.compareTo(uuid2) < 0) { this.first = uuid1; this.second = uuid2; diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java index 2fe4eb7..023cbee 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java @@ -3,9 +3,12 @@ package de.kentoj.scrow.bukkit; import de.kentoj.scrow.bukkit.economy.CoinsCommand; import de.kentoj.scrow.bukkit.friends.command.FriendCommand; import de.kentoj.scrow.bukkit.friends.friendship.MongoFriendshipDAO; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl; +import de.kentoj.scrow.bukkit.friends.request.MongoFriendRequestDAO; import org.bukkit.plugin.java.JavaPlugin; import reactor.core.publisher.Mono; +import java.time.Instant; import java.util.UUID; public class CoreImplPlugin extends JavaPlugin { @@ -14,9 +17,13 @@ public class CoreImplPlugin extends JavaPlugin { public void onEnable() { ScrowAPISurface.initScrowAPI(this, true); + assert ScrowAPI.getDatabase() != null; + var friendshipDAO = new MongoFriendshipDAO(ScrowAPI.getDatabase()); + var friendRequestsDAO = new MongoFriendRequestDAO(ScrowAPI.getDatabase()); + { ScrowAPI.getCommandManager().register(new CoinsCommand().getRootNode()); - ScrowAPI.getCommandManager().register(new FriendCommand(new MongoFriendshipDAO(ScrowAPI.getDatabase())).getRootNode()); + ScrowAPI.getCommandManager().register(new FriendCommand(friendshipDAO, friendRequestsDAO).getRootNode()); } { @@ -34,6 +41,20 @@ public class CoreImplPlugin extends JavaPlugin { .doOnNext(a -> System.out.println("coins: " + a)) .block(); } + + { + friendRequestsDAO.saveFriendRequest(new FriendRequestImpl( + UUID.fromString("7fca8ec5-c855-4547-aed9-21a20b31617c") /* logan */, + UUID.fromString("3218c49a-b80d-49ae-a42e-4496141eb2b1") /* kento2 */, + Instant.now() + )).block(); + + friendRequestsDAO.saveFriendRequest(new FriendRequestImpl( + UUID.fromString("3218c49a-b80d-49ae-a42e-4496141eb2b1") /* kento2 */, + UUID.fromString("3218c49a-b80d-49ae-a42e-4496141eb2b1") /* kento2 */, + Instant.now() + )); + } } @Override diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/BukkitExecutorService.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/BukkitExecutorService.java new file mode 100644 index 0000000..eeb47e8 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/BukkitExecutorService.java @@ -0,0 +1,86 @@ +package de.kentoj.scrow.bukkit.friends.command; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.*; + +public class BukkitExecutorService implements ExecutorService { + + private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + + public Future io(Callable task) { + return executor.submit(task); + } + + @Override + public void shutdown() { + executor.shutdown(); + } + + @Override + public @NotNull List shutdownNow() { + return executor.shutdownNow(); + } + + @Override + public boolean isShutdown() { + return executor.isShutdown(); + } + + @Override + public boolean isTerminated() { + return executor.isTerminated(); + } + + @Override + public boolean awaitTermination(long l, @NotNull TimeUnit timeUnit) throws InterruptedException { + return executor.awaitTermination(l, timeUnit); + } + + @Override + public @NotNull Future submit(@NotNull Callable callable) { + return executor.submit(callable); + } + + @Override + public @NotNull Future submit(@NotNull Runnable runnable, T t) { + return executor.submit(runnable, t); + } + + @Override + public @NotNull Future submit(@NotNull Runnable runnable) { + return executor.submit(runnable); + } + + @Override + public @NotNull List> invokeAll(@NotNull Collection> collection) throws InterruptedException { + return executor.invokeAll(collection); + } + + @Override + public @NotNull List> invokeAll(@NotNull Collection> collection, long l, @NotNull TimeUnit timeUnit) throws InterruptedException { + return executor.invokeAll(collection, l, timeUnit); + } + + @Override + public @NotNull T invokeAny(@NotNull Collection> collection) throws InterruptedException, ExecutionException { + return (T) executor.invokeAll(collection); + } + + @Override + public T invokeAny(@NotNull Collection> collection, long l, @NotNull TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { + return (T) executor.invokeAll(collection, l, timeUnit); + } + + @Override + public void close() { + executor.close(); + } + + @Override + public void execute(@NotNull Runnable runnable) { + executor.execute(runnable); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendAddLiteral.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendAddLiteral.java new file mode 100644 index 0000000..2d22e06 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendAddLiteral.java @@ -0,0 +1,29 @@ +package de.kentoj.scrow.bukkit.friends.command; + +import de.kentoj.kencommandapi.BukkitCommandContext; +import de.kentoj.kencommandapi.api.structure.node.CommandNode; +import de.kentoj.kencommandapi.type.OfflinePlayerArgumentType; +import de.kentoj.scrow.bukkit.ScrowAPI; +import de.kentoj.scrow.bukkit.friends.command.handler.FriendAddHandler; +import de.kentoj.scrow.bukkit.friends.friendship.FriendshipDAO; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestDAO; +import lombok.Getter; +import org.bukkit.command.CommandSender; + +public class FriendAddLiteral { + + @Getter + private final CommandNode node; + + public FriendAddLiteral(FriendshipDAO friendshipDAO, FriendRequestDAO friendRequestDAO) { + var cmdManager = ScrowAPI.getCommandManager(); + var playerArg = cmdManager.createArgument("player", OfflinePlayerArgumentType.getInstance()); + + this.node = cmdManager.createNode("add"); + this.node.addArgument(playerArg); + this.node.setExecutor(ctx -> { + var handler = new FriendAddHandler(ctx.getPlayerSender(), ctx.getArg(playerArg), friendshipDAO, friendRequestDAO); + handler.handle(); + }); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java index 865fcfd..ee47467 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java @@ -4,6 +4,7 @@ import de.kentoj.kencommandapi.BukkitCommandContext; import de.kentoj.kencommandapi.api.structure.node.CommandNode; import de.kentoj.scrow.bukkit.ScrowAPI; import de.kentoj.scrow.bukkit.friends.friendship.FriendshipDAO; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestDAO; import lombok.Getter; import org.bukkit.command.CommandSender; @@ -12,8 +13,10 @@ public class FriendCommand { @Getter private final CommandNode rootNode; - public FriendCommand(FriendshipDAO friendshipDAO) { + public FriendCommand(FriendshipDAO friendshipDAO, FriendRequestDAO friendRequestDAO) { this.rootNode = ScrowAPI.getCommandManager().createNode("friends", "f", "friend"); rootNode.addLiteral(new FriendListLiteral(friendshipDAO).getNode()); + rootNode.addLiteral(new FriendAddLiteral(friendshipDAO, friendRequestDAO).getNode()); + rootNode.addLiteral(new FriendRequestsLiteral(friendRequestDAO).getNode()); } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java index 11b5628..061ec5f 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendListLiteral.java @@ -25,16 +25,16 @@ public class FriendListLiteral { var ownUid = ctx.getPlayerSender().getUniqueId(); ctx.getPlayerSender().sendMessage("friends:"); friendshipDAO.getFriendships(ownUid) - .publishOn(ScrowAPI.getMinecraftScheduler()) - .subscribeOn(Schedulers.boundedElastic()) .map(friendship -> { var friendPlayerUid = friendship.getUuids().getOther(ownUid); var friendPlayerName = Bukkit.getOfflinePlayer(friendPlayerUid).getName(); return "- " + friendPlayerName; - }).doOnNext(line -> { - ctx.getPlayerSender().sendMessage(line); - }).doOnComplete(() -> { - ctx.getPlayerSender().sendMessage("EOF"); - }); + }) + .doOnNext(line -> ctx.getSender().sendMessage(line)) + .doOnComplete(() -> ctx.getSender().sendMessage("EOF")) + + .publishOn(ScrowAPI.getMinecraftScheduler()) + .subscribeOn(Schedulers.boundedElastic()) + .subscribe(); } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRequestsLiteral.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRequestsLiteral.java new file mode 100644 index 0000000..e5f985a --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendRequestsLiteral.java @@ -0,0 +1,38 @@ +package de.kentoj.scrow.bukkit.friends.command; + +import de.kentoj.kencommandapi.BukkitCommandContext; +import de.kentoj.kencommandapi.api.structure.node.CommandNode; +import de.kentoj.scrow.bukkit.ScrowAPI; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestDAO; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import reactor.core.scheduler.Schedulers; + +public class FriendRequestsLiteral { + + @Getter + private final CommandNode node; + private final FriendRequestDAO friendRequestDAO; + + public FriendRequestsLiteral(FriendRequestDAO friendRequestDAO) { + this.friendRequestDAO = friendRequestDAO; + this.node = ScrowAPI.getCommandManager().createNode("requests", "list-requests"); + node.setExecutor(this::friendRequestsList); + } + + private void friendRequestsList(BukkitCommandContext ctx) { + var ownUid = ctx.getPlayerSender().getUniqueId(); + ctx.getPlayerSender().sendMessage("incoming friends requests:"); + friendRequestDAO.getFriendRequestsTo(ownUid) + .publishOn(ScrowAPI.getMinecraftScheduler()) + .subscribeOn(Schedulers.boundedElastic()) + .map(friendRequest -> { + var fromUid = friendRequest.getFrom(); + var fromName = Bukkit.getOfflinePlayer(fromUid).getName(); + return "- " + fromName; + }).doOnNext(line -> ctx.getPlayerSender().sendMessage(line)) + .doOnComplete(() -> ctx.getPlayerSender().sendMessage("EOF")) + .subscribe(); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/handler/FriendAddHandler.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/handler/FriendAddHandler.java new file mode 100644 index 0000000..aab1362 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/handler/FriendAddHandler.java @@ -0,0 +1,97 @@ +package de.kentoj.scrow.bukkit.friends.command.handler; + +import de.kentoj.scrow.bukkit.ScrowAPI; +import de.kentoj.scrow.bukkit.friends.friendship.FriendshipDAO; +import de.kentoj.scrow.bukkit.friends.friendship.FriendshipImpl; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestDAO; +import de.kentoj.scrow.bukkit.friends.request.FriendRequestImpl; +import lombok.RequiredArgsConstructor; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import java.time.Instant; + +@RequiredArgsConstructor +public class FriendAddHandler { + + private final Player self; + private final OfflinePlayer other; + private final FriendshipDAO friendshipDAO; + private final FriendRequestDAO friendRequestDAO; + + public void handle() { + findAction() + .flatMap(this::executeAction) + .subscribeOn(Schedulers.boundedElastic()) + .subscribe(); + } + + private Mono executeAction(AddAction action) { + return switch (action) { + case ERR_ALREADY_FRIENDS -> sendMessage(self, "[friends] You are already friends with " + other.getName()); + case ERR_ALREADY_REQUESTED -> + sendMessage(self, "[friends] You already requested " + other.getName() + " to be friends with you"); + case SEND_REQUEST -> sendRequest(); + case ACCEPT_REQUEST -> acceptRequest(); + }; + } + + private Mono sendRequest() { + return friendRequestDAO.saveFriendRequest(new FriendRequestImpl(self.getUniqueId(), other.getUniqueId(), Instant.now())) + .then(sendMessage(self.getPlayer(), "[friends] Successfully sent " + other.getName() + " a friend request.")) + .then(Mono.fromRunnable(() -> { + var otherPlayer = other.getPlayer(); + if (otherPlayer != null) + sendMessage(otherPlayer, "[friends] " + self.getName() + " wants to be friends with you. Use '/f add " + self.getName() + "' to accept.").subscribe(); + })) + .doOnError(this::handleError); + } + + private Mono acceptRequest() { + return Mono.when( + friendRequestDAO.deleteFriendRequest(self.getUniqueId(), other.getUniqueId()), + friendRequestDAO.deleteFriendRequest(other.getUniqueId(), self.getUniqueId()), + friendshipDAO.saveFriendship(new FriendshipImpl(self.getUniqueId(), other.getUniqueId(), Instant.now())) + ) + .then(sendMessage(self, "[friends] You are now friends with " + other.getName())) + .then(Mono.fromRunnable(() -> { + var otherPlayer = other.getPlayer(); + if (otherPlayer != null) + sendMessage(otherPlayer, "[friends] You are now friends with " + self.getName()).subscribe(); + })) + .doOnError(this::handleError); + } + + private Mono<@NotNull AddAction> findAction() { + return friendshipDAO.getFriendship(self.getUniqueId(), other.getUniqueId()) + .map(f -> AddAction.ERR_ALREADY_FRIENDS) + .switchIfEmpty( + friendRequestDAO.getFriendRequest(self.getUniqueId(), other.getUniqueId()) + .map(r -> AddAction.ERR_ALREADY_REQUESTED) + .switchIfEmpty( + friendRequestDAO.getFriendRequest(other.getUniqueId(), self.getUniqueId()) + .map(r -> AddAction.ACCEPT_REQUEST) + .switchIfEmpty(Mono.just(AddAction.SEND_REQUEST)) + ) + ); + } + + private Mono sendMessage(Player player, String msg) { + return Mono.fromRunnable(() -> player.sendMessage(msg)) + .subscribeOn(ScrowAPI.getMinecraftScheduler()); + } + + private void handleError(Throwable e) { + sendMessage(self.getPlayer(), "FAILED SENDING REQUEST. REPORT THIS BUG. " + e.getMessage()).subscribe(); + } + + private enum AddAction { + ERR_ALREADY_FRIENDS, + ERR_ALREADY_REQUESTED, + SEND_REQUEST, + ACCEPT_REQUEST + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/FriendshipDAO.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/FriendshipDAO.java index 0af008c..6bf1952 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/FriendshipDAO.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/FriendshipDAO.java @@ -1,6 +1,7 @@ package de.kentoj.scrow.bukkit.friends.friendship; import de.kentoj.scrow.bukkit.friends.Friendship; +import de.kentoj.scrow.bukkit.friends.OrderedUUIDPair; import org.jetbrains.annotations.NotNull; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -9,9 +10,11 @@ import java.util.UUID; public interface FriendshipDAO { + Mono<@NotNull Friendship> getFriendship(UUID uuid1, UUID uuid2); + Flux<@NotNull Friendship> getFriendships(UUID playerId); - /** + /* * @return True if friendship saved, false if friendship already exists */ Mono<@NotNull Boolean> saveFriendship(Friendship playerIds); diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/MongoFriendshipDAO.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/MongoFriendshipDAO.java index 9b0ec1c..02e15ed 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/MongoFriendshipDAO.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/friendship/MongoFriendshipDAO.java @@ -26,14 +26,26 @@ public class MongoFriendshipDAO implements FriendshipDAO { public MongoFriendshipDAO(MongoDatabase database) { this.database = database; - Mono.from(database.createCollection(COLLECTION_NAME)).block(); - Mono.from(collection().createIndex( - Indexes.compoundIndex( - Indexes.ascending("first"), - Indexes.ascending("second") - ), - new IndexOptions().unique(true) - )).block(); + Mono.when( + database.createCollection(COLLECTION_NAME), + collection().createIndex( + Indexes.compoundIndex( + Indexes.ascending("first"), + Indexes.ascending("second") + ), + new IndexOptions().unique(true)) + ).block(); + } + + @Override + public Mono<@NotNull Friendship> getFriendship(UUID uuid1, UUID uuid2) { + var uuids = new OrderedUUIDPair(uuid1, uuid2); + return Mono.from(collection() + .find(Filters.or( + Filters.eq("first", uuids.getFirst()), + Filters.eq("second", uuids.getSecond()) + ))) + .map(this::fromDocument); } @Override @@ -73,14 +85,15 @@ public class MongoFriendshipDAO implements FriendshipDAO { return database.getCollection(COLLECTION_NAME); } - private @NotNull Friendship fromDocument(Document doc) { - var first = UUID.fromString(doc.getString("first")); - var second = UUID.fromString(doc.getString("second")); - var createdAt = doc.getDate("createdAt").toInstant(); - return new FriendshipImpl(first, second, createdAt); + private Friendship fromDocument(Document doc) { + return new FriendshipImpl( + doc.get("first", UUID.class), + doc.get("second", UUID.class), + doc.getDate("createdAt").toInstant() + ); } - private @NotNull Document toDocument(Friendship friendship) { + private Document toDocument(Friendship friendship) { return new Document() .append("first", friendship.getUuids().getFirst().toString()) .append("second", friendship.getUuids().getSecond().toString()) diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/BsonFriendRequest.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/BsonFriendRequest.java deleted file mode 100644 index e99e4ed..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/BsonFriendRequest.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.kentoj.scrow.bukkit.friends.request; - -import com.google.common.base.Preconditions; -import de.kentoj.scrow.bukkit.friends.FriendRequest; -import lombok.Value; -import org.bson.codecs.pojo.annotations.BsonCreator; -import org.bson.codecs.pojo.annotations.BsonProperty; -import org.bson.types.ObjectId; - -import java.time.Instant; -import java.util.UUID; - -@Value -public class BsonFriendRequest implements FriendRequest { - ObjectId requestId; - UUID from; - UUID to; - Instant createdAt; - - @BsonCreator - public BsonFriendRequest( - @BsonProperty("_id") ObjectId requestId, - @BsonProperty("from") UUID from, - @BsonProperty("to") UUID to, - @BsonProperty("createdAt") Instant createdAt - ) { - Preconditions.checkArgument(from != to, "friend-request can not have same 'from' and 'to' value"); - this.requestId = requestId; - this.from = from; - this.to = to; - this.createdAt = createdAt; - } - - public BsonFriendRequest(UUID from, UUID to, Instant createdAt) { - this(ObjectId.get(), from, to, createdAt); - } -} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestRepository.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestDAO.java similarity index 67% rename from core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestRepository.java rename to core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestDAO.java index 2302b33..213eed8 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestRepository.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestDAO.java @@ -8,18 +8,18 @@ import reactor.core.publisher.Mono; import java.time.Instant; import java.util.UUID; -public interface FriendRequestRepository { +public interface FriendRequestDAO { - Flux<@NotNull FriendRequest> getIncomingFriendRequests(UUID playerId); + Flux<@NotNull FriendRequest> getFriendRequestsTo(UUID playerId); - Flux<@NotNull FriendRequest> getOutgoingFriendRequests(UUID playerId); + Flux<@NotNull FriendRequest> getFriendRequestsFrom(UUID playerId); Mono<@NotNull FriendRequest> getFriendRequest(UUID from, UUID to); /** * @return True if request saved, false if already existing request found. */ - Mono<@NotNull Boolean> saveFriendRequest(UUID from, UUID to, Instant createdAt); + Mono<@NotNull Boolean> saveFriendRequest(FriendRequest friendRequest); /** * @return True if request deleted, false if none found. diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestImpl.java new file mode 100644 index 0000000..cec7f44 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/FriendRequestImpl.java @@ -0,0 +1,14 @@ +package de.kentoj.scrow.bukkit.friends.request; + +import de.kentoj.scrow.bukkit.friends.FriendRequest; +import lombok.Value; + +import java.time.Instant; +import java.util.UUID; + +@Value +public class FriendRequestImpl implements FriendRequest { + UUID from; + UUID to; + Instant createdAt; +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestDAO.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestDAO.java new file mode 100644 index 0000000..7a32dbb --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestDAO.java @@ -0,0 +1,106 @@ +package de.kentoj.scrow.bukkit.friends.request; + +import com.mongodb.ErrorCategory; +import com.mongodb.MongoWriteException; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.IndexOptions; +import com.mongodb.client.model.Indexes; +import com.mongodb.reactivestreams.client.MongoCollection; +import com.mongodb.reactivestreams.client.MongoDatabase; +import de.kentoj.scrow.bukkit.friends.FriendRequest; +import org.bson.Document; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Date; +import java.util.UUID; + +public class MongoFriendRequestDAO implements FriendRequestDAO { + + private static final String COLLECTION_NAME = "friend-requests"; + + private final MongoDatabase database; + + public MongoFriendRequestDAO(MongoDatabase database) { + this.database = database; + + Mono.when( + database.createCollection(COLLECTION_NAME), + collection().createIndex( + Indexes.compoundIndex( + Indexes.ascending("from"), + Indexes.ascending("to") + ), + new IndexOptions().unique(true) + ) + ).block(); + } + + @Override + public Mono<@NotNull FriendRequest> getFriendRequest(UUID from, UUID to) { + return Mono.from(collection() + .find(Filters.and( + Filters.eq("from", from), + Filters.eq("to", to) + ))) + .map(this::fromDocument); + } + + @Override + public Mono<@NotNull Boolean> saveFriendRequest(FriendRequest friendRequest) { + return Mono.from(collection() + .insertOne(toDocument(friendRequest)) + ) + .map(__ -> true) + .onErrorResume(MongoWriteException.class, e -> + e.getError().getCategory() == ErrorCategory.DUPLICATE_KEY ? + Mono.just(false) : + Mono.error(e) + ); + } + + @Override + public Flux<@NotNull FriendRequest> getFriendRequestsTo(UUID playerId) { + return Flux.from(collection() + .find(Filters.eq("to", playerId))) + .map(this::fromDocument); + } + + @Override + public Flux<@NotNull FriendRequest> getFriendRequestsFrom(UUID playerId) { + return Flux.from(collection() + .find() + .filter(Filters.eq("from", playerId))) + .map(this::fromDocument); + } + + @Override + public Mono<@NotNull Boolean> deleteFriendRequest(UUID from, UUID to) { + return Mono.from(collection() + .deleteOne(Filters.and( + Filters.eq("from", from), + Filters.eq("to", to) + ))) + .map(res -> res.getDeletedCount() > 0); + } + + private MongoCollection collection() { + return database.getCollection(COLLECTION_NAME); + } + + private Document toDocument(FriendRequest req) { + return new Document() + .append("from", req.getFrom()) + .append("to", req.getTo()) + .append("createdAt", Date.from(req.getCreatedAt())); + } + + private FriendRequest fromDocument(Document doc) { + return new FriendRequestImpl( + doc.get("from", UUID.class), + doc.get("to", UUID.class), + doc.getDate("createdAt").toInstant() + ); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestRepository.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestRepository.java deleted file mode 100644 index 9f14cc9..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/request/MongoFriendRequestRepository.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.kentoj.scrow.bukkit.friends.request; - -import com.mongodb.ErrorCategory; -import com.mongodb.MongoWriteException; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.IndexOptions; -import com.mongodb.client.model.Indexes; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; -import de.kentoj.scrow.bukkit.friends.FriendRequest; -import org.jetbrains.annotations.NotNull; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.time.Instant; -import java.util.UUID; - -public class MongoFriendRequestRepository implements FriendRequestRepository { - - private static final String COLLECTION_NAME = "friend-requests"; - - private final MongoDatabase database; - - public MongoFriendRequestRepository(MongoDatabase database) { - this.database = database; - - database.createCollection(COLLECTION_NAME); - collection().createIndex( - Indexes.compoundIndex( - Indexes.ascending("from"), - Indexes.ascending("to") - ), - new IndexOptions().unique(true) - ); - } - - @Override - public Mono<@NotNull FriendRequest> getFriendRequest(UUID from, UUID to) { - return Mono.from(collection() - .find(Filters.and( - Filters.eq("from", from), - Filters.eq("to", to) - )) - .first()); - } - - @Override - public Mono<@NotNull Boolean> saveFriendRequest(UUID from, UUID to, Instant createdAt) { - var request = new BsonFriendRequest(from, to, createdAt); - return Mono.from(collection() - .insertOne(request) - ) - .map(__ -> true) - .onErrorResume(e -> { - if (!(e instanceof MongoWriteException)) return Mono.error(e); - if (((MongoWriteException) e).getError().getCategory() != ErrorCategory.DUPLICATE_KEY) - return Mono.error(e); - return Mono.just(false); - }); - } - - @Override - public Flux<@NotNull FriendRequest> getIncomingFriendRequests(UUID playerId) { - return Flux.from(collection().find(Filters.eq("to", playerId))); - } - - @Override - public Flux<@NotNull FriendRequest> getOutgoingFriendRequests(UUID playerId) { - return Flux.from(collection().find(Filters.eq("from", playerId))); - } - - @Override - public Mono<@NotNull Boolean> deleteFriendRequest(UUID from, UUID to) { - return Mono.from(collection() - .deleteOne(Filters.and( - Filters.eq("from", from), - Filters.eq("to", to) - ))) - .map(res -> res.getDeletedCount() > 0); - } - - private MongoCollection collection() { - return database.getCollection(COLLECTION_NAME, BsonFriendRequest.class); - } -}