From 9ac575b952917127640aff61e89ebd874dd7c912 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 08:21:43 +0100 Subject: [PATCH 01/43] feat: Introduce friend management features with backend integration and command support --- .idea/compiler.xml | 6 + .idea/gradle.xml | 3 +- ...urf-friends.surf-friends-fallback.main.iml | 13 -- settings.gradle.kts | 4 +- .../slne/surf/friends/api/SurfFriendsApi.kt | 6 +- .../surf/friends/api/friend/FriendRequest.kt | 10 + .../surf/friends/api/friend/Friendship.kt | 10 + .../surf/friends/api/model/FriendRequest.kt | 23 -- .../slne/surf/friends/api/model/Friendship.kt | 23 -- .../surf/friends/api/player/FriendPlayer.kt | 15 ++ .../surf/friends/api/util/FriendSettings.kt | 8 - .../build.gradle.kts | 0 .../backend}/service/FallbackFriendService.kt | 4 +- .../backend/table/FriendRequestTable.kt | 15 ++ .../backend}/table/FriendSettingsTable.kt | 7 +- .../friends/backend}/table/FriendShipTable.kt | 7 +- .../friends/core/model/CoreFriendRequest.kt | 10 - .../surf/friends/core/model/CoreFriendship.kt | 10 - .../friends/core/pair/CoreFriendSettings.kt | 10 - .../friends/core/service/DatabaseService.kt | 127 ----------- .../core/service/FriendPlayerService.kt | 14 ++ .../friends/core/service/FriendService.kt | 168 +++----------- .../friends/fallback/api/FallbackFriendApi.kt | 70 ------ .../service/FallbackDatabaseService.kt | 209 ------------------ .../fallback/table/FriendRequestTable.kt | 13 -- surf-friends-paper/build.gradle.kts | 19 ++ .../dev/slne/surf/friends/paper/PaperMain.kt | 12 + .../friends/paper/command/FriendCommand.kt | 8 + .../listener/NetworkConnectionListener.kt | 17 ++ .../paper/permission/PermissionRegistry.kt | 19 ++ surf-friends-velocity/build.gradle.kts | 2 +- 31 files changed, 196 insertions(+), 666 deletions(-) delete mode 100644 .idea/modules/surf-friends-fallback/surf-friends.surf-friends-fallback.main.iml create mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt create mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt delete mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/FriendRequest.kt delete mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/Friendship.kt create mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt delete mode 100644 surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/util/FriendSettings.kt rename {surf-friends-fallback => surf-friends-backend}/build.gradle.kts (100%) rename {surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback => surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend}/service/FallbackFriendService.kt (96%) create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt rename {surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback => surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend}/table/FriendSettingsTable.kt (60%) rename {surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback => surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend}/table/FriendShipTable.kt (64%) delete mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendRequest.kt delete mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendship.kt delete mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/pair/CoreFriendSettings.kt delete mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/DatabaseService.kt create mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt delete mode 100644 surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/api/FallbackFriendApi.kt delete mode 100644 surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackDatabaseService.kt delete mode 100644 surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendRequestTable.kt create mode 100644 surf-friends-paper/build.gradle.kts create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 73a50c8..ec3c8f7 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -11,12 +11,18 @@ + + + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 097aafa..9e02b64 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -10,8 +10,9 @@ diff --git a/.idea/modules/surf-friends-fallback/surf-friends.surf-friends-fallback.main.iml b/.idea/modules/surf-friends-fallback/surf-friends.surf-friends-fallback.main.iml deleted file mode 100644 index a376b96..0000000 --- a/.idea/modules/surf-friends-fallback/surf-friends.surf-friends-fallback.main.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - ADVENTURE - - 1 - - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index fdf475a..153d239 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,4 +4,6 @@ plugins { include("surf-friends-api") include("surf-friends-core") include("surf-friends-velocity") -include("surf-friends-fallback") +include("surf-friends-backend") + +include("surf-friends-paper") \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt index 8457622..409442a 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt @@ -1,9 +1,9 @@ package dev.slne.surf.friends.api -import dev.slne.surf.friends.api.model.FriendRequest -import dev.slne.surf.friends.api.model.Friendship +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship import it.unimi.dsi.fastutil.objects.ObjectSet -import java.util.UUID +import java.util.* interface SurfFriendsApi { /** diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt new file mode 100644 index 0000000..efb78fd --- /dev/null +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.api.friend + +import java.time.OffsetDateTime +import java.util.* + +data class FriendRequest( + val senderUuid: UUID, + val receiverUuid: UUID, + val sentAt: OffsetDateTime +) \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt new file mode 100644 index 0000000..fff50aa --- /dev/null +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.api.friend + +import java.time.OffsetDateTime +import java.util.* + +data class Friendship( + val userUuid: UUID, + val friendUuid: UUID, + val createdAt: OffsetDateTime +) \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/FriendRequest.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/FriendRequest.kt deleted file mode 100644 index a402b12..0000000 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/FriendRequest.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.slne.surf.friends.api.model - -import java.util.UUID - -/** - * Represents a friend request between two users. - */ -interface FriendRequest { - /** - * The UUID of the user who sent the friend request. - */ - val senderUuid: UUID - - /** - * The UUID of the user who received the friend request. - */ - val receiverUuid: UUID - - /** - * The timestamp (in milliseconds since epoch) when the friend request was sent. - */ - val sentAt: Long -} \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/Friendship.kt deleted file mode 100644 index 3c54e1e..0000000 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/model/Friendship.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.slne.surf.friends.api.model - -import java.util.UUID - -/** - * Represents a friendship between two users. - */ -interface Friendship { - /** - * The UUID of the user who is part of the friendship. - */ - val userUuid: UUID - - /** - * The UUID of the friend in the friendship. - */ - val friendUuid: UUID - - /** - * The timestamp (in milliseconds since epoch) when the friendship was created. - */ - val createdAt: Long -} \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt new file mode 100644 index 0000000..93afb7a --- /dev/null +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.friends.api.player + +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship +import it.unimi.dsi.fastutil.objects.ObjectSet +import java.util.* + +data class FriendPlayer( + val uuid: UUID, + val name: String, + + val friends: ObjectSet, + val sentFriendRequests: ObjectSet, + val receivedFriendRequests: ObjectSet +) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/util/FriendSettings.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/util/FriendSettings.kt deleted file mode 100644 index 2cfe6cd..0000000 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/util/FriendSettings.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.slne.surf.friends.api.util - -interface FriendSettings { - var announcementsEnabled: Boolean - var soundsEnabled: Boolean - - fun modify(block: FriendSettings.() -> Unit): FriendSettings -} \ No newline at end of file diff --git a/surf-friends-fallback/build.gradle.kts b/surf-friends-backend/build.gradle.kts similarity index 100% rename from surf-friends-fallback/build.gradle.kts rename to surf-friends-backend/build.gradle.kts diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackFriendService.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt similarity index 96% rename from surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackFriendService.kt rename to surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt index afa43a3..8d03dc3 100644 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackFriendService.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt @@ -1,7 +1,7 @@ -package dev.slne.surf.friends.fallback.service +package dev.slne.surf.friends.backend.service import com.google.auto.service.AutoService -import dev.slne.surf.friends.api.model.FriendRequest +import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.core.service.FriendService import dev.slne.surf.friends.core.service.databaseService import net.kyori.adventure.util.Services diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt new file mode 100644 index 0000000..2f6c56e --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt @@ -0,0 +1,15 @@ +package dev.slne.surf.friends.backend.table + +import org.jetbrains.exposed.sql.Table +import java.util.* + +object FriendRequestTable : Table("friend_requests") { + val id = integer("id").autoIncrement() + val senderUuid = + varchar("sender_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) + val receiverUuid = + varchar("receiver_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) + val send_at = long("created_at") + + override val primaryKey = PrimaryKey(id) +} \ No newline at end of file diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendSettingsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt similarity index 60% rename from surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendSettingsTable.kt rename to surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt index 8d0b255..1a85836 100644 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendSettingsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt @@ -1,10 +1,11 @@ -package dev.slne.surf.friends.fallback.table +package dev.slne.surf.friends.backend.table import org.jetbrains.exposed.sql.Table -import java.util.UUID +import java.util.* object FriendSettingsTable : Table("friend_settings") { - val userUuid = varchar("user_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }).uniqueIndex() + val userUuid = + varchar("user_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }).uniqueIndex() var announcementsEnabled = bool("announcements_enabled").default(true) var soundsEnabled = bool("sounds_enabled").default(true) diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendShipTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt similarity index 64% rename from surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendShipTable.kt rename to surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt index 05f4b50..59cbbe8 100644 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendShipTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt @@ -1,12 +1,13 @@ -package dev.slne.surf.friends.fallback.table +package dev.slne.surf.friends.backend.table import org.jetbrains.exposed.sql.Table -import java.util.UUID +import java.util.* object FriendShipTable : Table("friend_ships") { val id = integer("id").autoIncrement() val userUuid = varchar("user_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val friendUuid = varchar("friend_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) + val friendUuid = + varchar("friend_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) val created_at = long("created_at") override val primaryKey = PrimaryKey(id) diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendRequest.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendRequest.kt deleted file mode 100644 index 29f0705..0000000 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slne.surf.friends.core.model - -import dev.slne.surf.friends.api.model.FriendRequest -import java.util.UUID - -data class CoreFriendRequest( - override val senderUuid: UUID, - override val receiverUuid: UUID, - override val sentAt: Long -) : FriendRequest \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendship.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendship.kt deleted file mode 100644 index c3ba36e..0000000 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/model/CoreFriendship.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slne.surf.friends.core.model - -import dev.slne.surf.friends.api.model.Friendship -import java.util.UUID - -data class CoreFriendship( - override val userUuid: UUID, - override val friendUuid: UUID, - override val createdAt: Long -) : Friendship \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/pair/CoreFriendSettings.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/pair/CoreFriendSettings.kt deleted file mode 100644 index 7307269..0000000 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/pair/CoreFriendSettings.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slne.surf.friends.core.pair - -import dev.slne.surf.friends.api.util.FriendSettings - -data class CoreFriendSettings( - override var announcementsEnabled: Boolean = true, - override var soundsEnabled: Boolean = true -) : FriendSettings { - override fun modify(block: FriendSettings.() -> Unit) = this.apply(block) -} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/DatabaseService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/DatabaseService.kt deleted file mode 100644 index 846d1fe..0000000 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/DatabaseService.kt +++ /dev/null @@ -1,127 +0,0 @@ -package dev.slne.surf.friends.core.service - - import dev.slne.surf.friends.api.model.FriendRequest - import dev.slne.surf.friends.api.model.Friendship - import dev.slne.surf.friends.core.pair.CoreFriendSettings - import dev.slne.surf.surfapi.core.api.util.requiredService - import it.unimi.dsi.fastutil.objects.ObjectSet - import java.nio.file.Path - import java.util.UUID - - /** - * Interface for database operations related to friendships and settings. - */ - interface DatabaseService { - - /** - * Establishes a connection to the database. - * - * @param path The path to the database storage location. - */ - fun connect(path: Path) - - /** - * Retrieves the friend list of a user. - * - * @param uuid The UUID of the user. - * @return A list of friendships representing the user's friends. - */ - suspend fun getFriends(uuid: UUID): ObjectSet - - /** - * Retrieves a specific friendship between two users. - * - * @param playerA The UUID of the first user. - * @param playerB The UUID of the second user. - * @return The friendship if it exists, otherwise null. - */ - suspend fun getFriendship(playerA: UUID, playerB: UUID): Friendship? - - /** - * Retrieves a specific friend request between two users. - * - * @param sender The UUID of the sender. - * @param target The UUID of the target user. - * @return The friend request if it exists, otherwise null. - */ - suspend fun getFriendRequest(sender: UUID, target: UUID): FriendRequest? - - /** - * Retrieves the list of sent friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A list of friend requests representing the recipients of the sent requests. - */ - suspend fun getSentFriendRequests(uuid: UUID): ObjectSet - - /** - * Retrieves the list of received friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A list of friend requests representing the senders of the received requests. - */ - suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet - - /** - * Retrieves the friendship settings of a user. - * - * @param uuid The UUID of the user. - * @return A pair representing the user's friendship settings. - */ - suspend fun getFriendSettings(uuid: UUID): CoreFriendSettings - - /** - * Adds a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - * @return The created friendship. - */ - suspend fun addFriendship(uuid: UUID, friend: UUID): Friendship - - /** - * Removes a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - */ - suspend fun removeFriendship(uuid: UUID, friend: UUID) - - /** - * Adds a friend request. - * - * @param sender The UUID of the sender. - * @param receiver The UUID of the receiver. - * @return The created friend request. - */ - suspend fun addFriendRequest(sender: UUID, receiver: UUID): FriendRequest - - /** - * Removes a friend request. - * - * @param sender The UUID of the sender. - * @param receiver The UUID of the receiver. - */ - suspend fun removeFriendRequest(sender: UUID, receiver: UUID) - - /** - * Updates the friendship settings of a user. - * - * @param uuid The UUID of the user. - * @param pair The pair representing the new friendship settings. - * @return The updated friendship settings. - */ - suspend fun updateFriendSettings(uuid: UUID, pair: CoreFriendSettings): CoreFriendSettings - - companion object { - /** - * Instance of the `DatabaseService`. - */ - val INSTANCE = requiredService() - } - } - - /** - * Extension function to retrieve the instance of the `DatabaseService`. - */ - val databaseService get() = DatabaseService.INSTANCE \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt new file mode 100644 index 0000000..1a3fd6e --- /dev/null +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -0,0 +1,14 @@ +package dev.slne.surf.friends.core.service + +import dev.slne.surf.friends.api.player.FriendPlayer +import it.unimi.dsi.fastutil.objects.ObjectSet +import java.util.* + +interface FriendPlayerService { + fun cachePlayer(friendPlayer: FriendPlayer) + fun invalidatePlayer(uuid: UUID) + fun getPlayers(): ObjectSet + + suspend fun loadPlayer(uuid: UUID, name: String): FriendPlayer + suspend fun savePlayer(friendPlayer: FriendPlayer) +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt index 182c234..ce345f5 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt @@ -1,138 +1,34 @@ package dev.slne.surf.friends.core.service - import dev.slne.surf.friends.api.model.FriendRequest - import dev.slne.surf.friends.api.model.Friendship - import dev.slne.surf.surfapi.core.api.util.requiredService - import it.unimi.dsi.fastutil.objects.ObjectSet - import java.util.UUID - - /** - * Service interface for managing friendships and related actions. - */ - interface FriendService { - /** - * Creates a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - * @return The created Friendship object. - */ - suspend fun createFriendship(uuid: UUID, friend: UUID): Friendship - - /** - * Removes a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - */ - suspend fun removeFriendship(uuid: UUID, friend: UUID) - - /** - * Retrieves a specific friendship between two users. - * - * @param playerA The UUID of the first user. - * @param playerB The UUID of the second user. - * @return The Friendship object if it exists, otherwise null. - */ - suspend fun getFriendship(playerA: UUID, playerB: UUID): Friendship? - - /** - * Checks if two users are friends. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the second user. - * @return The Friendship object if they are friends, otherwise null. - */ - suspend fun areFriends(uuid: UUID, friend: UUID): Friendship? - - /** - * Retrieves all friendships of a user. - * - * @param uuid The UUID of the user. - * @return A set of Friendships representing the user's friends. - */ - suspend fun getFriendships(uuid: UUID): ObjectSet - - /** - * Sends a friend request from one user to another. - * - * @param sender The UUID of the sender. - * @param receiver The UUID of the receiver. - * @return The created FriendRequest object. - */ - suspend fun sendFriendRequest(sender: UUID, receiver: UUID): FriendRequest - - /** - * Accepts a friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) - - /** - * Declines a friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun declineFriendRequest(sender: UUID, receiver: UUID) - - /** - * Revokes a sent friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) - - /** - * Retrieves all sent friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A set of FriendRequests representing the recipients of the sent requests. - */ - suspend fun getSentFriendRequests(uuid: UUID): ObjectSet - - /** - * Retrieves all received friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A set of FriendRequests representing the senders of the received requests. - */ - suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet - - /** - * Retrieves a specific friend request between two users. - * - * @param sender The UUID of the sender. - * @param target The UUID of the target user. - * @return The FriendRequest object if it exists, otherwise null. - */ - suspend fun getFriendRequest(sender: UUID, target: UUID): FriendRequest? - - /** - * Toggles announcements for a user. - * - * @param uuid The UUID of the user. - * @return The new status of announcements (true if enabled, false if disabled). - */ - suspend fun toggleAnnouncements(uuid: UUID): Boolean - - /** - * Toggles sounds for a user. - * - * @param uuid The UUID of the user. - * @return The new status of sounds (true if enabled, false if disabled). - */ - suspend fun toggleSounds(uuid: UUID): Boolean - - companion object { - val INSTANCE = requiredService() - } - } - - /** - * Extension function to get the friend service instance. - */ - val friendService get() = FriendService.INSTANCE \ No newline at end of file +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.surfapi.core.api.util.requiredService +import it.unimi.dsi.fastutil.objects.ObjectSet +import java.util.* + +/** + * Service interface for managing friendships and related actions. + */ +interface FriendService { + suspend fun createFriendship(uuid: UUID, friend: UUID): Friendship + suspend fun removeFriendship(uuid: UUID, friend: UUID) + suspend fun getFriendship(playerA: UUID, playerB: UUID): Friendship? + suspend fun areFriends(uuid: UUID, friend: UUID): Friendship? + suspend fun getFriendships(uuid: UUID): ObjectSet + suspend fun sendFriendRequest(sender: UUID, receiver: UUID): FriendRequest + suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) + suspend fun declineFriendRequest(sender: UUID, receiver: UUID) + suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) + suspend fun getSentFriendRequests(uuid: UUID): ObjectSet + suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet + suspend fun getFriendRequest(sender: UUID, target: UUID): FriendRequest? + + companion object { + val INSTANCE = requiredService() + } +} + +/** + * Extension function to get the friend service instance. + */ +val friendService get() = FriendService.INSTANCE \ No newline at end of file diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/api/FallbackFriendApi.kt b/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/api/FallbackFriendApi.kt deleted file mode 100644 index 456ca29..0000000 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/api/FallbackFriendApi.kt +++ /dev/null @@ -1,70 +0,0 @@ -package dev.slne.surf.friends.fallback.api - -import com.google.auto.service.AutoService -import dev.slne.surf.friends.api.SurfFriendsApi -import dev.slne.surf.friends.api.model.FriendRequest -import dev.slne.surf.friends.api.model.Friendship -import dev.slne.surf.friends.core.service.friendService -import it.unimi.dsi.fastutil.objects.ObjectSet -import net.kyori.adventure.util.Services -import java.util.UUID - -@AutoService(SurfFriendsApi::class) -class FallbackFriendApi: SurfFriendsApi, Services.Fallback { - override suspend fun createFriendship( - uuid: UUID, - friend: UUID - ): Friendship { - return friendService.createFriendship(uuid, friend) - } - - override suspend fun removeFriendship(uuid: UUID, friend: UUID) { - friendService.removeFriendship(uuid, friend) - } - - override suspend fun getFriendships(uuid: UUID): ObjectSet { - return friendService.getFriendships(uuid) - } - - override suspend fun sendFriendRequest( - sender: UUID, - receiver: UUID - ): FriendRequest { - return friendService.sendFriendRequest(sender, receiver) - } - - override suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) { - friendService.acceptFriendRequest(sender, receiver) - } - - override suspend fun declineFriendRequest(sender: UUID, receiver: UUID) { - friendService.declineFriendRequest(sender, receiver) - } - - override suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) { - friendService.revokeFriendRequest(sender, receiver) - } - - override suspend fun getSentFriendRequests(uuid: UUID): ObjectSet { - return friendService.getSentFriendRequests(uuid) - } - - override suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet { - return friendService.getReceivedFriendRequests(uuid) - } - - override suspend fun toggleAnnouncements(uuid: UUID): Boolean { - return friendService.toggleAnnouncements(uuid) - } - - override suspend fun toggleSounds(uuid: UUID): Boolean { - return friendService.toggleSounds(uuid) - } - - override suspend fun areFriends( - uuid: UUID, - friend: UUID - ): Friendship? { - return friendService.areFriends(uuid, friend) - } -} \ No newline at end of file diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackDatabaseService.kt b/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackDatabaseService.kt deleted file mode 100644 index 5f8d173..0000000 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/service/FallbackDatabaseService.kt +++ /dev/null @@ -1,209 +0,0 @@ -package dev.slne.surf.friends.fallback.service - -import com.google.auto.service.AutoService -import dev.slne.surf.database.DatabaseManager -import dev.slne.surf.friends.api.model.FriendRequest -import dev.slne.surf.friends.api.model.Friendship -import dev.slne.surf.friends.core.model.CoreFriendRequest -import dev.slne.surf.friends.core.model.CoreFriendship -import dev.slne.surf.friends.core.pair.CoreFriendSettings -import dev.slne.surf.friends.core.service.DatabaseService -import dev.slne.surf.friends.fallback.table.FriendRequestTable -import dev.slne.surf.friends.fallback.table.FriendSettingsTable -import dev.slne.surf.friends.fallback.table.FriendShipTable -import dev.slne.surf.surfapi.core.api.util.toObjectSet -import it.unimi.dsi.fastutil.objects.ObjectSet -import kotlinx.coroutines.Dispatchers -import net.kyori.adventure.util.Services -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction -import org.jetbrains.exposed.sql.transactions.transaction -import org.jetbrains.exposed.sql.upsert -import java.nio.file.Path -import java.util.* - -@AutoService(DatabaseService::class) -class FallbackDatabaseService : DatabaseService, Services.Fallback { - override fun connect(path: Path) { - DatabaseManager(path, path).databaseProvider.connect() - - transaction { - SchemaUtils.create( - FriendShipTable, - FriendRequestTable, - FriendSettingsTable - ) - } - } - - override suspend fun getFriends( - uuid: UUID - ): ObjectSet = newSuspendedTransaction(Dispatchers.IO) { - FriendShipTable.selectAll().where(FriendShipTable.userUuid eq uuid) - .map { - CoreFriendship( - userUuid = it[FriendShipTable.userUuid], - friendUuid = it[FriendShipTable.friendUuid], - createdAt = it[FriendShipTable.created_at] - ) - } - .toObjectSet() - } - - override suspend fun getFriendship( - playerA: UUID, - playerB: UUID - ): Friendship? = newSuspendedTransaction(Dispatchers.IO) { - FriendShipTable.selectAll().where( - (FriendShipTable.userUuid eq playerA) and (FriendShipTable.friendUuid eq playerB) or ( - (FriendShipTable.userUuid eq playerB) and (FriendShipTable.friendUuid eq playerA) - ) - ) - .map { - CoreFriendship( - userUuid = it[FriendShipTable.userUuid], - friendUuid = it[FriendShipTable.friendUuid], - createdAt = it[FriendShipTable.created_at] - ) - } - .firstOrNull() - } - - override suspend fun getFriendRequest( - sender: UUID, - target: UUID - ): FriendRequest? = newSuspendedTransaction(Dispatchers.IO) { - FriendRequestTable.selectAll().where( - (FriendRequestTable.senderUuid eq sender) and (FriendRequestTable.receiverUuid eq target) - ) - .map { - CoreFriendRequest( - senderUuid = it[FriendRequestTable.senderUuid], - receiverUuid = it[FriendRequestTable.receiverUuid], - sentAt = it[FriendRequestTable.send_at] - ) - } - .firstOrNull() - } - - override suspend fun getSentFriendRequests( - uuid: UUID - ): ObjectSet = newSuspendedTransaction(Dispatchers.IO) { - FriendRequestTable.selectAll().where(FriendRequestTable.senderUuid eq uuid) - .map { - CoreFriendRequest( - senderUuid = it[FriendRequestTable.senderUuid], - receiverUuid = it[FriendRequestTable.receiverUuid], - sentAt = it[FriendRequestTable.send_at] - ) - } - .toObjectSet() - } - - override suspend fun getReceivedFriendRequests( - uuid: UUID - ): ObjectSet = newSuspendedTransaction(Dispatchers.IO) { - FriendRequestTable.selectAll().where(FriendRequestTable.receiverUuid eq uuid) - .map { - CoreFriendRequest( - senderUuid = it[FriendRequestTable.senderUuid], - receiverUuid = it[FriendRequestTable.receiverUuid], - sentAt = it[FriendRequestTable.send_at] - ) - } - .toObjectSet() - } - - override suspend fun getFriendSettings( - uuid: UUID - ): CoreFriendSettings = newSuspendedTransaction(Dispatchers.IO) { - FriendSettingsTable.selectAll().where(FriendSettingsTable.userUuid eq uuid) - .map { it.toFriendSettings() } - .firstOrNull() ?: CoreFriendSettings() - } - - override suspend fun addFriendship( - uuid: UUID, - friend: UUID - ): Friendship = newSuspendedTransaction(Dispatchers.IO) { - val current: Long = System.currentTimeMillis() - - FriendShipTable.insert { - it[FriendShipTable.userUuid] = uuid - it[FriendShipTable.friendUuid] = friend - it[FriendShipTable.created_at] = System.currentTimeMillis() - } - - CoreFriendship( - userUuid = uuid, - friendUuid = friend, - createdAt = current - ) - } - - override suspend fun removeFriendship( - uuid: UUID, - friend: UUID - ) = newSuspendedTransaction(Dispatchers.IO) { - FriendShipTable.deleteWhere { - (FriendShipTable.userUuid eq uuid) and (FriendShipTable.friendUuid eq friend) - } - return@newSuspendedTransaction - } - - override suspend fun addFriendRequest( - sender: UUID, - receiver: UUID - ): FriendRequest = newSuspendedTransaction(Dispatchers.IO) { - val current: Long = System.currentTimeMillis() - - FriendRequestTable.insert { - it[FriendRequestTable.senderUuid] = sender - it[FriendRequestTable.receiverUuid] = receiver - it[FriendRequestTable.send_at] = current - } - - CoreFriendRequest( - senderUuid = sender, - receiverUuid = receiver, - sentAt = current - ) - } - - override suspend fun removeFriendRequest( - sender: UUID, - receiver: UUID - ) = newSuspendedTransaction(Dispatchers.IO) { - FriendRequestTable.deleteWhere { - (FriendRequestTable.senderUuid eq sender) and (FriendRequestTable.receiverUuid eq receiver) - } - return@newSuspendedTransaction - } - - override suspend fun updateFriendSettings( - uuid: UUID, - pair: CoreFriendSettings - ): CoreFriendSettings = newSuspendedTransaction(Dispatchers.IO) { - FriendSettingsTable.upsert { - it[FriendSettingsTable.userUuid] = uuid - it[FriendSettingsTable.announcementsEnabled] = pair.announcementsEnabled - it[FriendSettingsTable.soundsEnabled] = pair.soundsEnabled - } - - pair - } - - fun ResultRow.toFriendSettings(): CoreFriendSettings { - return CoreFriendSettings( - announcementsEnabled = this[FriendSettingsTable.announcementsEnabled], - soundsEnabled = this[FriendSettingsTable.soundsEnabled] - ) - } -} \ No newline at end of file diff --git a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendRequestTable.kt b/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendRequestTable.kt deleted file mode 100644 index 6303324..0000000 --- a/surf-friends-fallback/src/main/kotlin/dev/slne/surf/friends/fallback/table/FriendRequestTable.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.fallback.table - -import org.jetbrains.exposed.sql.Table -import java.util.UUID - -object FriendRequestTable : Table("friend_requests") { - val id = integer("id").autoIncrement() - val senderUuid = varchar("sender_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val receiverUuid = varchar("receiver_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val send_at = long("created_at") - - override val primaryKey = PrimaryKey(id) -} \ No newline at end of file diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts new file mode 100644 index 0000000..91ffc2f --- /dev/null +++ b/surf-friends-paper/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("dev.slne.surf.surfapi.gradle.paper-plugin") +} + +surfPaperPluginApi { + mainClass("dev.slne.surf.friends.paper.PaperMain") + generateLibraryLoader(false) + foliaSupported(true) + + withSurfRedis() + withCorePaper() + + authors.add("red") +} + +dependencies { + api(project(":surf-friends-core")) + runtimeOnly(project(":surf-friends-backend")) +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt new file mode 100644 index 0000000..12b7211 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -0,0 +1,12 @@ +package dev.slne.surf.friends.paper + +import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin +import org.bukkit.plugin.java.JavaPlugin + +val plugin get() = JavaPlugin.getPlugin(PaperMain::class.java) + +class PaperMain : SuspendingJavaPlugin() { + override fun onLoad() { + + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt new file mode 100644 index 0000000..955ebcc --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.friends.paper.command + +import dev.jorel.commandapi.kotlindsl.commandAPICommand +import dev.slne.surf.friends.paper.permission.PermissionRegistry + +fun friendCommand() = commandAPICommand("friend") { + withPermission(PermissionRegistry.BASE_COMMAND) +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt new file mode 100644 index 0000000..e384d23 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt @@ -0,0 +1,17 @@ +package dev.slne.surf.friends.paper.listener + +import dev.slne.surf.core.api.common.event.SurfEventHandler +import dev.slne.surf.core.api.common.event.SurfPlayerConnectEvent +import dev.slne.surf.core.api.common.event.SurfPlayerDisconnectEvent + +object NetworkConnectionListener { + @SurfEventHandler + fun onNetworkConnect(event: SurfPlayerConnectEvent) { + + } + + @SurfEventHandler + fun onNetworkDisconnect(event: SurfPlayerDisconnectEvent) { + + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt new file mode 100644 index 0000000..44693ab --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt @@ -0,0 +1,19 @@ +package dev.slne.surf.friends.paper.permission + +import dev.slne.surf.surfapi.bukkit.api.permission.PermissionRegistry + +object PermissionRegistry : PermissionRegistry() { + const val BASE = "surf.friends" + const val BASE_COMMAND = "$BASE.command" + + val COMMAND_FRIEND_REQUEST_SEND = create("$BASE_COMMAND.request.send") + val COMMAND_FRIEND_REQUEST_ACCEPT = create("$BASE_COMMAND.request.accept") + val COMMAND_FRIEND_REQUEST_DECLINE = create("$BASE_COMMAND.request.decline") + val COMMAND_FRIEND_REQUEST_REVOKE = create("$BASE_COMMAND.request.revoke") + val COMMAND_FRIEND_REQUEST_LIST = create("$BASE_COMMAND.request.list") + + val COMMAND_FRIEND_INFO = create("$BASE_COMMAND.info") + val COMMAND_FRIEND_JUMP = create("$BASE_COMMAND.jump") + val COMMAND_FRIEND_REMOVE = create("$BASE_COMMAND.remove") + val COMMAND_FRIEND_LIST = create("$BASE_COMMAND.list") +} \ No newline at end of file diff --git a/surf-friends-velocity/build.gradle.kts b/surf-friends-velocity/build.gradle.kts index 49bd9ce..b91b125 100644 --- a/surf-friends-velocity/build.gradle.kts +++ b/surf-friends-velocity/build.gradle.kts @@ -21,5 +21,5 @@ velocityPluginFile { dependencies { api(project(":surf-friends-core")) - runtimeOnly(project(":surf-friends-fallback")) + runtimeOnly(project(":surf-friends-backend")) } \ No newline at end of file From f47ade4effd9761295aacc7c8e73c68a2285ba4e Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 08:25:01 +0100 Subject: [PATCH 02/43] feat: Update friendship management API and adjust plugin dependencies --- settings.gradle.kts | 6 +- surf-friends-api/build.gradle.kts | 2 +- .../slne/surf/friends/api/SurfFriendsApi.kt | 102 +----------------- .../surf/friends/api/friend/Friendship.kt | 4 +- surf-friends-backend/build.gradle.kts | 8 -- .../backend/service/FallbackFriendService.kt | 79 -------------- .../backend/table/FriendRequestTable.kt | 15 --- .../backend/table/FriendSettingsTable.kt | 13 --- .../friends/backend/table/FriendShipTable.kt | 14 --- surf-friends-core/build.gradle.kts | 2 +- .../friends/core/service/FriendService.kt | 14 +-- surf-friends-paper/build.gradle.kts | 19 ---- .../dev/slne/surf/friends/paper/PaperMain.kt | 12 --- .../friends/paper/command/FriendCommand.kt | 8 -- .../listener/NetworkConnectionListener.kt | 17 --- .../paper/permission/PermissionRegistry.kt | 19 ---- surf-friends-velocity/build.gradle.kts | 25 ----- .../friends/velocity/SurfFriendsPlugin.kt | 62 ----------- .../friends/velocity/command/FriendCommand.kt | 29 ----- .../command/argument/PlayerStringArgument.kt | 43 -------- .../subcommand/friend/FriendInfoCommand.kt | 79 -------------- .../subcommand/friend/FriendJumpCommand.kt | 68 ------------ .../subcommand/friend/FriendListCommand.kt | 93 ---------------- .../subcommand/friend/FriendRemoveCommand.kt | 53 --------- .../request/FriendRequestAcceptCommand.kt | 62 ----------- .../request/FriendRequestDeclineCommand.kt | 53 --------- .../request/FriendRequestListCommand.kt | 82 -------------- .../request/FriendRequestRevokeCommand.kt | 53 --------- .../request/FriendRequestSendCommand.kt | 98 ----------------- .../FriendSettingsAnnouncementsCommand.kt | 27 ----- .../toggle/FriendSettingsCommand.kt | 11 -- .../toggle/FriendSettingsSoundsCommand.kt | 27 ----- .../velocity/listener/ConnectionListener.kt | 93 ---------------- .../redis/event/FriendRemoveRedisEvent.kt | 13 --- .../event/FriendRequestAcceptRedisEvent.kt | 13 --- .../event/FriendRequestDenyRedisEvent.kt | 13 --- .../event/FriendRequestRevokeRedisEvent.kt | 13 --- .../event/FriendRequestSendRedisEvent.kt | 14 --- .../listener/FriendRequestRedisListener.kt | 80 -------------- .../redis/listener/FriendshipRedisListener.kt | 21 ---- .../velocity/util/FriendPermissionRegistry.kt | 21 ---- .../surf/friends/velocity/util/friend-util.kt | 21 ---- .../surf/friends/velocity/util/uuid-util.kt | 42 -------- 43 files changed, 9 insertions(+), 1534 deletions(-) delete mode 100644 surf-friends-backend/build.gradle.kts delete mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt delete mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt delete mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt delete mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt delete mode 100644 surf-friends-paper/build.gradle.kts delete mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt delete mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt delete mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt delete mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt delete mode 100644 surf-friends-velocity/build.gradle.kts delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/SurfFriendsPlugin.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/FriendCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/argument/PlayerStringArgument.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendInfoCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendJumpCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendListCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendRemoveCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestAcceptCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestDeclineCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestListCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestRevokeCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestSendCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsAnnouncementsCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsSoundsCommand.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/listener/ConnectionListener.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRemoveRedisEvent.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestAcceptRedisEvent.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestDenyRedisEvent.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestRevokeRedisEvent.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestSendRedisEvent.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendRequestRedisListener.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendshipRedisListener.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/FriendPermissionRegistry.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/friend-util.kt delete mode 100644 surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/uuid-util.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 153d239..7e0f1cd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,8 +2,4 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } include("surf-friends-api") -include("surf-friends-core") -include("surf-friends-velocity") -include("surf-friends-backend") - -include("surf-friends-paper") \ No newline at end of file +include("surf-friends-core") \ No newline at end of file diff --git a/surf-friends-api/build.gradle.kts b/surf-friends-api/build.gradle.kts index 749ef8e..64d286f 100644 --- a/surf-friends-api/build.gradle.kts +++ b/surf-friends-api/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - id("dev.slne.surf.surfapi.gradle.core") + id("dev.slne.surf.surfapi.gradle.paper-raw") } \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt index 409442a..09b57d8 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt @@ -1,107 +1,9 @@ package dev.slne.surf.friends.api -import dev.slne.surf.friends.api.friend.FriendRequest -import dev.slne.surf.friends.api.friend.Friendship -import it.unimi.dsi.fastutil.objects.ObjectSet import java.util.* -interface SurfFriendsApi { - /** - * Creates a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - * @return The created Friendship object. - */ - suspend fun createFriendship(uuid: UUID, friend: UUID): Friendship - - /** - * Removes a friendship between two users. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the friend. - */ - suspend fun removeFriendship(uuid: UUID, friend: UUID) - - /** - * Retrieves all friendships of a user. - * - * @param uuid The UUID of the user. - * @return A set of Friendships representing the user's friends. - */ - suspend fun getFriendships(uuid: UUID): ObjectSet - - /** - * Sends a friend request from one user to another. - * - * @param sender The UUID of the sender. - * @param receiver The UUID of the receiver. - * @return The created FriendRequest object. - */ - suspend fun sendFriendRequest(sender: UUID, receiver: UUID): FriendRequest - - /** - * Accepts a friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) - - /** - * Declines a friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun declineFriendRequest(sender: UUID, receiver: UUID) +val surfFriendsApi = requiredService() - /** - * Revokes a sent friend request. - * - * @param sender The UUID of the sender of the request. - * @param receiver The UUID of the receiver of the request. - */ - suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) - - /** - * Retrieves all sent friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A set of FriendRequests representing the recipients of the sent requests. - */ - suspend fun getSentFriendRequests(uuid: UUID): ObjectSet - - /** - * Retrieves all received friend requests of a user. - * - * @param uuid The UUID of the user. - * @return A set of FriendRequests representing the senders of the received requests. - */ - suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet - - /** - * Toggles announcements for a user. - * - * @param uuid The UUID of the user. - * @return The new status of announcements (true if enabled, false if disabled). - */ - suspend fun toggleAnnouncements(uuid: UUID): Boolean - - /** - * Toggles sounds for a user. - * - * @param uuid The UUID of the user. - * @return The new status of sounds (true if enabled, false if disabled). - */ - suspend fun toggleSounds(uuid: UUID): Boolean +interface SurfFriendsApi { - /** - * Checks if two users are friends and retrieves their friendship details. - * - * @param uuid The UUID of the first user. - * @param friend The UUID of the second user. - * @return The Friendship object representing the friendship between the two users, or null if they are not friends. - */ - suspend fun areFriends(uuid: UUID, friend: UUID): Friendship? } \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index fff50aa..871453b 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -4,7 +4,7 @@ import java.time.OffsetDateTime import java.util.* data class Friendship( - val userUuid: UUID, - val friendUuid: UUID, + val requestedBy: UUID, + val acceptedBy: UUID, val createdAt: OffsetDateTime ) \ No newline at end of file diff --git a/surf-friends-backend/build.gradle.kts b/surf-friends-backend/build.gradle.kts deleted file mode 100644 index 6d793da..0000000 --- a/surf-friends-backend/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("dev.slne.surf.surfapi.gradle.core") -} - -dependencies { - api(project(":surf-friends-core")) - api(libs.surf.database) -} diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt deleted file mode 100644 index 8d03dc3..0000000 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FallbackFriendService.kt +++ /dev/null @@ -1,79 +0,0 @@ -package dev.slne.surf.friends.backend.service - -import com.google.auto.service.AutoService -import dev.slne.surf.friends.api.friend.FriendRequest -import dev.slne.surf.friends.core.service.FriendService -import dev.slne.surf.friends.core.service.databaseService -import net.kyori.adventure.util.Services -import java.util.* - -@AutoService(FriendService::class) -class FallbackFriendService : FriendService, Services.Fallback { - override suspend fun createFriendship(uuid: UUID, friend: UUID) = - databaseService.getFriendship(uuid, friend) ?: databaseService.addFriendship( - uuid, - friend - ) - - override suspend fun removeFriendship(uuid: UUID, friend: UUID) { - databaseService.removeFriendship(uuid, friend) - databaseService.removeFriendship(friend, uuid) - } - - override suspend fun getFriendship( - playerA: UUID, - playerB: UUID - ) = databaseService.getFriendship(playerA, playerB) - - override suspend fun areFriends( - uuid: UUID, - friend: UUID - ) = databaseService.getFriendship(uuid, friend) - - override suspend fun getFriendships(uuid: UUID) = databaseService.getFriends(uuid) - - override suspend fun sendFriendRequest(sender: UUID, receiver: UUID): FriendRequest = - databaseService.getFriendRequest(sender, receiver) - ?: databaseService.addFriendRequest(sender, receiver) - - override suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) { - databaseService.removeFriendRequest(sender, receiver) - databaseService.addFriendship(sender, receiver) - databaseService.addFriendship(receiver, sender) - } - - override suspend fun declineFriendRequest(sender: UUID, receiver: UUID) = - databaseService.removeFriendRequest(sender, receiver) - - override suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) = - databaseService.removeFriendRequest(sender, receiver) - - override suspend fun getSentFriendRequests(uuid: UUID) = - databaseService.getSentFriendRequests(uuid) - - override suspend fun getReceivedFriendRequests(uuid: UUID) = - databaseService.getReceivedFriendRequests(uuid) - - override suspend fun getFriendRequest( - sender: UUID, - target: UUID - ) = databaseService.getFriendRequest(sender, target) - - override suspend fun toggleAnnouncements(uuid: UUID): Boolean { - val friendSettings = databaseService.getFriendSettings(uuid) - val newSettings = friendSettings.modify { - announcementsEnabled = !friendSettings.announcementsEnabled - } - - return databaseService.updateFriendSettings(uuid, newSettings).announcementsEnabled - } - - override suspend fun toggleSounds(uuid: UUID): Boolean { - val friendSettings = databaseService.getFriendSettings(uuid) - val newSettings = friendSettings.modify { - soundsEnabled = !friendSettings.soundsEnabled - } - - return databaseService.updateFriendSettings(uuid, newSettings).soundsEnabled - } -} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt deleted file mode 100644 index 2f6c56e..0000000 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestTable.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.slne.surf.friends.backend.table - -import org.jetbrains.exposed.sql.Table -import java.util.* - -object FriendRequestTable : Table("friend_requests") { - val id = integer("id").autoIncrement() - val senderUuid = - varchar("sender_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val receiverUuid = - varchar("receiver_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val send_at = long("created_at") - - override val primaryKey = PrimaryKey(id) -} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt deleted file mode 100644 index 1a85836..0000000 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendSettingsTable.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.backend.table - -import org.jetbrains.exposed.sql.Table -import java.util.* - -object FriendSettingsTable : Table("friend_settings") { - val userUuid = - varchar("user_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }).uniqueIndex() - var announcementsEnabled = bool("announcements_enabled").default(true) - var soundsEnabled = bool("sounds_enabled").default(true) - - override val primaryKey = PrimaryKey(userUuid) -} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt deleted file mode 100644 index 59cbbe8..0000000 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipTable.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.slne.surf.friends.backend.table - -import org.jetbrains.exposed.sql.Table -import java.util.* - -object FriendShipTable : Table("friend_ships") { - val id = integer("id").autoIncrement() - val userUuid = varchar("user_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val friendUuid = - varchar("friend_uuid", 36).transform({ UUID.fromString(it) }, { it.toString() }) - val created_at = long("created_at") - - override val primaryKey = PrimaryKey(id) -} \ No newline at end of file diff --git a/surf-friends-core/build.gradle.kts b/surf-friends-core/build.gradle.kts index 14e6bbb..0363f84 100644 --- a/surf-friends-core/build.gradle.kts +++ b/surf-friends-core/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("dev.slne.surf.surfapi.gradle.core") + id("dev.slne.surf.surfapi.gradle.paper-raw") } dependencies { diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt index ce345f5..3125d09 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt @@ -2,13 +2,11 @@ package dev.slne.surf.friends.core.service import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship -import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet import java.util.* -/** - * Service interface for managing friendships and related actions. - */ +val friendService get() = requiredService() + interface FriendService { suspend fun createFriendship(uuid: UUID, friend: UUID): Friendship suspend fun removeFriendship(uuid: UUID, friend: UUID) @@ -22,13 +20,5 @@ interface FriendService { suspend fun getSentFriendRequests(uuid: UUID): ObjectSet suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet suspend fun getFriendRequest(sender: UUID, target: UUID): FriendRequest? - - companion object { - val INSTANCE = requiredService() - } } -/** - * Extension function to get the friend service instance. - */ -val friendService get() = FriendService.INSTANCE \ No newline at end of file diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts deleted file mode 100644 index 91ffc2f..0000000 --- a/surf-friends-paper/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id("dev.slne.surf.surfapi.gradle.paper-plugin") -} - -surfPaperPluginApi { - mainClass("dev.slne.surf.friends.paper.PaperMain") - generateLibraryLoader(false) - foliaSupported(true) - - withSurfRedis() - withCorePaper() - - authors.add("red") -} - -dependencies { - api(project(":surf-friends-core")) - runtimeOnly(project(":surf-friends-backend")) -} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt deleted file mode 100644 index 12b7211..0000000 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.slne.surf.friends.paper - -import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin -import org.bukkit.plugin.java.JavaPlugin - -val plugin get() = JavaPlugin.getPlugin(PaperMain::class.java) - -class PaperMain : SuspendingJavaPlugin() { - override fun onLoad() { - - } -} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt deleted file mode 100644 index 955ebcc..0000000 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.slne.surf.friends.paper.command - -import dev.jorel.commandapi.kotlindsl.commandAPICommand -import dev.slne.surf.friends.paper.permission.PermissionRegistry - -fun friendCommand() = commandAPICommand("friend") { - withPermission(PermissionRegistry.BASE_COMMAND) -} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt deleted file mode 100644 index e384d23..0000000 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/NetworkConnectionListener.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.slne.surf.friends.paper.listener - -import dev.slne.surf.core.api.common.event.SurfEventHandler -import dev.slne.surf.core.api.common.event.SurfPlayerConnectEvent -import dev.slne.surf.core.api.common.event.SurfPlayerDisconnectEvent - -object NetworkConnectionListener { - @SurfEventHandler - fun onNetworkConnect(event: SurfPlayerConnectEvent) { - - } - - @SurfEventHandler - fun onNetworkDisconnect(event: SurfPlayerDisconnectEvent) { - - } -} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt deleted file mode 100644 index 44693ab..0000000 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.slne.surf.friends.paper.permission - -import dev.slne.surf.surfapi.bukkit.api.permission.PermissionRegistry - -object PermissionRegistry : PermissionRegistry() { - const val BASE = "surf.friends" - const val BASE_COMMAND = "$BASE.command" - - val COMMAND_FRIEND_REQUEST_SEND = create("$BASE_COMMAND.request.send") - val COMMAND_FRIEND_REQUEST_ACCEPT = create("$BASE_COMMAND.request.accept") - val COMMAND_FRIEND_REQUEST_DECLINE = create("$BASE_COMMAND.request.decline") - val COMMAND_FRIEND_REQUEST_REVOKE = create("$BASE_COMMAND.request.revoke") - val COMMAND_FRIEND_REQUEST_LIST = create("$BASE_COMMAND.request.list") - - val COMMAND_FRIEND_INFO = create("$BASE_COMMAND.info") - val COMMAND_FRIEND_JUMP = create("$BASE_COMMAND.jump") - val COMMAND_FRIEND_REMOVE = create("$BASE_COMMAND.remove") - val COMMAND_FRIEND_LIST = create("$BASE_COMMAND.list") -} \ No newline at end of file diff --git a/surf-friends-velocity/build.gradle.kts b/surf-friends-velocity/build.gradle.kts deleted file mode 100644 index b91b125..0000000 --- a/surf-friends-velocity/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - id("dev.slne.surf.surfapi.gradle.velocity") -} - -surfVelocityApi { - withSurfRedis() - withCoreVelocity() -} - -velocityPluginFile { - main = "dev.slne.surf.friends.velocity.SurfFriendsPlugin" - name = "SurfFriends" - id = "surf-friends" - authors = listOf("red") - version = "${rootProject.version}" - - pluginDependencies { - register("commandapi") - } -} - -dependencies { - api(project(":surf-friends-core")) - runtimeOnly(project(":surf-friends-backend")) -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/SurfFriendsPlugin.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/SurfFriendsPlugin.kt deleted file mode 100644 index c2d161f..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/SurfFriendsPlugin.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.slne.surf.friends.velocity - -import com.github.shynixn.mccoroutine.velocity.SuspendingPluginContainer -import com.google.inject.Inject -import com.velocitypowered.api.event.Subscribe -import com.velocitypowered.api.event.proxy.ProxyInitializeEvent -import com.velocitypowered.api.event.proxy.ProxyShutdownEvent -import com.velocitypowered.api.plugin.PluginContainer -import com.velocitypowered.api.plugin.annotation.DataDirectory -import com.velocitypowered.api.proxy.ProxyServer -import dev.slne.surf.friends.core.service.databaseService -import dev.slne.surf.friends.velocity.command.friendCommand -import dev.slne.surf.friends.velocity.command.subcommand.friend.FriendListCommand -import dev.slne.surf.friends.velocity.command.subcommand.request.FriendRequestSendCommand -import dev.slne.surf.friends.velocity.listener.ConnectionListener -import dev.slne.surf.friends.velocity.redis.listener.FriendRequestRedisListener -import dev.slne.surf.friends.velocity.redis.listener.FriendshipRedisListener -import dev.slne.surf.redis.RedisApi -import java.nio.file.Path - -class SurfFriendsPlugin -@Inject -constructor( - val proxy: ProxyServer, - val container: PluginContainer, - @param:DataDirectory val dataDirectory: Path, - suspendingPluginContainer: SuspendingPluginContainer -) { - init { - suspendingPluginContainer.initialize(this) - INSTANCE = this - } - - @Subscribe - fun onProxyInitialization(event: ProxyInitializeEvent) { - proxy.eventManager.register(this, ConnectionListener()) - - databaseService.connect(dataDirectory) - redisApi = RedisApi.create() - redisApi.subscribeToEvents(FriendshipRedisListener) - redisApi.subscribeToEvents(FriendRequestRedisListener) - redisApi.freezeAndConnect() - - friendCommand() - FriendRequestSendCommand("fa").register() - FriendListCommand("fl").register() - } - - @Subscribe - fun onProxyShutdown(event: ProxyShutdownEvent) { - redisApi.disconnect() - } - - companion object { - lateinit var redisApi: RedisApi - lateinit var INSTANCE: SurfFriendsPlugin - } -} - -val container get() = SurfFriendsPlugin.INSTANCE.container -val plugin get() = SurfFriendsPlugin.INSTANCE -val redisApi get() = SurfFriendsPlugin.redisApi \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/FriendCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/FriendCommand.kt deleted file mode 100644 index adb3479..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/FriendCommand.kt +++ /dev/null @@ -1,29 +0,0 @@ -package dev.slne.surf.friends.velocity.command - -import dev.jorel.commandapi.kotlindsl.commandAPICommand -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.velocity.command.subcommand.friend.FriendListCommand -import dev.slne.surf.friends.velocity.command.subcommand.friend.friendInfoCommand -import dev.slne.surf.friends.velocity.command.subcommand.friend.friendJumpCommand -import dev.slne.surf.friends.velocity.command.subcommand.friend.friendRemoveCommand -import dev.slne.surf.friends.velocity.command.subcommand.request.* -import dev.slne.surf.friends.velocity.command.subcommand.toggle.friendToggleCommand -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry - -fun friendCommand() = commandAPICommand("friend") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND) - withAliases("f") - - friendRemoveCommand() - friendInfoCommand() - friendJumpCommand() - - friendRequestAcceptCommand() - friendRequestDeclineCommand() - friendRequestRevokeCommand() - friendRequestListCommand() - friendToggleCommand() - - subcommand(FriendListCommand("list")) - subcommand(FriendRequestSendCommand("add")) -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/argument/PlayerStringArgument.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/argument/PlayerStringArgument.kt deleted file mode 100644 index 59a809b..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/argument/PlayerStringArgument.kt +++ /dev/null @@ -1,43 +0,0 @@ -package dev.slne.surf.friends.velocity.command.argument - -import com.mojang.brigadier.arguments.StringArgumentType -import com.mojang.brigadier.context.CommandContext -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.arguments.Argument -import dev.jorel.commandapi.arguments.ArgumentSuggestions -import dev.jorel.commandapi.arguments.CommandAPIArgumentType -import dev.jorel.commandapi.executors.CommandArguments -import dev.slne.surf.core.api.common.surfCoreApi - -class PlayerStringArgument(nodeName: String) : - Argument(nodeName, StringArgumentType.string()) { - override fun getPrimitiveType(): Class { - return String::class.java - } - - override fun getArgumentType(): CommandAPIArgumentType { - return CommandAPIArgumentType.PRIMITIVE_TEXT - } - - override fun parseArgument( - cmdCtx: CommandContext, - key: String, - previousArgs: CommandArguments - ): String { - return StringArgumentType.getString(cmdCtx, key) - } - - init { - replaceSuggestions(ArgumentSuggestions.stringCollection { - surfCoreApi.getOnlinePlayers().mapNotNull { it.lastKnownName } - }) - } -} - -inline fun CommandAPICommand.playerStringArgument( - nodeName: String, - optional: Boolean = false, - block: Argument<*>.() -> Unit = {} -): CommandAPICommand = withArguments( - PlayerStringArgument(nodeName).setOptional(optional).apply(block) -) \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendInfoCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendInfoCommand.kt deleted file mode 100644 index 94aea7a..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendInfoCommand.kt +++ /dev/null @@ -1,79 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.friend - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.format -import dev.slne.surf.friends.velocity.util.getUsernameAsync -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.font.toSmallCaps -import dev.slne.surf.surfapi.core.api.messages.adventure.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService -import net.kyori.adventure.text.format.TextDecoration - -fun CommandAPICommand.friendInfoCommand() = subcommand("info") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_INFO) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendShip = friendService.getFriendship(player.uniqueId, targetUuid) - - if (targetUuid == player.uniqueId) { - player.uniqueId.sendText { - error("Du kannst keine Informationen über dich selbst abrufen.") - } - return@launch - } - - if (friendShip == null) { - player.uniqueId.sendText { - error("Du bist nicht mit $target befreundet.") - } - return@launch - } - - val userName = friendShip.userUuid.getUsernameAsync() - val targetName = friendShip.friendUuid.getUsernameAsync() - - player.sendText { - info("Freundschaftsinformationen".toSmallCaps()) - appendNewline() - append { - info("| ") - decorate(TextDecoration.BOLD) - } - variableKey("Spieler: ".toSmallCaps()) - variableValue(userName) - appendNewline() - - append { - info("| ") - decorate(TextDecoration.BOLD) - } - variableKey("Freund: ".toSmallCaps()) - variableValue(targetName) - appendNewline() - - append { - info("| ") - decorate(TextDecoration.BOLD) - } - variableKey("Befreundet seit: ".toSmallCaps()) - variableValue(friendShip.createdAt.format()) - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendJumpCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendJumpCommand.kt deleted file mode 100644 index 0edd52d..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendJumpCommand.kt +++ /dev/null @@ -1,68 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.friend - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand - -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.plugin -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.isOnline -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService -import kotlin.jvm.optionals.getOrNull - -fun CommandAPICommand.friendJumpCommand() = subcommand("jump") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_JUMP) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendShip = friendService.getFriendship(player.uniqueId, targetUuid) - - if (friendShip == null) { - player.uniqueId.sendText { - error("Du bist nicht mit $target befreundet.") - } - return@launch - } - - if (targetUuid.isOnline()) { - val onlineFriend = - plugin.proxy.getPlayer(targetUuid).getOrNull() ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target ist nicht online.") - } - } - - player.createConnectionRequest( - onlineFriend.currentServer.getOrNull()?.server ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target ist nicht auf einem Server.") - } - }).fireAndForget() - - player.uniqueId.sendText { - success("Du bist ") - variableValue(target) - success(" auf den Server gefolgt.") - } - return@launch - } - - player.uniqueId.sendText { - error("Der Spieler $target ist nicht online.") - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendListCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendListCommand.kt deleted file mode 100644 index 3308fd4..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendListCommand.kt +++ /dev/null @@ -1,93 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.friend - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.integerArgument -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.* -import dev.slne.surf.surfapi.core.api.font.toSmallCaps -import dev.slne.surf.surfapi.core.api.messages.CommonComponents -import dev.slne.surf.surfapi.core.api.messages.adventure.buildText -import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand -import dev.slne.surf.surfapi.core.api.messages.adventure.sendText -import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import net.kyori.adventure.text.format.TextDecoration -import kotlin.jvm.optionals.getOrNull - -class FriendListCommand(commandName: String) : CommandAPICommand(commandName) { - init { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_LIST) - integerArgument("page", 1, optional = true) - playerExecutor { player, args -> - container.launch { - val friendList = friendService.getFriendships(player.uniqueId) - val page = args.getOrDefaultUnchecked("page", 1) - - if (friendList.isEmpty()) { - player.uniqueId.sendText { - error("Du hast keine Freunde.") - } - return@launch - } - - val friendEntries = friendList.sortedByDescending { - it.friendUuid.isOnline() - }.map { - async { - it to it.friendUuid.getUsernameAsync() - } - }.awaitAll().map { - LocalFriendEntry( - it.second, - it.first.friendUuid.isOnline(), - it.first.friendUuid.toPlayer()?.currentServer?.getOrNull()?.serverInfo?.name - ?: "Unbekannt" - ) - } - - val pagination = Pagination { - title { primary("Freundesliste".toSmallCaps(), TextDecoration.BOLD) } - - rowRenderer { row, _ -> - listOf( - buildText { - append(CommonComponents.EM_DASH) - appendSpace() - variableKey(row.friendName) - appendSpace() - if (row.isOnline) { - appendSpace() - success("(Online auf ${row.onlineServer})") - } else { - appendSpace() - error("(Offline)") - } - hoverEvent(buildText { - info("Klicke hier, um ") - variableValue(row.friendName) - info(" hinterher zuspringen.") - }) - clickRunsCommand("/friend jump ${row.friendName}") - } - ) - } - } - - - player.sendText { - append(pagination.renderComponent(friendEntries, page)) - } - } - } - } -} - -private data class LocalFriendEntry( - val friendName: String, - val isOnline: Boolean, - val onlineServer: String -) \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendRemoveCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendRemoveCommand.kt deleted file mode 100644 index 6910706..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/friend/FriendRemoveCommand.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.friend - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.redis.event.FriendRemoveRedisEvent -import dev.slne.surf.friends.velocity.redisApi -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService - -fun CommandAPICommand.friendRemoveCommand() = subcommand("remove") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REMOVE) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendShip = friendService.getFriendship(player.uniqueId, targetUuid) - - if (friendShip == null) { - player.uniqueId.sendText { - error("Du bist nicht mit $target befreundet.") - } - return@launch - } - - friendService.removeFriendship(player.uniqueId, targetUuid) - - player.uniqueId.sendText { - success("Du hast die Freundschaft mit ") - variableValue(target) - success(" beendet.") - } - - redisApi.publishEvent( - FriendRemoveRedisEvent( - player.uniqueId, player.username, targetUuid - ) - ) - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestAcceptCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestAcceptCommand.kt deleted file mode 100644 index 5b7b774..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestAcceptCommand.kt +++ /dev/null @@ -1,62 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.request - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.redis.event.FriendRequestAcceptRedisEvent -import dev.slne.surf.friends.velocity.redisApi -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService - -fun CommandAPICommand.friendRequestAcceptCommand() = subcommand("accept") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REQUEST_ACCEPT) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendRequest = friendService.getFriendRequest(targetUuid, player.uniqueId) - - if (friendRequest == null) { - player.uniqueId.sendText { - error("Du hast keine Freundschaftsanfrage von $target erhalten.") - } - return@launch - } - - val friendShip = friendService.getFriendship(player.uniqueId, targetUuid) - - if (friendShip != null) { - player.uniqueId.sendText { - error("Du bist bereits mit $target befreundet.") - } - return@launch - } - - friendService.acceptFriendRequest(targetUuid, player.uniqueId) - - player.uniqueId.sendText { - success("Du bist nun mit ") - variableValue(target) - success(" befreundet.") - } - - redisApi.publishEvent( - FriendRequestAcceptRedisEvent( - targetUuid, player.uniqueId, player.username - ) - ) - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestDeclineCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestDeclineCommand.kt deleted file mode 100644 index 69d3415..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestDeclineCommand.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.request - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.redis.event.FriendRequestDenyRedisEvent -import dev.slne.surf.friends.velocity.redisApi -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService - -fun CommandAPICommand.friendRequestDeclineCommand() = subcommand("decline") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REQUEST_DECLINE) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendRequest = friendService.getFriendRequest(targetUuid, player.uniqueId) - - if (friendRequest == null) { - player.uniqueId.sendText { - error("Du hast keine Freundschaftsanfrage von $target erhalten.") - } - return@launch - } - - friendService.declineFriendRequest(targetUuid, player.uniqueId) - - player.uniqueId.sendText { - success("Du hast die Freundschaftsanfrage von ") - variableValue(target) - success(" abgelehnt.") - } - - redisApi.publishEvent( - FriendRequestDenyRedisEvent( - targetUuid, player.uniqueId, player.username - ) - ) - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestListCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestListCommand.kt deleted file mode 100644 index 6cb3236..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestListCommand.kt +++ /dev/null @@ -1,82 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.request - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.integerArgument -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.format -import dev.slne.surf.friends.velocity.util.getUsernameAsync -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.font.toSmallCaps -import dev.slne.surf.surfapi.core.api.messages.CommonComponents -import dev.slne.surf.surfapi.core.api.messages.adventure.buildText -import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand -import dev.slne.surf.surfapi.core.api.messages.adventure.sendText -import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import net.kyori.adventure.text.format.TextDecoration - -fun CommandAPICommand.friendRequestListCommand() = subcommand("requests") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REQUEST_LIST) - integerArgument("page", 1, optional = true) - playerExecutor { player, args -> - container.launch { - val friendRequests = friendService.getReceivedFriendRequests(player.uniqueId) - .sortedByDescending { it.sentAt } - val page = args.getOrDefaultUnchecked("page", 1) - - if (friendRequests.isEmpty()) { - player.uniqueId.sendText { - error("Du hast keine Freundschaftsanfragen offen.") - } - return@launch - } - - val requesterEntries = friendRequests.map { - async { - it to it.senderUuid.getUsernameAsync() - } - }.awaitAll().map { - LocalFriendRequest( - it.second, - it.first.sentAt - ) - } - - val pagination = Pagination { - title { primary("Offene Freundschaftsanfragen".toSmallCaps(), TextDecoration.BOLD) } - - rowRenderer { row, _ -> - listOf( - buildText { - append(CommonComponents.EM_DASH) - appendSpace() - variableKey(row.requesterName) - spacer(" (${row.sentAt.format()})") - hoverEvent(buildText { - info("Klicke hier, um die Freundschaftsanfrage von ") - variableValue(row.requesterName) - info(" anzunehmen.") - }) - clickRunsCommand("/friend accept ${row.requesterName}") - } - ) - } - } - - player.sendText { - append(pagination.renderComponent(requesterEntries, page)) - } - } - } -} - -private data class LocalFriendRequest( - val requesterName: String, - val sentAt: Long -) \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestRevokeCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestRevokeCommand.kt deleted file mode 100644 index 1e9a693..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestRevokeCommand.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.request - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.redis.event.FriendRequestRevokeRedisEvent -import dev.slne.surf.friends.velocity.redisApi -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService - -fun CommandAPICommand.friendRequestRevokeCommand() = subcommand("revoke") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REQUEST_REVOKE) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - val friendRequest = friendService.getFriendRequest(player.uniqueId, targetUuid) - - if (friendRequest == null) { - player.uniqueId.sendText { - error("Du hast keine Freundschaftsanfrage an $target gesendet.") - } - return@launch - } - - friendService.revokeFriendRequest(player.uniqueId, targetUuid) - - player.uniqueId.sendText { - success("Du hast die Freundschaftsanfrage an ") - variableValue(target) - success(" zurückgezogen.") - } - - redisApi.publishEvent( - FriendRequestRevokeRedisEvent( - player.uniqueId, player.username, targetUuid - ) - ) - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestSendCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestSendCommand.kt deleted file mode 100644 index ae3a899..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/request/FriendRequestSendCommand.kt +++ /dev/null @@ -1,98 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.request - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.getValue -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.slne.surf.friends.core.service.databaseService -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.command.argument.playerStringArgument -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.redis.event.FriendRequestSendRedisEvent -import dev.slne.surf.friends.velocity.redisApi -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.font.toSmallCaps -import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService - -class FriendRequestSendCommand(commandName: String) : CommandAPICommand(commandName) { - init { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_REQUEST_SEND) - playerStringArgument("target") - playerExecutor { player, args -> - container.launch { - val target: String by args - val targetUuid = PlayerLookupService.getUuid(target) ?: return@launch run { - player.uniqueId.sendText { - error("Der Spieler $target wurde nicht gefunden.") - } - } - - if (player.uniqueId == targetUuid) { - player.uniqueId.sendText { - error("Du kannst dir keine Freundschaftsanfrage selbst senden.") - } - return@launch - } - - val friendShip = friendService.getFriendship(player.uniqueId, targetUuid) - - if (friendShip != null) { - player.uniqueId.sendText { - error("Du bist bereits mit $target befreundet.") - } - return@launch - } - - val friendRequest = friendService.getFriendRequest(player.uniqueId, targetUuid) - val receivedFriendRequest = friendService.getFriendship(targetUuid, player.uniqueId) - - if (friendRequest != null) { - player.uniqueId.sendText { - error("Du hast bereits eine Freundschaftsanfrage an $target gesendet.") - } - return@launch - } - - if (receivedFriendRequest != null) { - player.uniqueId.sendText { - error("Du hast bereits eine Freundschaftsanfrage von $target erhalten. Möchtest du diese annehmen?") - append { - clickRunsCommand("/friend accept $target") - spacer(" [") - info("Akzeptieren".toSmallCaps()) - spacer("]") - } - } - return@launch - } - - friendService.sendFriendRequest(player.uniqueId, targetUuid) - - player.uniqueId.sendText { - success("Du hast eine Freundschaftsanfrage an ") - variableValue(target) - success(" gesendet.") - append { - clickRunsCommand("/friend revoke $target") - spacer(" [") - info("Zurückziehen".toSmallCaps()) - spacer("]") - } - } - - val targetSettings = databaseService.getFriendSettings(targetUuid) - - redisApi.publishEvent( - FriendRequestSendRedisEvent( - player.uniqueId, - player.username, - targetUuid, - targetSettings.announcementsEnabled - ) - ) - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsAnnouncementsCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsAnnouncementsCommand.kt deleted file mode 100644 index 38a7008..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsAnnouncementsCommand.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.toggle - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText - -fun CommandAPICommand.toggleAnnouncementsCommand() = subcommand("notify-messages") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_TOGGLE_ANNOUNCEMENTS) - playerExecutor { player, _ -> - container.launch { - when (friendService.toggleAnnouncements(player.uniqueId)) { - true -> player.uniqueId.sendText { - success("Du hast die Freundes-Benachrichtigungen aktiviert.") - } - - false -> player.uniqueId.sendText { - success("Du hast die Freundes-Benachrichtigungen deaktiviert.") - } - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsCommand.kt deleted file mode 100644 index 1adc7c0..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsCommand.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.toggle - -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry - -fun CommandAPICommand.friendToggleCommand() = subcommand("settings") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_TOGGLE) - toggleAnnouncementsCommand() - toggleSoundsCommand() -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsSoundsCommand.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsSoundsCommand.kt deleted file mode 100644 index c298a79..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/command/subcommand/toggle/FriendSettingsSoundsCommand.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.slne.surf.friends.velocity.command.subcommand.toggle - -import com.github.shynixn.mccoroutine.velocity.launch -import dev.jorel.commandapi.CommandAPICommand -import dev.jorel.commandapi.kotlindsl.playerExecutor -import dev.jorel.commandapi.kotlindsl.subcommand -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.FriendPermissionRegistry -import dev.slne.surf.friends.velocity.util.sendText - -fun CommandAPICommand.toggleSoundsCommand() = subcommand("notify-sounds") { - withPermission(FriendPermissionRegistry.COMMAND_FRIEND_TOGGLE_SOUNDS) - playerExecutor { player, _ -> - container.launch { - when (friendService.toggleSounds(player.uniqueId)) { - true -> player.uniqueId.sendText { - success("Du hast die Soundbenachrichtigungen aktiviert.") - } - - false -> player.uniqueId.sendText { - success("Du hast die Soundbenachrichtigungen deaktiviert.") - } - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/listener/ConnectionListener.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/listener/ConnectionListener.kt deleted file mode 100644 index 0fd8acc..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/listener/ConnectionListener.kt +++ /dev/null @@ -1,93 +0,0 @@ -package dev.slne.surf.friends.velocity.listener - -import com.github.shynixn.mccoroutine.velocity.launch -import com.velocitypowered.api.event.Subscribe -import com.velocitypowered.api.event.connection.DisconnectEvent -import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent -import dev.slne.surf.friends.core.service.databaseService -import dev.slne.surf.friends.core.service.friendService -import dev.slne.surf.friends.velocity.container -import dev.slne.surf.friends.velocity.util.getOnlineFriends -import dev.slne.surf.friends.velocity.util.sendText -import dev.slne.surf.surfapi.core.api.font.toSmallCaps -import dev.slne.surf.surfapi.core.api.messages.adventure.buildText -import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand - -class ConnectionListener { - @Subscribe - fun onConnect(event: PlayerChooseInitialServerEvent) { - val player = event.player - - container.launch { - val friendRequests = friendService.getReceivedFriendRequests(player.uniqueId) - val onlineFriends = friendService.getOnlineFriends(player.uniqueId) - - if (onlineFriends.isNotEmpty()) { - player.uniqueId.sendText { - info("Aktuell sind ") - variableValue(onlineFriends.size) - info(" deiner Freunde online. ") - - append { - clickRunsCommand("/friend list") - info("[Ansehen]".toSmallCaps()) - hoverEvent(buildText { - info("Klicke hier, um die Freunde anzusehen.") - }) - } - } - - onlineFriends.forEach { - val playerSettings = databaseService.getFriendSettings(it) - - if (playerSettings.announcementsEnabled) { - it.sendText { - variableValue(player.username) - info(" ist nun online.") - } - } - } - } - - if (friendRequests.isNotEmpty()) { - player.uniqueId.sendText { - info("Du hast noch ") - variableValue(friendRequests.size) - info(" Freundschaftsanfragen offen. ") - - append { - clickRunsCommand("/friend requests") - info("[Ansehen]".toSmallCaps()) - hoverEvent(buildText { - info("Klicke hier, um die Freundschaftsanfragen anzusehen.") - }) - } - } - } - } - } - - @Subscribe - fun onDisconnect(event: DisconnectEvent) { - if (event.loginStatus != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) { - return - } - - val player = event.player - - container.launch { - val onlineFriends = friendService.getOnlineFriends(player.uniqueId) - - onlineFriends.forEach { - val playerSettings = databaseService.getFriendSettings(it) - - if (playerSettings.announcementsEnabled) { - it.sendText { - variableValue(player.username) - info(" ist nun offline.") - } - } - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRemoveRedisEvent.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRemoveRedisEvent.kt deleted file mode 100644 index 65a8d62..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRemoveRedisEvent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.event - -import dev.slne.surf.redis.event.RedisEvent -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.util.* - -@Serializable -data class FriendRemoveRedisEvent( - val remover: @Contextual UUID, - val removerName: String, - val target: @Contextual UUID -) : RedisEvent() diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestAcceptRedisEvent.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestAcceptRedisEvent.kt deleted file mode 100644 index ed06e88..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestAcceptRedisEvent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.event - -import dev.slne.surf.redis.event.RedisEvent -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.util.* - -@Serializable -data class FriendRequestAcceptRedisEvent( - val requester: @Contextual UUID, - val target: @Contextual UUID, - val targetName: String -) : RedisEvent() diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestDenyRedisEvent.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestDenyRedisEvent.kt deleted file mode 100644 index 1cdee59..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestDenyRedisEvent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.event - -import dev.slne.surf.redis.event.RedisEvent -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.util.* - -@Serializable -data class FriendRequestDenyRedisEvent( - val requester: @Contextual UUID, - val target: @Contextual UUID, - val targetName: String -) : RedisEvent() diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestRevokeRedisEvent.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestRevokeRedisEvent.kt deleted file mode 100644 index 95b9aef..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestRevokeRedisEvent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.event - -import dev.slne.surf.redis.event.RedisEvent -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.util.* - -@Serializable -data class FriendRequestRevokeRedisEvent( - val requester: @Contextual UUID, - val requesterName: String, - val target: @Contextual UUID -) : RedisEvent() diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestSendRedisEvent.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestSendRedisEvent.kt deleted file mode 100644 index 64f7c9b..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/event/FriendRequestSendRedisEvent.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.event - -import dev.slne.surf.redis.event.RedisEvent -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable -import java.util.* - -@Serializable -data class FriendRequestSendRedisEvent( - val requester: @Contextual UUID, - val requesterName: String, - val target: @Contextual UUID, - val announce: Boolean -) : RedisEvent() diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendRequestRedisListener.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendRequestRedisListener.kt deleted file mode 100644 index a81d177..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendRequestRedisListener.kt +++ /dev/null @@ -1,80 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.listener - -import dev.slne.surf.friends.velocity.plugin -import dev.slne.surf.friends.velocity.redis.event.FriendRequestAcceptRedisEvent -import dev.slne.surf.friends.velocity.redis.event.FriendRequestDenyRedisEvent -import dev.slne.surf.friends.velocity.redis.event.FriendRequestRevokeRedisEvent -import dev.slne.surf.friends.velocity.redis.event.FriendRequestSendRedisEvent -import dev.slne.surf.redis.event.OnRedisEvent -import dev.slne.surf.surfapi.core.api.messages.Colors -import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand -import dev.slne.surf.surfapi.core.api.messages.adventure.sendText -import kotlin.jvm.optionals.getOrNull - -object FriendRequestRedisListener { - @OnRedisEvent - fun onFriendRequestAccept(event: FriendRequestAcceptRedisEvent) { - val player = plugin.proxy.getPlayer(event.requester).getOrNull() ?: return - - player.sendText { - appendInfoPrefix() - info("Du bist nun mit ") - variableValue(event.targetName) - info(" befreundet.") - } - } - - @OnRedisEvent - fun onFriendRequestDeny(event: FriendRequestDenyRedisEvent) { - val player = plugin.proxy.getPlayer(event.requester).getOrNull() ?: return - - player.sendText { - appendInfoPrefix() - info("Die Freundschaftsanfrage an ") - variableValue(event.targetName) - info(" wurde abgelehnt.") - } - } - - @OnRedisEvent - fun onFriendRequestRevoke(event: FriendRequestRevokeRedisEvent) { - val player = plugin.proxy.getPlayer(event.target).getOrNull() ?: return - - player.sendText { - appendInfoPrefix() - info("Die Freundschaftsanfrage von ") - variableValue(event.requesterName) - info(" wurde zurückgezogen.") - } - } - - @OnRedisEvent - fun onFriendRequestSend(event: FriendRequestSendRedisEvent) { - val player = plugin.proxy.getPlayer(event.target).getOrNull() ?: return - - if (!event.announce) { - return - } - - player.sendText { - appendInfoPrefix() - info("Du hast eine Freundschaftsanfrage von ") - variableValue(event.requesterName) - info(" erhalten.") - appendSpace() - append { - clickRunsCommand("/friend accept ${event.requesterName}") - spacer("[") - text("Akzeptieren", Colors.GREEN) - spacer("]") - } - appendSpace() - append { - clickRunsCommand("/friend decline ${event.requesterName}") - spacer("[") - text("Ablehnen", Colors.RED) - spacer("]") - } - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendshipRedisListener.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendshipRedisListener.kt deleted file mode 100644 index da8478b..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/redis/listener/FriendshipRedisListener.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.slne.surf.friends.velocity.redis.listener - -import dev.slne.surf.friends.velocity.plugin -import dev.slne.surf.friends.velocity.redis.event.FriendRemoveRedisEvent -import dev.slne.surf.redis.event.OnRedisEvent -import dev.slne.surf.surfapi.core.api.messages.adventure.sendText -import kotlin.jvm.optionals.getOrNull - -object FriendshipRedisListener { - @OnRedisEvent - fun onFriendRemove(event: FriendRemoveRedisEvent) { - val player = plugin.proxy.getPlayer(event.target).getOrNull() ?: return - - player.sendText { - appendInfoPrefix() - info("Die Freundschaft mit ") - variableValue(event.removerName) - info(" wurde beendet.") - } - } -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/FriendPermissionRegistry.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/FriendPermissionRegistry.kt deleted file mode 100644 index 3c1af45..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/FriendPermissionRegistry.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.slne.surf.friends.velocity.util - -object FriendPermissionRegistry { - const val COMMAND_FRIEND = "surf.friends.command" - - const val COMMAND_FRIEND_REQUEST_SEND = "surf.friends.command.request.send" - const val COMMAND_FRIEND_REQUEST_ACCEPT = "surf.friends.command.request.accept" - const val COMMAND_FRIEND_REQUEST_DECLINE = "surf.friends.command.request.decline" - const val COMMAND_FRIEND_REQUEST_REVOKE = "surf.friends.command.request.revoke" - const val COMMAND_FRIEND_REQUEST_LIST = "surf.friends.command.request.list" - - const val COMMAND_FRIEND_INFO = "surf.friends.command.info" - const val COMMAND_FRIEND_JUMP = "surf.friends.command.jump" - const val COMMAND_FRIEND_REMOVE = "surf.friends.command.remove" - const val COMMAND_FRIEND_LIST = "surf.friends.command.list" - - const val COMMAND_FRIEND_TOGGLE = "surf.friends.command.toggle" - const val COMMAND_FRIEND_TOGGLE_ANNOUNCEMENTS = "surf.friends.command.toggle.announcements" - const val COMMAND_FRIEND_TOGGLE_SOUNDS = "surf.friends.command.toggle.sounds" - -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/friend-util.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/friend-util.kt deleted file mode 100644 index 2db9434..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/friend-util.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.slne.surf.friends.velocity.util - -import dev.slne.surf.friends.core.service.FriendService -import dev.slne.surf.friends.velocity.plugin -import dev.slne.surf.friends.velocity.util.isOnline -import dev.slne.surf.surfapi.core.api.util.mutableObjectListOf -import it.unimi.dsi.fastutil.objects.ObjectList -import java.util.UUID - -suspend fun FriendService.getOnlineFriends(user: UUID): ObjectList { - return this.getFriendships(user) - .mapNotNull { - val other = if (it.friendUuid == user) it.userUuid else it.friendUuid - if (other.isOnline()) other else null - } - .toCollection(mutableObjectListOf()) -} - -fun UUID.isOnline(): Boolean { - return plugin.proxy.getPlayer(this).isPresent -} \ No newline at end of file diff --git a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/uuid-util.kt b/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/uuid-util.kt deleted file mode 100644 index 190ea83..0000000 --- a/surf-friends-velocity/src/main/kotlin/dev/slne/surf/friends/velocity/util/uuid-util.kt +++ /dev/null @@ -1,42 +0,0 @@ -package dev.slne.surf.friends.velocity.util - -import com.velocitypowered.api.proxy.Player -import dev.slne.surf.friends.velocity.plugin -import dev.slne.surf.surfapi.core.api.messages.Colors -import dev.slne.surf.surfapi.core.api.messages.builder.SurfComponentBuilder -import dev.slne.surf.surfapi.core.api.service.PlayerLookupService -import java.time.Instant -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.util.* -import kotlin.jvm.optionals.getOrNull - -val timeFormatter: DateTimeFormatter = DateTimeFormatter - .ofPattern("dd.MM.yyyy, HH:mm:ss", Locale.GERMANY) - .withZone(ZoneId.of("Europe/Berlin")) - -fun Long.format(): String { - return timeFormatter.format(Instant.ofEpochMilli(this)) -} - -fun formatTime(millis: Long): String { - return timeFormatter.format(Instant.ofEpochMilli(millis)) -} - -fun UUID.sendText(builder: SurfComponentBuilder.() -> Unit) { - val player = plugin.proxy.getPlayer(this).getOrNull() ?: return - - player.sendMessage(Colors.PREFIX.append(SurfComponentBuilder(builder))) -} - -suspend fun UUID.getUsernameAsync(): String { - return PlayerLookupService.getUsername(this) ?: "Unknown" -} - -fun UUID.getUsername(): String { - return plugin.proxy.getPlayer(this).getOrNull()?.username ?: "Unknown" -} - -fun UUID.toPlayer(): Player? { - return plugin.proxy.getPlayer(this).getOrNull() -} \ No newline at end of file From 863a4aae62eefa9642b3c60c516d8f7e40f6715b Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 08:26:38 +0100 Subject: [PATCH 03/43] feat: Refactor FriendPlayerService and FriendService to use requiredService for dependency management --- .../kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt | 2 +- .../slne/surf/friends/core/service/FriendPlayerService.kt | 8 ++++++-- .../dev/slne/surf/friends/core/service/FriendService.kt | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt index 09b57d8..14dcc37 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/SurfFriendsApi.kt @@ -1,6 +1,6 @@ package dev.slne.surf.friends.api -import java.util.* +import dev.slne.surf.surfapi.core.api.util.requiredService val surfFriendsApi = requiredService() diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index 1a3fd6e..b9c7667 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -1,14 +1,18 @@ package dev.slne.surf.friends.core.service import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet import java.util.* +val friendPlayerService get() = requiredService() + interface FriendPlayerService { fun cachePlayer(friendPlayer: FriendPlayer) fun invalidatePlayer(uuid: UUID) - fun getPlayers(): ObjectSet - suspend fun loadPlayer(uuid: UUID, name: String): FriendPlayer + val players: ObjectSet + + suspend fun loadPlayer(uuid: UUID): FriendPlayer suspend fun savePlayer(friendPlayer: FriendPlayer) } \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt index 3125d09..ad68938 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.core.service import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet import java.util.* From 52a7b1ec561159946c8e5f681eb312e3d2a115f2 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 08:27:10 +0100 Subject: [PATCH 04/43] feat: Update version to 1.21.11-3.0.0-SNAPSHOT and upgrade Kotlin version to 2.3.10 --- .idea/gradle.xml | 3 --- .idea/kotlinc.xml | 2 +- .../surf-friends.surf-friends-velocity.main.iml | 14 -------------- gradle.properties | 2 +- gradle/libs.versions.toml | 5 ----- 5 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 .idea/modules/surf-friends-velocity/surf-friends.surf-friends-velocity.main.iml delete mode 100644 gradle/libs.versions.toml diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9e02b64..0e688fa 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -10,10 +10,7 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 739bc36..f203762 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/.idea/modules/surf-friends-velocity/surf-friends.surf-friends-velocity.main.iml b/.idea/modules/surf-friends-velocity/surf-friends.surf-friends-velocity.main.iml deleted file mode 100644 index a587e97..0000000 --- a/.idea/modules/surf-friends-velocity/surf-friends.surf-friends-velocity.main.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - VELOCITY - ADVENTURE - - 1 - - - - \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d446010..da641c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.stdlib.default.dependency=false org.gradle.parallel=true -version=1.21.11-2.0.2-SNAPSHOT +version=1.21.11-3.0.0-SNAPSHOT diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index ad6aa21..0000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,5 +0,0 @@ -[versions] -surf-database = "2.0.4-SNAPSHOT" - -[libraries] -surf-database = { module = "dev.slne.surf:surf-database", version.ref = "surf-database" } \ No newline at end of file From ae0d5256be7cd1e0a08c2f8ead49495c5ec827bc Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 08:28:03 +0100 Subject: [PATCH 05/43] chore: delete idea files --- .idea/.gitignore | 3 -- .idea/compiler.xml | 34 ------------------- .idea/copilot.data.migration.agent.xml | 6 ---- .idea/copilot.data.migration.ask2agent.xml | 6 ---- .idea/copilot.data.migration.edit.xml | 6 ---- .idea/discord.xml | 14 -------- .idea/gradle.xml | 19 ----------- .idea/kotlinc.xml | 7 ---- .idea/material_theme_project_new.xml | 12 ------- .idea/misc.xml | 7 ---- .../surf-friends.surf-friends-api.main.iml | 13 ------- .../surf-friends.surf-friends-core.main.iml | 13 ------- .idea/vcs.xml | 12 ------- 13 files changed, 152 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copilot.data.migration.agent.xml delete mode 100644 .idea/copilot.data.migration.ask2agent.xml delete mode 100644 .idea/copilot.data.migration.edit.xml delete mode 100644 .idea/discord.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/kotlinc.xml delete mode 100644 .idea/material_theme_project_new.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules/surf-friends-api/surf-friends.surf-friends-api.main.iml delete mode 100644 .idea/modules/surf-friends-core/surf-friends.surf-friends-core.main.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index ec3c8f7..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml deleted file mode 100644 index 4ea72a9..0000000 --- a/.idea/copilot.data.migration.agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml deleted file mode 100644 index 1f2ea11..0000000 --- a/.idea/copilot.data.migration.ask2agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml deleted file mode 100644 index 8648f94..0000000 --- a/.idea/copilot.data.migration.edit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml deleted file mode 100644 index 912db82..0000000 --- a/.idea/discord.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 0e688fa..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index f203762..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml deleted file mode 100644 index 5adc677..0000000 --- a/.idea/material_theme_project_new.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index ad18843..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/modules/surf-friends-api/surf-friends.surf-friends-api.main.iml b/.idea/modules/surf-friends-api/surf-friends.surf-friends-api.main.iml deleted file mode 100644 index a376b96..0000000 --- a/.idea/modules/surf-friends-api/surf-friends.surf-friends-api.main.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - ADVENTURE - - 1 - - - - \ No newline at end of file diff --git a/.idea/modules/surf-friends-core/surf-friends.surf-friends-core.main.iml b/.idea/modules/surf-friends-core/surf-friends.surf-friends-core.main.iml deleted file mode 100644 index a376b96..0000000 --- a/.idea/modules/surf-friends-core/surf-friends.surf-friends-core.main.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - ADVENTURE - - 1 - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 7ddfc9e..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file From 8dd74f199200b47f527b0eb4ad49325a3afd14ae Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 17:14:43 +0100 Subject: [PATCH 06/43] feat: Implement database loader and friend management tables with serialization support --- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 3 +- .../surf/friends/api/friend/FriendRequest.kt | 11 ++++--- .../surf/friends/api/friend/Friendship.kt | 11 ++++--- .../surf/friends/api/player/FriendPlayer.kt | 5 ++- surf-friends-backend/build.gradle.kts | 11 +++++++ .../friends/backend/DatabaseLoaderImpl.kt | 32 +++++++++++++++++++ .../backend/table/FriendPlayerTable.kt | 10 ++++++ .../backend/table/FriendRequestsTable.kt | 8 +++++ .../friends/backend/table/FriendShipsTable.kt | 8 +++++ .../friends/core/loader/DatabaseLoader.kt | 11 +++++++ .../friends/core/service/FriendService.kt | 17 +--------- 12 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 surf-friends-backend/build.gradle.kts create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/DatabaseLoaderImpl.kt create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendPlayerTable.kt create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt create mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/DatabaseLoader.kt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f670658..103a61b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,4 +4,4 @@ distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-9.4. networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7e0f1cd..0b8a4c9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,4 +2,5 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } include("surf-friends-api") -include("surf-friends-core") \ No newline at end of file +include("surf-friends-core") +include("surf-friends-backend") \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt index efb78fd..ef4dea9 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt @@ -1,10 +1,13 @@ package dev.slne.surf.friends.api.friend -import java.time.OffsetDateTime +import dev.slne.surf.surfapi.core.api.serializer.java.datetime.datetime.offset.SerializableOffsetDateTime +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable import java.util.* +@Serializable data class FriendRequest( - val senderUuid: UUID, - val receiverUuid: UUID, - val sentAt: OffsetDateTime + val senderUuid: @Contextual UUID, + val receiverUuid: @Contextual UUID, + val sentAt: SerializableOffsetDateTime ) \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index 871453b..9852d98 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -1,10 +1,13 @@ package dev.slne.surf.friends.api.friend -import java.time.OffsetDateTime +import dev.slne.surf.surfapi.core.api.serializer.java.datetime.datetime.offset.SerializableOffsetDateTime +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable import java.util.* +@Serializable data class Friendship( - val requestedBy: UUID, - val acceptedBy: UUID, - val createdAt: OffsetDateTime + val requestedBy: @Contextual UUID, + val acceptedBy: @Contextual UUID, + val createdAt: SerializableOffsetDateTime ) \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt index 93afb7a..eb58dda 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -3,10 +3,13 @@ package dev.slne.surf.friends.api.player import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import it.unimi.dsi.fastutil.objects.ObjectSet +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable import java.util.* +@Serializable data class FriendPlayer( - val uuid: UUID, + val uuid: @Contextual UUID, val name: String, val friends: ObjectSet, diff --git a/surf-friends-backend/build.gradle.kts b/surf-friends-backend/build.gradle.kts new file mode 100644 index 0000000..0e10efe --- /dev/null +++ b/surf-friends-backend/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("dev.slne.surf.surfapi.gradle.paper-raw") +} + +dependencies { + api(project(":surf-friends-core")) +} + +surfRawPaperApi { + withSurfDatabaseR2dbc("1.3.0", "dev.slne.surf.friends.libs") +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/DatabaseLoaderImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/DatabaseLoaderImpl.kt new file mode 100644 index 0000000..193127b --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/DatabaseLoaderImpl.kt @@ -0,0 +1,32 @@ +package dev.slne.surf.friends.backend + +import com.google.auto.service.AutoService +import dev.slne.surf.database.DatabaseApi +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.SchemaUtils +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction +import dev.slne.surf.friends.backend.table.FriendPlayerTable +import dev.slne.surf.friends.backend.table.FriendRequestsTable +import dev.slne.surf.friends.backend.table.FriendShipsTable +import dev.slne.surf.friends.core.loader.DatabaseLoader +import net.kyori.adventure.util.Services +import java.nio.file.Path + +@AutoService(DatabaseLoader::class) +class DatabaseLoaderImpl : DatabaseLoader, Services.Fallback { + lateinit var databaseApi: DatabaseApi + override suspend fun connect(dataPath: Path) { + databaseApi = DatabaseApi.create(dataPath) + + suspendTransaction { + SchemaUtils.create( + FriendPlayerTable, + FriendRequestsTable, + FriendShipsTable + ) + } + } + + override fun disconnect() { + databaseApi.shutdown() + } +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendPlayerTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendPlayerTable.kt new file mode 100644 index 0000000..2558cad --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendPlayerTable.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.backend.table + +import dev.slne.surf.database.columns.nativeUuid +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.dao.id.LongIdTable + +object FriendPlayerTable : LongIdTable("friend_players") { + val playerUuid = nativeUuid("player_uuid").uniqueIndex() + val playerName = varchar("player_name", 16) + val texture = largeText("texture") +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt new file mode 100644 index 0000000..8facf47 --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.friends.backend.table + +import dev.slne.surf.database.table.AuditableLongIdTable + +object FriendRequestsTable : AuditableLongIdTable("friend_requests") { + val senderId = long("sender_id").references(FriendPlayerTable.id) + val receiverId = long("receiver_id").references(FriendPlayerTable.id) +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt new file mode 100644 index 0000000..cdda8b8 --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.friends.backend.table + +import dev.slne.surf.database.table.AuditableLongIdTable + +object FriendShipsTable : AuditableLongIdTable("friend_ships") { + val requesterId = long("requester_id").references(FriendPlayerTable.id) + val acceptorId = long("acceptor_id").references(FriendPlayerTable.id) +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/DatabaseLoader.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/DatabaseLoader.kt new file mode 100644 index 0000000..312ae53 --- /dev/null +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/DatabaseLoader.kt @@ -0,0 +1,11 @@ +package dev.slne.surf.friends.core.loader + +import dev.slne.surf.surfapi.core.api.util.requiredService +import java.nio.file.Path + +val databaseLoader = requiredService() + +interface DatabaseLoader { + suspend fun connect(dataPath: Path) + fun disconnect() +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt index ad68938..727ee77 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt @@ -1,25 +1,10 @@ package dev.slne.surf.friends.core.service -import dev.slne.surf.friends.api.friend.FriendRequest -import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.surfapi.core.api.util.requiredService -import it.unimi.dsi.fastutil.objects.ObjectSet -import java.util.* val friendService get() = requiredService() interface FriendService { - suspend fun createFriendship(uuid: UUID, friend: UUID): Friendship - suspend fun removeFriendship(uuid: UUID, friend: UUID) - suspend fun getFriendship(playerA: UUID, playerB: UUID): Friendship? - suspend fun areFriends(uuid: UUID, friend: UUID): Friendship? - suspend fun getFriendships(uuid: UUID): ObjectSet - suspend fun sendFriendRequest(sender: UUID, receiver: UUID): FriendRequest - suspend fun acceptFriendRequest(sender: UUID, receiver: UUID) - suspend fun declineFriendRequest(sender: UUID, receiver: UUID) - suspend fun revokeFriendRequest(sender: UUID, receiver: UUID) - suspend fun getSentFriendRequests(uuid: UUID): ObjectSet - suspend fun getReceivedFriendRequests(uuid: UUID): ObjectSet - suspend fun getFriendRequest(sender: UUID, target: UUID): FriendRequest? + } From 3c1d453bf1afac8a95bba5f48dd9fd20645af86a Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 17:26:36 +0100 Subject: [PATCH 07/43] feat: Update FriendRequestsTable and FriendShipsTable to use UUIDs for player references --- .../slne/surf/friends/backend/table/FriendRequestsTable.kt | 5 +++-- .../dev/slne/surf/friends/backend/table/FriendShipsTable.kt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt index 8facf47..f6b9651 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt @@ -1,8 +1,9 @@ package dev.slne.surf.friends.backend.table +import dev.slne.surf.database.columns.nativeUuid import dev.slne.surf.database.table.AuditableLongIdTable object FriendRequestsTable : AuditableLongIdTable("friend_requests") { - val senderId = long("sender_id").references(FriendPlayerTable.id) - val receiverId = long("receiver_id").references(FriendPlayerTable.id) + val senderUuid = nativeUuid("sender_uuid").references(FriendPlayerTable.playerUuid) + val receiverUuid = nativeUuid("receiver_uuid").references(FriendPlayerTable.playerUuid) } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt index cdda8b8..d1dd3a1 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt @@ -1,8 +1,9 @@ package dev.slne.surf.friends.backend.table +import dev.slne.surf.database.columns.nativeUuid import dev.slne.surf.database.table.AuditableLongIdTable object FriendShipsTable : AuditableLongIdTable("friend_ships") { - val requesterId = long("requester_id").references(FriendPlayerTable.id) - val acceptorId = long("acceptor_id").references(FriendPlayerTable.id) + val requesterUuid = nativeUuid("requester_uuid").references(FriendPlayerTable.playerUuid) + val acceptorUuid = nativeUuid("acceptor_uuid").references(FriendPlayerTable.playerUuid) } \ No newline at end of file From ffeb6ff6c24c4da3f755ef4232652451c0281b2a Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 17:26:41 +0100 Subject: [PATCH 08/43] feat: Add FriendRequestRepository for managing friend request data --- .../repository/FriendRequestRepository.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt new file mode 100644 index 0000000..c00b6c3 --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt @@ -0,0 +1,33 @@ +package dev.slne.surf.friends.backend.repository + +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.backend.table.FriendRequestsTable +import dev.slne.surf.surfapi.core.api.util.toObjectSet +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toSet +import java.util.* + +val friendRequestRepository = FriendRequestRepository() + +class FriendRequestRepository { + suspend fun loadSentRequests(uuid: UUID) = suspendTransaction { + FriendRequestsTable.selectAll().where(FriendRequestsTable.senderUuid eq uuid) + .map { createRequest(it) } + }.toSet().toObjectSet() + + suspend fun loadReceivedRequests(uuid: UUID) = suspendTransaction { + FriendRequestsTable.selectAll().where(FriendRequestsTable.receiverUuid eq uuid) + .map { createRequest(it) } + }.toSet().toObjectSet() + + + private fun createRequest(row: ResultRow) = FriendRequest( + senderUuid = row[FriendRequestsTable.senderUuid], + receiverUuid = row[FriendRequestsTable.receiverUuid], + sentAt = row[FriendRequestsTable.createdAt] + ) +} \ No newline at end of file From 261a54fe699595a642b4bdc97d85398944902238 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 17:45:35 +0100 Subject: [PATCH 09/43] feat: Add FriendPlayerRepository and FriendShipRepository for managing player and friendship data --- .../repository/FriendPlayerRepository.kt | 63 +++++++++++++++++++ .../repository/FriendShipRepository.kt | 29 +++++++++ 2 files changed, 92 insertions(+) create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt new file mode 100644 index 0000000..d9d054d --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -0,0 +1,63 @@ +package dev.slne.surf.friends.backend.repository + +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.backend.table.FriendPlayerTable +import it.unimi.dsi.fastutil.objects.ObjectSet +import kotlinx.coroutines.flow.firstOrNull +import org.bukkit.entity.Player + +val friendPlayerRepository = FriendPlayerRepository() + +class FriendPlayerRepository { + suspend fun loadOrCreatePlayer(player: Player) = suspendTransaction { + val uuid = player.uniqueId + val row = + FriendPlayerTable.selectAll().where(FriendPlayerTable.playerUuid eq uuid).firstOrNull() + + if (row == null) { + FriendPlayerTable.insert { + it[playerUuid] = uuid + it[playerName] = player.name + it[texture] = + player.playerProfile.properties.find { property -> property.name == "textures" }?.value + ?: "" + } + + createPlayer( + row = FriendPlayerTable.selectAll().where(FriendPlayerTable.playerUuid eq uuid) + .firstOrNull() ?: error("Failed to load created player with uuid $uuid"), + sentRequests = friendRequestRepository.loadSentRequests(uuid), + receivedRequests = friendRequestRepository.loadReceivedRequests(uuid), + friendShips = friendShipRepository.loadFriendShips(uuid) + ) + } else { + createPlayer( + row = row, + sentRequests = friendRequestRepository.loadSentRequests(uuid), + receivedRequests = friendRequestRepository.loadReceivedRequests(uuid), + friendShips = friendShipRepository.loadFriendShips(uuid) + ) + } + } + + private fun createPlayer( + row: ResultRow, + sentRequests: ObjectSet, + receivedRequests: ObjectSet, + friendShips: ObjectSet + ) = + FriendPlayer( + uuid = row[FriendPlayerTable.playerUuid], + name = row[FriendPlayerTable.playerName], + sentFriendRequests = sentRequests, + receivedFriendRequests = receivedRequests, + friends = friendShips + ) +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt new file mode 100644 index 0000000..4cf633f --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt @@ -0,0 +1,29 @@ +package dev.slne.surf.friends.backend.repository + +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.or +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction +import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.friends.backend.table.FriendShipsTable +import dev.slne.surf.surfapi.core.api.util.toObjectSet +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toSet +import java.util.* + +val friendShipRepository = FriendShipRepository() + +class FriendShipRepository { + suspend fun loadFriendShips(uuid: UUID) = suspendTransaction { + FriendShipsTable.selectAll().where { + (FriendShipsTable.requesterUuid eq uuid) or (FriendShipsTable.acceptorUuid eq uuid) + }.map { createFriendShip(it) } + }.toSet().toObjectSet() + + private fun createFriendShip(row: ResultRow) = Friendship( + requestedBy = row[FriendShipsTable.requesterUuid], + acceptedBy = row[FriendShipsTable.acceptorUuid], + createdAt = row[FriendShipsTable.createdAt] + ) +} \ No newline at end of file From 7815ee5c11f48b8bed782ae53c808deba671f695 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 17:59:37 +0100 Subject: [PATCH 10/43] feat: Add savePlayer function to FriendPlayerRepository for persisting player data --- .../surf/friends/backend/repository/FriendPlayerRepository.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index d9d054d..b1f3f67 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -47,6 +47,10 @@ class FriendPlayerRepository { } } + suspend fun savePlayer(player: FriendPlayer) = suspendTransaction { + + } + private fun createPlayer( row: ResultRow, sentRequests: ObjectSet, From 1c9af0597e997f073d2312a1887015e813d756a8 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 18:03:08 +0100 Subject: [PATCH 11/43] feat: Add texture field to FriendPlayer and implement save functions in repositories --- .../dev/slne/surf/friends/api/player/FriendPlayer.kt | 1 + .../friends/backend/repository/FriendPlayerRepository.kt | 7 ++++++- .../backend/repository/FriendRequestRepository.kt | 8 ++++++++ .../friends/backend/repository/FriendShipRepository.kt | 9 +++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt index eb58dda..f870086 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -11,6 +11,7 @@ import java.util.* data class FriendPlayer( val uuid: @Contextual UUID, val name: String, + val texture: String, val friends: ObjectSet, val sentFriendRequests: ObjectSet, diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index b1f3f67..4fbd267 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -48,7 +48,11 @@ class FriendPlayerRepository { } suspend fun savePlayer(player: FriendPlayer) = suspendTransaction { - + FriendPlayerTable.insert { + it[playerUuid] = player.uuid + it[playerName] = player.name + it[texture] = player.texture + } } private fun createPlayer( @@ -60,6 +64,7 @@ class FriendPlayerRepository { FriendPlayer( uuid = row[FriendPlayerTable.playerUuid], name = row[FriendPlayerTable.playerName], + texture = row[FriendPlayerTable.texture], sentFriendRequests = sentRequests, receivedFriendRequests = receivedRequests, friends = friendShips diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt index c00b6c3..abdfe85 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.backend.repository import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction import dev.slne.surf.friends.api.friend.FriendRequest @@ -24,6 +25,13 @@ class FriendRequestRepository { .map { createRequest(it) } }.toSet().toObjectSet() + suspend fun saveRequest(request: FriendRequest) = suspendTransaction { + FriendRequestsTable.insert { + it[senderUuid] = request.senderUuid + it[receiverUuid] = request.receiverUuid + it[createdAt] = request.sentAt + } + } private fun createRequest(row: ResultRow) = FriendRequest( senderUuid = row[FriendRequestsTable.senderUuid], diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt index 4cf633f..589473e 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt @@ -3,6 +3,7 @@ package dev.slne.surf.friends.backend.repository import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.or +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction import dev.slne.surf.friends.api.friend.Friendship @@ -21,6 +22,14 @@ class FriendShipRepository { }.map { createFriendShip(it) } }.toSet().toObjectSet() + suspend fun saveFriendship(friendship: Friendship) = suspendTransaction { + FriendShipsTable.insert { + it[requesterUuid] = friendship.requestedBy + it[acceptorUuid] = friendship.acceptedBy + it[createdAt] = friendship.createdAt + } + } + private fun createFriendShip(row: ResultRow) = Friendship( requestedBy = row[FriendShipsTable.requesterUuid], acceptedBy = row[FriendShipsTable.acceptorUuid], From a738d5da2c8144af633d830ada8e12ae91e29bbc Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 18:09:43 +0100 Subject: [PATCH 12/43] feat: Implement FriendPlayerService and FriendRequestService interfaces with caching and player management functions --- .../service/FriendPlayerServiceImpl.kt | 35 +++++++++++++++++++ .../core/service/FriendPlayerService.kt | 7 ++-- .../core/service/FriendRequestService.kt | 10 ++++++ .../friends/core/service/FriendService.kt | 10 ------ .../friends/core/service/FriendShipService.kt | 10 ++++++ 5 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt create mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt delete mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt create mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt new file mode 100644 index 0000000..2504487 --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -0,0 +1,35 @@ +package dev.slne.surf.friends.backend.service + +import com.github.benmanes.caffeine.cache.Caffeine +import com.google.auto.service.AutoService +import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.core.service.FriendPlayerService +import dev.slne.surf.surfapi.core.api.util.toObjectSet +import it.unimi.dsi.fastutil.objects.ObjectSet +import net.kyori.adventure.util.Services +import org.bukkit.entity.Player +import java.util.* + +@AutoService(FriendPlayerService::class) +class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { + val playerCache = Caffeine.newBuilder().build() + + override val players: ObjectSet + get() = playerCache.asMap().values.toObjectSet() + + override fun cachePlayer(friendPlayer: FriendPlayer) { + TODO("Not yet implemented") + } + + override fun invalidatePlayer(uuid: UUID) { + TODO("Not yet implemented") + } + + override suspend fun loadOrCreatePlayer(player: Player): FriendPlayer { + TODO("Not yet implemented") + } + + override suspend fun savePlayer(friendPlayer: FriendPlayer) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index b9c7667..b2a4159 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -3,16 +3,17 @@ package dev.slne.surf.friends.core.service import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet +import org.bukkit.entity.Player import java.util.* val friendPlayerService get() = requiredService() interface FriendPlayerService { + val players: ObjectSet + fun cachePlayer(friendPlayer: FriendPlayer) fun invalidatePlayer(uuid: UUID) - val players: ObjectSet - - suspend fun loadPlayer(uuid: UUID): FriendPlayer + suspend fun loadOrCreatePlayer(player: Player): FriendPlayer suspend fun savePlayer(friendPlayer: FriendPlayer) } \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt new file mode 100644 index 0000000..6ed43fa --- /dev/null +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.core.service + +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.surfapi.core.api.util.requiredService + +val friendRequestService = requiredService() + +interface FriendRequestService { + suspend fun saveFriendRequest(friendRequest: FriendRequest) +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt deleted file mode 100644 index 727ee77..0000000 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slne.surf.friends.core.service - -import dev.slne.surf.surfapi.core.api.util.requiredService - -val friendService get() = requiredService() - -interface FriendService { - -} - diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt new file mode 100644 index 0000000..6e7bcd0 --- /dev/null +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.core.service + +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.surfapi.core.api.util.requiredService + +val friendShipService = requiredService() + +interface FriendShipService { + suspend fun saveFriendRequest(friendRequest: FriendRequest) +} \ No newline at end of file From 1af26c21e6af1fd612f0f24b1bd2d2bcc3c50008 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 18:47:07 +0100 Subject: [PATCH 13/43] feat: Implement FriendRequestService and FriendShipService with repository integration --- .../backend/service/FriendPlayerServiceImpl.kt | 16 ++++++++++++---- .../backend/service/FriendRequestServiceImpl.kt | 14 ++++++++++++++ .../backend/service/FriendShipServiceImpl.kt | 14 ++++++++++++++ .../friends/core/service/FriendShipService.kt | 4 ++-- 4 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt create mode 100644 surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 2504487..50c8275 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -3,6 +3,7 @@ package dev.slne.surf.friends.backend.service import com.github.benmanes.caffeine.cache.Caffeine import com.google.auto.service.AutoService import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.backend.repository.friendPlayerRepository import dev.slne.surf.friends.core.service.FriendPlayerService import dev.slne.surf.surfapi.core.api.util.toObjectSet import it.unimi.dsi.fastutil.objects.ObjectSet @@ -18,18 +19,25 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { get() = playerCache.asMap().values.toObjectSet() override fun cachePlayer(friendPlayer: FriendPlayer) { - TODO("Not yet implemented") + playerCache.put(friendPlayer.uuid, friendPlayer) } override fun invalidatePlayer(uuid: UUID) { - TODO("Not yet implemented") + playerCache.invalidate(uuid) } override suspend fun loadOrCreatePlayer(player: Player): FriendPlayer { - TODO("Not yet implemented") + val cachedPlayer = playerCache.getIfPresent(player.uniqueId) + if (cachedPlayer != null) { + return cachedPlayer + } + + val loadedPlayer = friendPlayerRepository.loadOrCreatePlayer(player) + cachePlayer(loadedPlayer) + return loadedPlayer } override suspend fun savePlayer(friendPlayer: FriendPlayer) { - TODO("Not yet implemented") + friendPlayerRepository.savePlayer(friendPlayer) } } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt new file mode 100644 index 0000000..15919ca --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt @@ -0,0 +1,14 @@ +package dev.slne.surf.friends.backend.service + +import com.google.auto.service.AutoService +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.backend.repository.friendRequestRepository +import dev.slne.surf.friends.core.service.FriendRequestService +import net.kyori.adventure.util.Services + +@AutoService(FriendRequestService::class) +class FriendRequestServiceImpl : FriendRequestService, Services.Fallback { + override suspend fun saveFriendRequest(friendRequest: FriendRequest) { + friendRequestRepository.saveRequest(friendRequest) + } +} \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt new file mode 100644 index 0000000..0f3be13 --- /dev/null +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt @@ -0,0 +1,14 @@ +package dev.slne.surf.friends.backend.service + +import com.google.auto.service.AutoService +import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.friends.backend.repository.friendShipRepository +import dev.slne.surf.friends.core.service.FriendShipService +import net.kyori.adventure.util.Services + +@AutoService(FriendShipService::class) +class FriendShipServiceImpl : FriendShipService, Services.Fallback { + override suspend fun saveFriendShip(friendShip: Friendship) { + friendShipRepository.saveFriendship(friendShip) + } +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt index 6e7bcd0..65e6f7b 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt @@ -1,10 +1,10 @@ package dev.slne.surf.friends.core.service -import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.surfapi.core.api.util.requiredService val friendShipService = requiredService() interface FriendShipService { - suspend fun saveFriendRequest(friendRequest: FriendRequest) + suspend fun saveFriendShip(friendShip: Friendship) } \ No newline at end of file From 8a33b1c4f73df9babae6c904b7dce1729265a23f Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 19:22:25 +0100 Subject: [PATCH 14/43] feat: Add surf-friends-paper module to project settings --- settings.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0b8a4c9..e376460 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,4 +3,5 @@ plugins { } include("surf-friends-api") include("surf-friends-core") -include("surf-friends-backend") \ No newline at end of file +include("surf-friends-backend") +include("surf-friends-paper") \ No newline at end of file From 3b36af00dd3092b25a1c17aceba6a72a1f2ed794 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 20:01:29 +0100 Subject: [PATCH 15/43] feat: Add Paper plugin integration and player connection handling --- .../repository/FriendPlayerRepository.kt | 11 +++++---- .../service/FriendPlayerServiceImpl.kt | 9 ++++---- .../core/service/FriendPlayerService.kt | 3 ++- surf-friends-paper/build.gradle.kts | 17 ++++++++++++++ .../dev/slne/surf/friends/paper/PaperMain.kt | 17 ++++++++++++++ .../listener/PlayerConnectionListener.kt | 23 +++++++++++++++++++ .../paper/listener/SurfPlayerListener.kt | 17 ++++++++++++++ 7 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 surf-friends-paper/build.gradle.kts create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index 4fbd267..a96af10 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -1,5 +1,6 @@ package dev.slne.surf.friends.backend.repository +import com.destroystokyo.paper.profile.PlayerProfile import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert @@ -9,24 +10,24 @@ import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.backend.table.FriendPlayerTable +import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import it.unimi.dsi.fastutil.objects.ObjectSet import kotlinx.coroutines.flow.firstOrNull -import org.bukkit.entity.Player val friendPlayerRepository = FriendPlayerRepository() class FriendPlayerRepository { - suspend fun loadOrCreatePlayer(player: Player) = suspendTransaction { - val uuid = player.uniqueId + suspend fun loadOrCreatePlayer(profile: PlayerProfile) = suspendTransaction { + val uuid = profile.idOrThrow() val row = FriendPlayerTable.selectAll().where(FriendPlayerTable.playerUuid eq uuid).firstOrNull() if (row == null) { FriendPlayerTable.insert { it[playerUuid] = uuid - it[playerName] = player.name + it[playerName] = profile.name ?: error("Profile name is null for uuid $uuid") it[texture] = - player.playerProfile.properties.find { property -> property.name == "textures" }?.value + profile.properties.find { property -> property.name == "textures" }?.value ?: "" } diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 50c8275..7fb0980 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -1,14 +1,15 @@ package dev.slne.surf.friends.backend.service +import com.destroystokyo.paper.profile.PlayerProfile import com.github.benmanes.caffeine.cache.Caffeine import com.google.auto.service.AutoService import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.backend.repository.friendPlayerRepository import dev.slne.surf.friends.core.service.FriendPlayerService +import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import dev.slne.surf.surfapi.core.api.util.toObjectSet import it.unimi.dsi.fastutil.objects.ObjectSet import net.kyori.adventure.util.Services -import org.bukkit.entity.Player import java.util.* @AutoService(FriendPlayerService::class) @@ -26,13 +27,13 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { playerCache.invalidate(uuid) } - override suspend fun loadOrCreatePlayer(player: Player): FriendPlayer { - val cachedPlayer = playerCache.getIfPresent(player.uniqueId) + override suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer { + val cachedPlayer = playerCache.getIfPresent(profile.idOrThrow()) if (cachedPlayer != null) { return cachedPlayer } - val loadedPlayer = friendPlayerRepository.loadOrCreatePlayer(player) + val loadedPlayer = friendPlayerRepository.loadOrCreatePlayer(profile) cachePlayer(loadedPlayer) return loadedPlayer } diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index b2a4159..5f2a86d 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -1,5 +1,6 @@ package dev.slne.surf.friends.core.service +import com.destroystokyo.paper.profile.PlayerProfile import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet @@ -14,6 +15,6 @@ interface FriendPlayerService { fun cachePlayer(friendPlayer: FriendPlayer) fun invalidatePlayer(uuid: UUID) - suspend fun loadOrCreatePlayer(player: Player): FriendPlayer + suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer suspend fun savePlayer(friendPlayer: FriendPlayer) } \ No newline at end of file diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts new file mode 100644 index 0000000..7223ab5 --- /dev/null +++ b/surf-friends-paper/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("dev.slne.surf.surfapi.gradle.paper-plugin") +} + + +surfPaperPluginApi { + mainClass("dev.slne.surf.friends.paper.PaperMain") + generateLibraryLoader(false) + foliaSupported(true) + withCorePaper() + + authors.add("red") +} + +dependencies { + api(project(":surf-friends-core")) +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt new file mode 100644 index 0000000..1e70498 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -0,0 +1,17 @@ +package dev.slne.surf.friends.paper + +import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin +import dev.slne.surf.core.api.common.surfCoreApi +import dev.slne.surf.friends.paper.listener.PlayerConnectionListener +import dev.slne.surf.friends.paper.listener.SurfPlayerListener +import dev.slne.surf.surfapi.bukkit.api.event.register +import org.bukkit.plugin.java.JavaPlugin + +val plugin get() = JavaPlugin.getPlugin(PaperMain::class.java) + +class PaperMain : SuspendingJavaPlugin() { + override suspend fun onEnableAsync() { + PlayerConnectionListener.register() + surfCoreApi.registerListener(SurfPlayerListener) + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt new file mode 100644 index 0000000..fbd819d --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt @@ -0,0 +1,23 @@ +package dev.slne.surf.friends.paper.listener + +import dev.slne.surf.friends.core.service.friendPlayerService +import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent +import kotlinx.coroutines.runBlocking +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerQuitEvent + +@Suppress("UnstableApiUsage") +object PlayerConnectionListener : Listener { + @EventHandler + fun onJoin(event: AsyncPlayerConnectionConfigureEvent) { + runBlocking { + friendPlayerService.cachePlayer(friendPlayerService.loadOrCreatePlayer(event.connection.profile)) + } + } + + @EventHandler + fun onQuit(event: PlayerQuitEvent) { + friendPlayerService.invalidatePlayer(event.player.uniqueId) + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt new file mode 100644 index 0000000..8a97ce0 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt @@ -0,0 +1,17 @@ +package dev.slne.surf.friends.paper.listener + +import dev.slne.surf.core.api.common.event.SurfEventHandler +import dev.slne.surf.core.api.common.event.SurfPlayerConnectEvent +import dev.slne.surf.core.api.common.event.SurfPlayerDisconnectEvent + +object SurfPlayerListener { + @SurfEventHandler + fun onNetworkConnect(event: SurfPlayerConnectEvent) { + // TODO: Broadcast message to friends + } + + @SurfEventHandler + fun onNetworkDisconnect(event: SurfPlayerDisconnectEvent) { + // TODO: Broadcast message to friends + } +} \ No newline at end of file From b433ea5a69545ae64b8195e1abdd3678088fcd3f Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Wed, 25 Feb 2026 20:31:51 +0100 Subject: [PATCH 16/43] feat: Add friend command and permission registry for surf friends integration --- .../kotlin/dev/slne/surf/friends/paper/PaperMain.kt | 3 +++ .../slne/surf/friends/paper/command/FriendCommand.kt | 8 ++++++++ .../friends/paper/permission/PermissionRegistry.kt | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt index 1e70498..b9ee991 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.paper import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin import dev.slne.surf.core.api.common.surfCoreApi +import dev.slne.surf.friends.paper.command.friendCommand import dev.slne.surf.friends.paper.listener.PlayerConnectionListener import dev.slne.surf.friends.paper.listener.SurfPlayerListener import dev.slne.surf.surfapi.bukkit.api.event.register @@ -13,5 +14,7 @@ class PaperMain : SuspendingJavaPlugin() { override suspend fun onEnableAsync() { PlayerConnectionListener.register() surfCoreApi.registerListener(SurfPlayerListener) + + friendCommand() } } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt new file mode 100644 index 0000000..0a7413d --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.friends.paper.command + +import dev.jorel.commandapi.kotlindsl.commandTree +import dev.slne.surf.friends.paper.permission.PermissionRegistry + +fun friendCommand() = commandTree("friend") { + withPermission(PermissionRegistry.PREFIX_COMMAND) +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt new file mode 100644 index 0000000..5653cd9 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/permission/PermissionRegistry.kt @@ -0,0 +1,10 @@ +package dev.slne.surf.friends.paper.permission + +import dev.slne.surf.surfapi.bukkit.api.permission.PermissionRegistry + +object PermissionRegistry : PermissionRegistry() { + const val PREFIX = "surf.friends" + const val PREFIX_COMMAND = "$PREFIX.command" + + val COMMAND = create(PREFIX_COMMAND) +} \ No newline at end of file From 9e86008b1bf403fb7c3ad35c819c727c8987bf96 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:12:16 +0100 Subject: [PATCH 17/43] feat: Integrate Redis for player caching and enhance friend command functionality --- .../surf/friends/api/friend/Friendship.kt | 4 ++ surf-friends-backend/build.gradle.kts | 1 + .../repository/FriendPlayerRepository.kt | 14 +++++ .../repository/FriendShipRepository.kt | 4 ++ .../service/FriendPlayerServiceImpl.kt | 20 +++++- .../friends/backend/table/FriendShipsTable.kt | 3 + surf-friends-core/build.gradle.kts | 5 ++ .../surf/friends/core/loader/RedisLoader.kt | 30 +++++++++ .../core/service/FriendPlayerService.kt | 4 +- surf-friends-paper/build.gradle.kts | 1 + .../dev/slne/surf/friends/paper/PaperMain.kt | 10 +++ .../friends/paper/command/FriendCommand.kt | 32 ++++++++++ .../command/argument/FriendPlayerArgument.kt | 53 ++++++++++++++++ .../command/argument/OfflineFriendArgument.kt | 63 +++++++++++++++++++ 14 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index 9852d98..4e66254 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -9,5 +9,9 @@ import java.util.* data class Friendship( val requestedBy: @Contextual UUID, val acceptedBy: @Contextual UUID, + + val requesterName: String, + val acceptorName: String, + val createdAt: SerializableOffsetDateTime ) \ No newline at end of file diff --git a/surf-friends-backend/build.gradle.kts b/surf-friends-backend/build.gradle.kts index 0e10efe..ef4f336 100644 --- a/surf-friends-backend/build.gradle.kts +++ b/surf-friends-backend/build.gradle.kts @@ -8,4 +8,5 @@ dependencies { surfRawPaperApi { withSurfDatabaseR2dbc("1.3.0", "dev.slne.surf.friends.libs") + withSurfRedis() } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index a96af10..977c976 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -48,6 +48,20 @@ class FriendPlayerRepository { } } + suspend fun loadPlayer(name: String) = suspendTransaction { + FriendPlayerTable.selectAll().where(FriendPlayerTable.playerName eq name).firstOrNull() + ?.let { + val uuid = it[FriendPlayerTable.playerUuid] + + createPlayer( + row = it, + sentRequests = friendRequestRepository.loadSentRequests(uuid), + receivedRequests = friendRequestRepository.loadReceivedRequests(uuid), + friendShips = friendShipRepository.loadFriendShips(uuid) + ) + } + } + suspend fun savePlayer(player: FriendPlayer) = suspendTransaction { FriendPlayerTable.insert { it[playerUuid] = player.uuid diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt index 589473e..a6e2ec4 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt @@ -26,6 +26,8 @@ class FriendShipRepository { FriendShipsTable.insert { it[requesterUuid] = friendship.requestedBy it[acceptorUuid] = friendship.acceptedBy + it[requesterName] = friendship.requesterName + it[acceptorName] = friendship.acceptorName it[createdAt] = friendship.createdAt } } @@ -33,6 +35,8 @@ class FriendShipRepository { private fun createFriendShip(row: ResultRow) = Friendship( requestedBy = row[FriendShipsTable.requesterUuid], acceptedBy = row[FriendShipsTable.acceptorUuid], + requesterName = row[FriendShipsTable.requesterName], + acceptorName = row[FriendShipsTable.acceptorName], createdAt = row[FriendShipsTable.createdAt] ) } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 7fb0980..8a5c316 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -1,10 +1,10 @@ package dev.slne.surf.friends.backend.service import com.destroystokyo.paper.profile.PlayerProfile -import com.github.benmanes.caffeine.cache.Caffeine import com.google.auto.service.AutoService import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.backend.repository.friendPlayerRepository +import dev.slne.surf.friends.core.loader.redisApi import dev.slne.surf.friends.core.service.FriendPlayerService import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import dev.slne.surf.surfapi.core.api.util.toObjectSet @@ -14,10 +14,10 @@ import java.util.* @AutoService(FriendPlayerService::class) class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { - val playerCache = Caffeine.newBuilder().build() + val playerCache = redisApi.createSyncMap("surf-friends:player-cache") override val players: ObjectSet - get() = playerCache.asMap().values.toObjectSet() + get() = playerCache.snapshot().values.toObjectSet() override fun cachePlayer(friendPlayer: FriendPlayer) { playerCache.put(friendPlayer.uuid, friendPlayer) @@ -27,6 +27,8 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { playerCache.invalidate(uuid) } + override fun init() {} + override suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer { val cachedPlayer = playerCache.getIfPresent(profile.idOrThrow()) if (cachedPlayer != null) { @@ -38,6 +40,18 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { return loadedPlayer } + override suspend fun findOrLoadPlayer(name: String): FriendPlayer? { + val cachedPlayer = playerCache.asMap().values.find { it.name == name } + if (cachedPlayer != null) { + return cachedPlayer + } + + val loadedPlayer = friendPlayerRepository.loadPlayer(name) + + loadedPlayer?.let { cachePlayer(it) } + return loadedPlayer + } + override suspend fun savePlayer(friendPlayer: FriendPlayer) { friendPlayerRepository.savePlayer(friendPlayer) } diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt index d1dd3a1..3b19325 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt @@ -6,4 +6,7 @@ import dev.slne.surf.database.table.AuditableLongIdTable object FriendShipsTable : AuditableLongIdTable("friend_ships") { val requesterUuid = nativeUuid("requester_uuid").references(FriendPlayerTable.playerUuid) val acceptorUuid = nativeUuid("acceptor_uuid").references(FriendPlayerTable.playerUuid) + + val requesterName = varchar("requester_name", 16) + val acceptorName = varchar("acceptor_name", 16) } \ No newline at end of file diff --git a/surf-friends-core/build.gradle.kts b/surf-friends-core/build.gradle.kts index 0363f84..d5bb94a 100644 --- a/surf-friends-core/build.gradle.kts +++ b/surf-friends-core/build.gradle.kts @@ -2,6 +2,11 @@ plugins { id("dev.slne.surf.surfapi.gradle.paper-raw") } +surfRawPaperApi { + withCorePaper() + withSurfRedis() +} + dependencies { api(project(":surf-friends-api")) } \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt new file mode 100644 index 0000000..ca7d89b --- /dev/null +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt @@ -0,0 +1,30 @@ +package dev.slne.surf.friends.core.loader + +import dev.slne.surf.redis.RedisApi + +val redisLoader = RedisLoader() +val redisApi get() = redisLoader.redisApi + +class RedisLoader { + val redisApi = RedisApi.create() + + + fun load() { + } + + fun withRequestResponseHandler(handler: Any) { + redisApi.registerRequestHandler(handler) + } + + fun withListener(listener: Any) { + redisApi.subscribeToEvents(listener) + } + + fun connect() { + redisApi.freezeAndConnect() + } + + fun disconnect() { + redisApi.disconnect() + } +} \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index 5f2a86d..da68650 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -4,7 +4,6 @@ import com.destroystokyo.paper.profile.PlayerProfile import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.surfapi.core.api.util.requiredService import it.unimi.dsi.fastutil.objects.ObjectSet -import org.bukkit.entity.Player import java.util.* val friendPlayerService get() = requiredService() @@ -15,6 +14,9 @@ interface FriendPlayerService { fun cachePlayer(friendPlayer: FriendPlayer) fun invalidatePlayer(uuid: UUID) + fun init() + suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer + suspend fun findOrLoadPlayer(name: String): FriendPlayer? suspend fun savePlayer(friendPlayer: FriendPlayer) } \ No newline at end of file diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts index 7223ab5..ede3e6a 100644 --- a/surf-friends-paper/build.gradle.kts +++ b/surf-friends-paper/build.gradle.kts @@ -8,6 +8,7 @@ surfPaperPluginApi { generateLibraryLoader(false) foliaSupported(true) withCorePaper() + withSurfRedis() authors.add("red") } diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt index b9ee991..585106c 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.paper import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin import dev.slne.surf.core.api.common.surfCoreApi +import dev.slne.surf.friends.core.loader.redisLoader import dev.slne.surf.friends.paper.command.friendCommand import dev.slne.surf.friends.paper.listener.PlayerConnectionListener import dev.slne.surf.friends.paper.listener.SurfPlayerListener @@ -11,10 +12,19 @@ import org.bukkit.plugin.java.JavaPlugin val plugin get() = JavaPlugin.getPlugin(PaperMain::class.java) class PaperMain : SuspendingJavaPlugin() { + override suspend fun onLoadAsync() { + redisLoader.load() + redisLoader.connect() + } + override suspend fun onEnableAsync() { PlayerConnectionListener.register() surfCoreApi.registerListener(SurfPlayerListener) friendCommand() } + + override suspend fun onDisableAsync() { + redisLoader.disconnect() + } } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 0a7413d..885c694 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -1,8 +1,40 @@ package dev.slne.surf.friends.paper.command import dev.jorel.commandapi.kotlindsl.commandTree +import dev.jorel.commandapi.kotlindsl.literalArgument +import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry fun friendCommand() = commandTree("friend") { withPermission(PermissionRegistry.PREFIX_COMMAND) + + literalArgument("add") { + surfOfflinePlayerArgument("target") { + + } + } + + literalArgument("remove") { + + } + + literalArgument("list") { + + } + + literalArgument("requests") { + + } + + literalArgument("accept") { + + } + + literalArgument("decline") { + + } + + literalArgument("revoke") { + + } } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt new file mode 100644 index 0000000..68e0cbc --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt @@ -0,0 +1,53 @@ +package dev.slne.surf.friends.paper.command.argument + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.CommandTree +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import dev.slne.surf.surfapi.core.api.messages.adventure.uuid + +class FriendPlayerArgument(nodeName: String) : + CustomArgument(StringArgument(nodeName), { info -> + friendPlayerService.players.firstOrNull { it.name == info.input } + ?: throw CustomArgumentException.fromAdventureComponent( + buildText { + appendErrorPrefix() + error("Der Spieler wurde nicht gefunden.") + }) + }) { + init { + this.replaceSuggestions( + ArgumentSuggestions.stringCollection { sender -> + friendPlayerService.players.find { it.uuid == sender.sender.uuid() }?.friends?.map { it.acceptorName } + } + ) + } +} + +inline fun CommandTree.friendPlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandTree = then( + FriendPlayerArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun Argument<*>.friendPlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): Argument<*> = then( + FriendPlayerArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun CommandAPICommand.friendPlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = + withArguments(FriendPlayerArgument(nodeName).setOptional(optional).apply(block)) \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt new file mode 100644 index 0000000..e8d9f9f --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt @@ -0,0 +1,63 @@ +package dev.slne.surf.friends.paper.command.argument + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.CommandTree +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.messages.adventure.uuid +import dev.slne.surf.surfapi.core.api.util.logger +import kotlinx.coroutines.* +import kotlinx.coroutines.future.asDeferred +import kotlinx.coroutines.future.future + +class OfflineFriendArgument(nodeName: String) : + CustomArgument, String>(StringArgument(nodeName), { info -> + scope.future { + friendPlayerService.findOrLoadPlayer(info.input) + }.asDeferred() + }) { + init { + this.replaceSuggestions( + ArgumentSuggestions.stringCollection { info -> + friendPlayerService.players.find { it.uuid == info.sender.uuid() }?.friends?.map { it.acceptorName } + } + ) + } + + companion object { + private val log = logger() + private val scope = + CoroutineScope(Dispatchers.IO + CoroutineName("OfflineFriendArgument") + CoroutineExceptionHandler { _, throwable -> + log.atWarning() + .withCause(throwable) + .log("An error occurred in OfflineFriendArgument") + }) + } +} + +inline fun CommandTree.offlineFriendArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandTree = then( + OfflineFriendArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun Argument<*>.offlineFriendArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): Argument<*> = then( + OfflineFriendArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun CommandAPICommand.offlineFriendArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = + withArguments(OfflineFriendArgument(nodeName).setOptional(optional).apply(block)) \ No newline at end of file From 2eeb6948944031487a58e4e9b13044ead392fb88 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:13:17 +0100 Subject: [PATCH 18/43] feat: Refactor player cache handling in FriendPlayerServiceImpl --- .../surf/friends/backend/service/FriendPlayerServiceImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 8a5c316..850c807 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -24,13 +24,13 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { } override fun invalidatePlayer(uuid: UUID) { - playerCache.invalidate(uuid) + playerCache.remove(uuid) } override fun init() {} override suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer { - val cachedPlayer = playerCache.getIfPresent(profile.idOrThrow()) + val cachedPlayer = players.firstOrNull { it.uuid == profile.idOrThrow() } if (cachedPlayer != null) { return cachedPlayer } @@ -41,7 +41,7 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { } override suspend fun findOrLoadPlayer(name: String): FriendPlayer? { - val cachedPlayer = playerCache.asMap().values.find { it.name == name } + val cachedPlayer = players.find { it.name == name } if (cachedPlayer != null) { return cachedPlayer } From 4721c7a91c32fe39f0ba2a24ebc879a0c1f614d4 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:17:01 +0100 Subject: [PATCH 19/43] feat: Add offline friend argument and refactor package structure for friend commands --- .../dev/slne/surf/friends/paper/command/FriendCommand.kt | 3 +++ .../command/argument/{ => friend}/FriendPlayerArgument.kt | 2 +- .../command/argument/{ => friend}/OfflineFriendArgument.kt | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) rename surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/{ => friend}/FriendPlayerArgument.kt (97%) rename surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/{ => friend}/OfflineFriendArgument.kt (97%) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 885c694..4782dc7 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -3,6 +3,7 @@ package dev.slne.surf.friends.paper.command import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument +import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry fun friendCommand() = commandTree("friend") { @@ -15,7 +16,9 @@ fun friendCommand() = commandTree("friend") { } literalArgument("remove") { + offlineFriendArgument("friend") { + } } literalArgument("list") { diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt similarity index 97% rename from surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt rename to surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt index 68e0cbc..04001b0 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/FriendPlayerArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.friends.paper.command.argument +package dev.slne.surf.friends.paper.command.argument.friend import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.CommandTree diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt similarity index 97% rename from surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt rename to surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt index e8d9f9f..4a5f894 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/OfflineFriendArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt @@ -1,4 +1,4 @@ -package dev.slne.surf.friends.paper.command.argument +package dev.slne.surf.friends.paper.command.argument.friend import dev.jorel.commandapi.CommandAPICommand import dev.jorel.commandapi.CommandTree From 22219b4793dd0036e168da5dccef024dc3d6f116 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:42:26 +0100 Subject: [PATCH 20/43] feat: Implement friend player utility and enhance friend command functionality --- .../surf/friends/api/player/FriendPlayer.kt | 8 ++++++- .../friends/paper/command/FriendCommand.kt | 23 +++++++++++++++++-- .../surf/friends/paper/util/friend-util.kt | 8 +++++++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/util/friend-util.kt diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt index f870086..1a8045f 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -16,4 +16,10 @@ data class FriendPlayer( val friends: ObjectSet, val sentFriendRequests: ObjectSet, val receivedFriendRequests: ObjectSet -) +) { + fun hasFriend(uuid: UUID) = + friends.any { it.acceptedBy == uuid } || friends.any { it.requestedBy == uuid } + + fun hasSentFriendRequest(uuid: UUID) = sentFriendRequests.any { it.receiverUuid == uuid } + fun hasReceivedFriendRequest(uuid: UUID) = receivedFriendRequests.any { it.senderUuid == uuid } +} diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 4782dc7..07bdf89 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -2,16 +2,35 @@ package dev.slne.surf.friends.paper.command import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument +import dev.slne.surf.core.api.common.player.SurfPlayer import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry +import dev.slne.surf.friends.paper.util.friendPlayer +import dev.slne.surf.surfapi.bukkit.api.command.executors.playerExecutorSuspend +import dev.slne.surf.surfapi.core.api.command.args.awaiting +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText fun friendCommand() = commandTree("friend") { withPermission(PermissionRegistry.PREFIX_COMMAND) literalArgument("add") { - surfOfflinePlayerArgument("target") { - + surfOfflinePlayerArgument("target") { // TODO: Exclude self and online friends + playerExecutorSuspend { player, args -> + val target = args.awaiting("target") + + if (target == null) { + player.sendText { + appendErrorPrefix() + error("Der Spieler wurde nicht gefunden.") + } + return@playerExecutorSuspend + } + + val friendPlayer = player.friendPlayer + + if(friendPlayer.friends) + } } } diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/util/friend-util.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/util/friend-util.kt new file mode 100644 index 0000000..ac3c043 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/util/friend-util.kt @@ -0,0 +1,8 @@ +package dev.slne.surf.friends.paper.util + +import dev.slne.surf.friends.core.service.friendPlayerService +import org.bukkit.entity.Player + +val Player.friendPlayer + get() = friendPlayerService.players.find { it.uuid == this.uniqueId } + ?: error("Player ${this.name} is not cached as a FriendPlayer") \ No newline at end of file From 9fd8df1b2588ec57e49b416006d994b83d4f4951 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:49:45 +0100 Subject: [PATCH 21/43] feat: Enhance friend request functionality with sender and receiver names --- .../surf/friends/api/friend/FriendRequest.kt | 13 +++++++++++++ .../surf/friends/api/friend/Friendship.kt | 15 +++++++++++++++ .../repository/FriendRequestRepository.kt | 4 ++++ .../backend/table/FriendRequestsTable.kt | 3 +++ .../friends/paper/command/FriendCommand.kt | 19 ++++++++++++++++++- 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt index ef4dea9..1e3d610 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/FriendRequest.kt @@ -3,11 +3,24 @@ package dev.slne.surf.friends.api.friend import dev.slne.surf.surfapi.core.api.serializer.java.datetime.datetime.offset.SerializableOffsetDateTime import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.time.OffsetDateTime import java.util.* + +fun friendRequest( + senderUuid: UUID, + receiverUuid: UUID, + senderName: String, + receiverName: String +) = FriendRequest(senderUuid, receiverUuid, senderName, receiverName, OffsetDateTime.now()) + @Serializable data class FriendRequest( val senderUuid: @Contextual UUID, val receiverUuid: @Contextual UUID, + + val senderName: String, + val receiverName: String, + val sentAt: SerializableOffsetDateTime ) \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index 4e66254..bd607cd 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -3,8 +3,23 @@ package dev.slne.surf.friends.api.friend import dev.slne.surf.surfapi.core.api.serializer.java.datetime.datetime.offset.SerializableOffsetDateTime import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +import java.time.OffsetDateTime import java.util.* +fun friendship( + requestedBy: UUID, + acceptedBy: UUID, + + requesterName: String, + acceptorName: String, +) = Friendship( + requestedBy = requestedBy, + acceptedBy = acceptedBy, + requesterName = requesterName, + acceptorName = acceptorName, + createdAt = OffsetDateTime.now() +) + @Serializable data class Friendship( val requestedBy: @Contextual UUID, diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt index abdfe85..bec5746 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt @@ -29,6 +29,8 @@ class FriendRequestRepository { FriendRequestsTable.insert { it[senderUuid] = request.senderUuid it[receiverUuid] = request.receiverUuid + it[senderName] = request.senderName + it[receiverName] = request.receiverName it[createdAt] = request.sentAt } } @@ -36,6 +38,8 @@ class FriendRequestRepository { private fun createRequest(row: ResultRow) = FriendRequest( senderUuid = row[FriendRequestsTable.senderUuid], receiverUuid = row[FriendRequestsTable.receiverUuid], + senderName = row[FriendRequestsTable.senderName], + receiverName = row[FriendRequestsTable.receiverName], sentAt = row[FriendRequestsTable.createdAt] ) } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt index f6b9651..1fc20d7 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt @@ -6,4 +6,7 @@ import dev.slne.surf.database.table.AuditableLongIdTable object FriendRequestsTable : AuditableLongIdTable("friend_requests") { val senderUuid = nativeUuid("sender_uuid").references(FriendPlayerTable.playerUuid) val receiverUuid = nativeUuid("receiver_uuid").references(FriendPlayerTable.playerUuid) + + val senderName = varchar("sender_name", 16) + val receiverName = varchar("receiver_name", 16) } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 07bdf89..3cc79f7 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -4,6 +4,7 @@ import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument import dev.slne.surf.core.api.common.player.SurfPlayer import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument +import dev.slne.surf.friends.api.friend.friendRequest import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry import dev.slne.surf.friends.paper.util.friendPlayer @@ -29,7 +30,23 @@ fun friendCommand() = commandTree("friend") { val friendPlayer = player.friendPlayer - if(friendPlayer.friends) + if (friendPlayer.hasFriend(target.uuid)) { + player.sendText { + appendErrorPrefix() + error("Du bist bereits mit diesem Spieler befreundet.") + } + return@playerExecutorSuspend + } + + if (friendPlayer.hasReceivedFriendRequest(target.uuid)) { + // TODO: Accept request + return@playerExecutorSuspend + } + + val friendRequest = friendRequest( + senderUuid = player.uniqueId, + receiverUuid = target.uuid + ) } } } From 76922f8f9cc93e64cba9afbc07fd74e26b8eba75 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:55:08 +0100 Subject: [PATCH 22/43] feat: Implement friend request sending and friendship removal functionality --- .../repository/FriendShipRepository.kt | 9 +++ .../backend/service/FriendShipServiceImpl.kt | 4 ++ .../friends/core/service/FriendShipService.kt | 1 + .../friends/paper/command/FriendCommand.kt | 61 ++++++++++++++++++- 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt index a6e2ec4..fe537bb 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendShipRepository.kt @@ -1,8 +1,10 @@ package dev.slne.surf.friends.backend.repository import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.and import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.or +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.deleteWhere import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction @@ -32,6 +34,13 @@ class FriendShipRepository { } } + suspend fun deleteFriendship(friendship: Friendship) = suspendTransaction { + FriendShipsTable.deleteWhere { + (FriendShipsTable.requesterUuid eq friendship.requestedBy) and + (FriendShipsTable.acceptorUuid eq friendship.acceptedBy) + } + } + private fun createFriendShip(row: ResultRow) = Friendship( requestedBy = row[FriendShipsTable.requesterUuid], acceptedBy = row[FriendShipsTable.acceptorUuid], diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt index 0f3be13..7b56a15 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt @@ -11,4 +11,8 @@ class FriendShipServiceImpl : FriendShipService, Services.Fallback { override suspend fun saveFriendShip(friendShip: Friendship) { friendShipRepository.saveFriendship(friendShip) } + + override suspend fun deleteFriendShip(friendShip: Friendship) { + friendShipRepository.deleteFriendship(friendShip) + } } \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt index 65e6f7b..208d0ca 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendShipService.kt @@ -7,4 +7,5 @@ val friendShipService = requiredService() interface FriendShipService { suspend fun saveFriendShip(friendShip: Friendship) + suspend fun deleteFriendShip(friendShip: Friendship) } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 3cc79f7..d7dd9bb 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -5,6 +5,9 @@ import dev.jorel.commandapi.kotlindsl.literalArgument import dev.slne.surf.core.api.common.player.SurfPlayer import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.api.friend.friendRequest +import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.core.service.friendRequestService +import dev.slne.surf.friends.core.service.friendShipService import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry import dev.slne.surf.friends.paper.util.friendPlayer @@ -45,15 +48,71 @@ fun friendCommand() = commandTree("friend") { val friendRequest = friendRequest( senderUuid = player.uniqueId, - receiverUuid = target.uuid + receiverUuid = target.uuid, + senderName = player.name, + receiverName = target.lastKnownName ?: args.getRaw("target") + ?: error("Player has no name and argument has no raw!") ) + + friendRequestService.saveFriendRequest(friendRequest) + + player.sendText { + appendSuccessPrefix() + success("Du hast eine Freundschaftsanfrage an ") + variableValue(target.lastKnownName ?: args.getRaw("target") ?: "#Unbekannt") + success(" gesendet.") + } + + // TODO: Notify target if online and if settings allow it } } } literalArgument("remove") { offlineFriendArgument("friend") { + playerExecutorSuspend { player, args -> + val friend = args.awaiting("friend") + + if (friend == null) { + player.sendText { + appendErrorPrefix() + error("Der Spieler wurde nicht gefunden.") + } + return@playerExecutorSuspend + } + val friendPlayer = player.friendPlayer + + if (!friendPlayer.hasFriend(friend.uuid)) { + player.sendText { + appendErrorPrefix() + error("Du bist mit diesem Spieler nicht befreundet.") + } + return@playerExecutorSuspend + } + + val friendship = + friendPlayer.friends.firstOrNull { it.acceptedBy == friend.uuid || it.requestedBy == friend.uuid } + + if (friendship == null) { + player.sendText { + appendErrorPrefix() + error("Du bist mit diesem Spieler nicht befreundet.") + } + return@playerExecutorSuspend + } + + friendShipService.deleteFriendShip(friendship) + + player.sendText { + appendSuccessPrefix() + success("Du hast die Freundschaft mit ") + variableValue(friend.name) + success(" beendet.") + } + + // TODO: Notify old friend if online and if settings allow it + } } } From 7afd2f8709787c0afe3e6a601d46bf8265d6056c Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 08:59:32 +0100 Subject: [PATCH 23/43] feat: Add pagination for friend lists and enhance Friendship model with name retrieval --- .../surf/friends/api/friend/Friendship.kt | 4 ++- .../friends/paper/command/FriendCommand.kt | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index bd607cd..2a01599 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -29,4 +29,6 @@ data class Friendship( val acceptorName: String, val createdAt: SerializableOffsetDateTime -) \ No newline at end of file +) { + fun getOtherName(ownUuid: UUID) = if (requestedBy == ownUuid) acceptorName else requesterName +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index d7dd9bb..5375353 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -4,6 +4,8 @@ import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument import dev.slne.surf.core.api.common.player.SurfPlayer import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.api.friend.friendRequest import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.core.service.friendRequestService @@ -13,7 +15,12 @@ import dev.slne.surf.friends.paper.permission.PermissionRegistry import dev.slne.surf.friends.paper.util.friendPlayer import dev.slne.surf.surfapi.bukkit.api.command.executors.playerExecutorSuspend import dev.slne.surf.surfapi.core.api.command.args.awaiting +import dev.slne.surf.surfapi.core.api.font.toSmallCaps +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination +import net.kyori.adventure.text.format.TextDecoration +import java.util.* fun friendCommand() = commandTree("friend") { withPermission(PermissionRegistry.PREFIX_COMMAND) @@ -135,4 +142,26 @@ fun friendCommand() = commandTree("friend") { literalArgument("revoke") { } +} + +private fun friendPagination(ownUuid: UUID) = Pagination { + title { primary("Deine Freunde".toSmallCaps(), TextDecoration.BOLD) } + + rowRenderer { friendship, _ -> + listOf(buildText { + spacer("-") + appendSpace() + variableValue(friendship.getOtherName(ownUuid)) + + // TODO: Display if online, if yes: where + }) + } +} + +private val sendRequestsPagination = Pagination { + +} + +private val receivedRequestsPagination = Pagination { + } \ No newline at end of file From 8fab2a1f525ec44968135959ad22bad13c3cf862 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:01:40 +0100 Subject: [PATCH 24/43] feat: Add friend list and request pagination with empty state handling --- .../friends/paper/command/FriendCommand.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 5375353..4e5e0e4 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.paper.command import dev.jorel.commandapi.kotlindsl.commandTree import dev.jorel.commandapi.kotlindsl.literalArgument +import dev.jorel.commandapi.kotlindsl.playerExecutor import dev.slne.surf.core.api.common.player.SurfPlayer import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.api.friend.FriendRequest @@ -124,7 +125,22 @@ fun friendCommand() = commandTree("friend") { } literalArgument("list") { + playerExecutor { player, _ -> + val friendPlayer = player.friendPlayer + if (friendPlayer.friends.isEmpty()) { + player.sendText { + appendInfoPrefix() + info("Du hast keine Freunde.") + } + return@playerExecutor + } + + player.sendText { + appendNewline() + append(friendPagination(player.uniqueId).renderComponent(friendPlayer.friends)) + } + } } literalArgument("requests") { @@ -159,9 +175,25 @@ private fun friendPagination(ownUuid: UUID) = Pagination { } private val sendRequestsPagination = Pagination { + title { primary("Gesendete Anfragen".toSmallCaps(), TextDecoration.BOLD) } + rowRenderer { friendRequest, _ -> + listOf(buildText { + spacer("-") + appendSpace() + variableValue(friendRequest.receiverName) + }) + } } private val receivedRequestsPagination = Pagination { + title { primary("Empfangene Anfragen".toSmallCaps(), TextDecoration.BOLD) } + rowRenderer { friendRequest, _ -> + listOf(buildText { + spacer("-") + appendSpace() + variableValue(friendRequest.senderName) + }) + } } \ No newline at end of file From 8f816ac4a487d49661be1b7761e85db6dd2b6c6b Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:02:30 +0100 Subject: [PATCH 25/43] feat: Add functionality to view sent and received friend requests with pagination --- .../friends/paper/command/FriendCommand.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 4e5e0e4..190132f 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -144,7 +144,43 @@ fun friendCommand() = commandTree("friend") { } literalArgument("requests") { + literalArgument("sent") { + playerExecutor { player, _ -> + val friendPlayer = player.friendPlayer + + if (friendPlayer.sentFriendRequests.isEmpty()) { + player.sendText { + appendInfoPrefix() + info("Du hast keine gesendeten Freundschaftsanfragen.") + } + return@playerExecutor + } + + player.sendText { + appendNewline() + append(sendRequestsPagination.renderComponent(friendPlayer.sentFriendRequests)) + } + } + } + literalArgument("received") { + playerExecutor { player, _ -> + val friendPlayer = player.friendPlayer + + if (friendPlayer.receivedFriendRequests.isEmpty()) { + player.sendText { + appendInfoPrefix() + info("Du hast keine empfangenen Freundschaftsanfragen.") + } + return@playerExecutor + } + + player.sendText { + appendNewline() + append(receivedRequestsPagination.renderComponent(friendPlayer.receivedFriendRequests)) + } + } + } } literalArgument("accept") { From c51f35d49839735adcabb16676b9e02f0e06d185 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:08:52 +0100 Subject: [PATCH 26/43] feat: Add custom arguments for handling received and sent friend requests --- .../argument/friend/FriendPlayerArgument.kt | 2 +- .../request/ReceivedFriendRequestArgument.kt | 55 +++++++++++++++++++ .../request/SentFriendRequestArgument.kt | 55 +++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt index 04001b0..2c4eb4e 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt @@ -13,7 +13,7 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.uuid class FriendPlayerArgument(nodeName: String) : CustomArgument(StringArgument(nodeName), { info -> - friendPlayerService.players.firstOrNull { it.name == info.input } + friendPlayerService.players.firstOrNull { it.name == info.input } // TODO: Only allow real friends, not every online friend player ?: throw CustomArgumentException.fromAdventureComponent( buildText { appendErrorPrefix() diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt new file mode 100644 index 0000000..6089cad --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt @@ -0,0 +1,55 @@ +package dev.slne.surf.friends.paper.command.argument.request + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.CommandTree +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import dev.slne.surf.surfapi.core.api.messages.adventure.uuid + +class ReceivedFriendRequestArgument(nodeName: String) : + CustomArgument(StringArgument(nodeName), { info -> + val senderPlayer = friendPlayerService.players.first { it.uuid == info.sender.uuid() } + + senderPlayer.receivedFriendRequests.firstOrNull { it.senderName == info.input } + ?: throw CustomArgumentException.fromAdventureComponent( + buildText { + appendErrorPrefix() + error("Du hast keine Freundschaftsanfrage von diesem Spieler erhalten.") + }) + }) { + init { + this.replaceSuggestions( + ArgumentSuggestions.stringCollection { sender -> + friendPlayerService.players.first { it.uuid == sender.sender.uuid() }.receivedFriendRequests.map { it.senderName } + } + ) + } +} + +inline fun CommandTree.friendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandTree = then( + ReceivedFriendRequestArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun Argument<*>.friendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): Argument<*> = then( + ReceivedFriendRequestArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun CommandAPICommand.friendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = + withArguments(ReceivedFriendRequestArgument(nodeName).setOptional(optional).apply(block)) \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt new file mode 100644 index 0000000..c11165b --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt @@ -0,0 +1,55 @@ +package dev.slne.surf.friends.paper.command.argument.request + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.CommandTree +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import dev.slne.surf.surfapi.core.api.messages.adventure.uuid + +class SentFriendRequestArgument(nodeName: String) : + CustomArgument(StringArgument(nodeName), { info -> + val senderPlayer = friendPlayerService.players.first { it.uuid == info.sender.uuid() } + + senderPlayer.sentFriendRequests.firstOrNull { it.receiverName == info.input } + ?: throw CustomArgumentException.fromAdventureComponent( + buildText { + appendErrorPrefix() + error("Du hast keine Freundschaftsanfrage an diesen Spieler gesendet.") + }) + }) { + init { + this.replaceSuggestions( + ArgumentSuggestions.stringCollection { sender -> + friendPlayerService.players.first { it.uuid == sender.sender.uuid() }.sentFriendRequests.map { it.receiverName } + } + ) + } +} + +inline fun CommandTree.sentFriendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandTree = then( + SentFriendRequestArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun Argument<*>.sentFriendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): Argument<*> = then( + SentFriendRequestArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun CommandAPICommand.sentFriendRequestArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = + withArguments(SentFriendRequestArgument(nodeName).setOptional(optional).apply(block)) \ No newline at end of file From 0c4510a2d902c81c1c3d93592958b06ab64f3f83 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:14:27 +0100 Subject: [PATCH 27/43] feat: Implement accept, decline, and revoke functionalities for friend requests --- .../repository/FriendRequestRepository.kt | 9 +++ .../service/FriendRequestServiceImpl.kt | 4 ++ .../core/service/FriendRequestService.kt | 1 + .../friends/paper/command/FriendCommand.kt | 66 +++++++++++++++++++ .../request/ReceivedFriendRequestArgument.kt | 6 +- 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt index bec5746..35c5f45 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendRequestRepository.kt @@ -1,7 +1,9 @@ package dev.slne.surf.friends.backend.repository import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.and import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.deleteWhere import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction @@ -25,6 +27,13 @@ class FriendRequestRepository { .map { createRequest(it) } }.toSet().toObjectSet() + suspend fun deleteRequest(request: FriendRequest) = suspendTransaction { + FriendRequestsTable.deleteWhere { + (FriendRequestsTable.senderUuid eq request.senderUuid) and + (FriendRequestsTable.receiverUuid eq request.receiverUuid) + } + } + suspend fun saveRequest(request: FriendRequest) = suspendTransaction { FriendRequestsTable.insert { it[senderUuid] = request.senderUuid diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt index 15919ca..0b46447 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt @@ -11,4 +11,8 @@ class FriendRequestServiceImpl : FriendRequestService, Services.Fallback { override suspend fun saveFriendRequest(friendRequest: FriendRequest) { friendRequestRepository.saveRequest(friendRequest) } + + override suspend fun deleteFriendRequest(friendRequest: FriendRequest) { + friendRequestRepository.deleteRequest(friendRequest) + } } \ No newline at end of file diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt index 6ed43fa..eba1ddf 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendRequestService.kt @@ -7,4 +7,5 @@ val friendRequestService = requiredService() interface FriendRequestService { suspend fun saveFriendRequest(friendRequest: FriendRequest) + suspend fun deleteFriendRequest(friendRequest: FriendRequest) } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 190132f..08ebc66 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -1,6 +1,7 @@ package dev.slne.surf.friends.paper.command import dev.jorel.commandapi.kotlindsl.commandTree +import dev.jorel.commandapi.kotlindsl.getValue import dev.jorel.commandapi.kotlindsl.literalArgument import dev.jorel.commandapi.kotlindsl.playerExecutor import dev.slne.surf.core.api.common.player.SurfPlayer @@ -8,10 +9,13 @@ import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.api.friend.friendRequest +import dev.slne.surf.friends.api.friend.friendship import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.core.service.friendRequestService import dev.slne.surf.friends.core.service.friendShipService import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument +import dev.slne.surf.friends.paper.command.argument.request.receivedFriendRequestArgument +import dev.slne.surf.friends.paper.command.argument.request.sentFriendRequestArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry import dev.slne.surf.friends.paper.util.friendPlayer import dev.slne.surf.surfapi.bukkit.api.command.executors.playerExecutorSuspend @@ -184,15 +188,77 @@ fun friendCommand() = commandTree("friend") { } literalArgument("accept") { + receivedFriendRequestArgument("receivedRequest") { + playerExecutorSuspend { player, args -> + val friendRequest: FriendRequest by args + val friendPlayer = player.friendPlayer + + if (friendPlayer.hasFriend(friendRequest.senderUuid)) { + player.sendText { + appendErrorPrefix() + error("Du bist bereits mit diesem Spieler befreundet.") + } + return@playerExecutorSuspend + } + val friendship = friendship( + requestedBy = friendRequest.senderUuid, + acceptedBy = friendRequest.receiverUuid, + requesterName = friendRequest.senderName, + acceptorName = friendRequest.receiverName + ) + + friendRequestService.deleteFriendRequest(friendRequest) + friendShipService.saveFriendShip(friendship) + + player.sendText { + appendSuccessPrefix() + success("Du hast die Freundschaftsanfrage von ") + variableValue(friendRequest.senderName) + success(" angenommen.") + } + + // TODO: Notify sender if online and if settings allow it + } + } } literalArgument("decline") { + receivedFriendRequestArgument("receivedRequest") { + playerExecutorSuspend { player, args -> + val friendRequest: FriendRequest by args + + friendRequestService.deleteFriendRequest(friendRequest) + player.sendText { + appendSuccessPrefix() + success("Du hast die Freundschaftsanfrage von ") + variableValue(friendRequest.senderName) + success(" abgelehnt.") + } + + // TODO: Notify sender if online and if settings allow it + } + } } literalArgument("revoke") { + sentFriendRequestArgument("sentRequest") { + playerExecutorSuspend { player, args -> + val friendRequest: FriendRequest by args + + friendRequestService.deleteFriendRequest(friendRequest) + + player.sendText { + appendSuccessPrefix() + success("Du hast die Freundschaftsanfrage an ") + variableValue(friendRequest.receiverName) + success(" zurückgezogen.") + } + // TODO: Notify receiver if online and if settings allow it + } + } } } diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt index 6089cad..e9841f0 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt @@ -31,7 +31,7 @@ class ReceivedFriendRequestArgument(nodeName: String) : } } -inline fun CommandTree.friendRequestArgument( +inline fun CommandTree.receivedFriendRequestArgument( nodeName: String, optional: Boolean = false, block: Argument<*>.() -> Unit = {} @@ -39,7 +39,7 @@ inline fun CommandTree.friendRequestArgument( ReceivedFriendRequestArgument(nodeName).setOptional(optional).apply(block) ) -inline fun Argument<*>.friendRequestArgument( +inline fun Argument<*>.receivedFriendRequestArgument( nodeName: String, optional: Boolean = false, block: Argument<*>.() -> Unit = {} @@ -47,7 +47,7 @@ inline fun Argument<*>.friendRequestArgument( ReceivedFriendRequestArgument(nodeName).setOptional(optional).apply(block) ) -inline fun CommandAPICommand.friendRequestArgument( +inline fun CommandAPICommand.receivedFriendRequestArgument( nodeName: String, optional: Boolean = false, block: Argument<*>.() -> Unit = {} From b69ba8791c24c29fa47c4d091d42ce887b46f8bd Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:18:30 +0100 Subject: [PATCH 28/43] feat: Enhance player connection listener with online friend count and pending requests notifications --- surf-friends-api/build.gradle.kts | 4 +++ .../surf/friends/api/friend/Friendship.kt | 1 + .../surf/friends/api/player/FriendPlayer.kt | 5 ++++ .../listener/PlayerConnectionListener.kt | 28 ++++++++++++++++++- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/surf-friends-api/build.gradle.kts b/surf-friends-api/build.gradle.kts index 64d286f..2cf8a7e 100644 --- a/surf-friends-api/build.gradle.kts +++ b/surf-friends-api/build.gradle.kts @@ -1,3 +1,7 @@ plugins { id("dev.slne.surf.surfapi.gradle.paper-raw") +} + +surfRawPaperApi { + withCorePaper() } \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt index 2a01599..80d9b71 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/friend/Friendship.kt @@ -31,4 +31,5 @@ data class Friendship( val createdAt: SerializableOffsetDateTime ) { fun getOtherName(ownUuid: UUID) = if (requestedBy == ownUuid) acceptorName else requesterName + fun getOtherUuid(ownUuid: UUID) = if (requestedBy == ownUuid) acceptedBy else requestedBy } \ No newline at end of file diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt index 1a8045f..14c68ce 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -1,5 +1,6 @@ package dev.slne.surf.friends.api.player +import dev.slne.surf.core.api.common.surfCoreApi import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import it.unimi.dsi.fastutil.objects.ObjectSet @@ -22,4 +23,8 @@ data class FriendPlayer( fun hasSentFriendRequest(uuid: UUID) = sentFriendRequests.any { it.receiverUuid == uuid } fun hasReceivedFriendRequest(uuid: UUID) = receivedFriendRequests.any { it.senderUuid == uuid } + + fun getOnlineFriendCount() = friends.count { ship -> + surfCoreApi.getOnlinePlayers().any { it.uuid == ship.getOtherUuid(uuid) } + } } diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt index fbd819d..33587f2 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt @@ -1,21 +1,47 @@ package dev.slne.surf.friends.paper.listener import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.friends.paper.util.friendPlayer +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent import kotlinx.coroutines.runBlocking import org.bukkit.event.EventHandler import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerQuitEvent @Suppress("UnstableApiUsage") object PlayerConnectionListener : Listener { @EventHandler - fun onJoin(event: AsyncPlayerConnectionConfigureEvent) { + fun onConfigure(event: AsyncPlayerConnectionConfigureEvent) { runBlocking { friendPlayerService.cachePlayer(friendPlayerService.loadOrCreatePlayer(event.connection.profile)) } } + @EventHandler + fun onJoin(event: PlayerJoinEvent) { + val friendPlayer = event.player.friendPlayer + + if (friendPlayer.receivedFriendRequests.isNotEmpty()) { + event.player.sendText { + appendInfoPrefix() + info("Du hast noch ") + variableValue(friendPlayer.receivedFriendRequests.size) + info(" offene Freundschaftsanfragen.") + } + } + + if (friendPlayer.getOnlineFriendCount() > 0) { + event.player.sendText { + appendInfoPrefix() + info("Derzeit sind ") + variableValue(friendPlayer.getOnlineFriendCount()) + info(" Freunde online.") + } + } + } + @EventHandler fun onQuit(event: PlayerQuitEvent) { friendPlayerService.invalidatePlayer(event.player.uniqueId) From 68e9a8ecb74c474ebbc70280b2f8ed79cc21c2c4 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 09:19:11 +0100 Subject: [PATCH 29/43] feat: Add conditional notifications for online friends and pending requests based on player settings --- .../surf/friends/paper/listener/PlayerConnectionListener.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt index 33587f2..0005e5f 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt @@ -29,7 +29,7 @@ object PlayerConnectionListener : Listener { info("Du hast noch ") variableValue(friendPlayer.receivedFriendRequests.size) info(" offene Freundschaftsanfragen.") - } + } // TODO: Only sent if enabled in player settings } if (friendPlayer.getOnlineFriendCount() > 0) { @@ -39,7 +39,7 @@ object PlayerConnectionListener : Listener { variableValue(friendPlayer.getOnlineFriendCount()) info(" Freunde online.") } - } + } // TODO: Only sent if enabled in player settings } @EventHandler From 8132fc3b0e5505d7cda7751b294a476727e50c41 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 15:38:46 +0100 Subject: [PATCH 30/43] feat: Update friend request and friendship services to manage player cache on save and delete --- .../service/FriendRequestServiceImpl.kt | 46 +++++++++++++++++++ .../backend/service/FriendShipServiceImpl.kt | 42 +++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt index 0b46447..2c722e4 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendRequestServiceImpl.kt @@ -4,15 +4,61 @@ import com.google.auto.service.AutoService import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.backend.repository.friendRequestRepository import dev.slne.surf.friends.core.service.FriendRequestService +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.util.toObjectSet import net.kyori.adventure.util.Services @AutoService(FriendRequestService::class) class FriendRequestServiceImpl : FriendRequestService, Services.Fallback { override suspend fun saveFriendRequest(friendRequest: FriendRequest) { friendRequestRepository.saveRequest(friendRequest) + + val sender = friendPlayerService.players + .firstOrNull { it.uuid == friendRequest.senderUuid } + + if (sender != null) { + val updatedSender = sender.copy( + sentFriendRequests = (sender.sentFriendRequests + friendRequest).toObjectSet() + ) + friendPlayerService.cachePlayer(updatedSender) + } + + val receiver = friendPlayerService.players + .firstOrNull { it.uuid == friendRequest.receiverUuid } + + if (receiver != null) { + val updatedReceiver = receiver.copy( + receivedFriendRequests = (receiver.receivedFriendRequests + friendRequest).toObjectSet() + ) + friendPlayerService.cachePlayer(updatedReceiver) + } } override suspend fun deleteFriendRequest(friendRequest: FriendRequest) { friendRequestRepository.deleteRequest(friendRequest) + + val sender = friendPlayerService.players + .firstOrNull { it.uuid == friendRequest.senderUuid } + + if (sender != null) { + val updatedSender = sender.copy( + sentFriendRequests = sender.sentFriendRequests + .filterNot { it == friendRequest } + .toObjectSet() + ) + friendPlayerService.cachePlayer(updatedSender) + } + + val receiver = friendPlayerService.players + .firstOrNull { it.uuid == friendRequest.receiverUuid } + + if (receiver != null) { + val updatedReceiver = receiver.copy( + receivedFriendRequests = receiver.receivedFriendRequests + .filterNot { it == friendRequest } + .toObjectSet() + ) + friendPlayerService.cachePlayer(updatedReceiver) + } } } \ No newline at end of file diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt index 7b56a15..e2cc339 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendShipServiceImpl.kt @@ -4,15 +4,57 @@ import com.google.auto.service.AutoService import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.backend.repository.friendShipRepository import dev.slne.surf.friends.core.service.FriendShipService +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.util.toObjectSet import net.kyori.adventure.util.Services @AutoService(FriendShipService::class) class FriendShipServiceImpl : FriendShipService, Services.Fallback { override suspend fun saveFriendShip(friendShip: Friendship) { friendShipRepository.saveFriendship(friendShip) + + val cachedPlayers = friendPlayerService.players + + val requester = cachedPlayers.firstOrNull { it.uuid == friendShip.requestedBy } + if (requester != null) { + val updated = requester.copy( + friends = (requester.friends + friendShip).toObjectSet() + ) + friendPlayerService.cachePlayer(updated) + } + + val accepter = cachedPlayers.firstOrNull { it.uuid == friendShip.acceptedBy } + if (accepter != null) { + val updated = accepter.copy( + friends = (accepter.friends + friendShip).toObjectSet() + ) + friendPlayerService.cachePlayer(updated) + } } override suspend fun deleteFriendShip(friendShip: Friendship) { friendShipRepository.deleteFriendship(friendShip) + + val cachedPlayers = friendPlayerService.players + + val requester = cachedPlayers.firstOrNull { it.uuid == friendShip.requestedBy } + if (requester != null) { + val updated = requester.copy( + friends = requester.friends + .filterNot { it == friendShip } + .toObjectSet() + ) + friendPlayerService.cachePlayer(updated) + } + + val accepter = cachedPlayers.firstOrNull { it.uuid == friendShip.acceptedBy } + if (accepter != null) { + val updated = accepter.copy( + friends = accepter.friends + .filterNot { it == friendShip } + .toObjectSet() + ) + friendPlayerService.cachePlayer(updated) + } } } \ No newline at end of file From 0f271e3705938d1cfb12c1e93176093048f5fdde Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 15:52:47 +0100 Subject: [PATCH 31/43] feat: Update .gitignore to exclude the entire .idea directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e4906fd..b8a88a0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ build/ .idea/jarRepositories.xml .idea/compiler.xml .idea/libraries/ +.idea *.iws *.iml *.ipr From 5ed70579711396f90bc5f8ed504d12bea9b57c9f Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 16:19:28 +0100 Subject: [PATCH 32/43] feat: Refactor friend player caching and database interactions --- .../dev/slne/surf/friends/api/player/FriendPlayer.kt | 7 +++---- .../backend/service/FriendPlayerServiceImpl.kt | 12 ++++-------- .../friends/backend/table/FriendRequestsTable.kt | 4 ++-- .../surf/friends/backend/table/FriendShipsTable.kt | 4 ++-- .../dev/slne/surf/friends/core/loader/RedisLoader.kt | 5 +++++ .../surf/friends/core/service/FriendPlayerService.kt | 2 -- surf-friends-paper/build.gradle.kts | 1 + .../kotlin/dev/slne/surf/friends/paper/PaperMain.kt | 4 ++++ 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt index 14c68ce..e22452b 100644 --- a/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt +++ b/surf-friends-api/src/main/kotlin/dev/slne/surf/friends/api/player/FriendPlayer.kt @@ -3,7 +3,6 @@ package dev.slne.surf.friends.api.player import dev.slne.surf.core.api.common.surfCoreApi import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship -import it.unimi.dsi.fastutil.objects.ObjectSet import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import java.util.* @@ -14,9 +13,9 @@ data class FriendPlayer( val name: String, val texture: String, - val friends: ObjectSet, - val sentFriendRequests: ObjectSet, - val receivedFriendRequests: ObjectSet + val friends: Set, + val sentFriendRequests: Set, + val receivedFriendRequests: Set ) { fun hasFriend(uuid: UUID) = friends.any { it.acceptedBy == uuid } || friends.any { it.requestedBy == uuid } diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 850c807..89b0ab7 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -4,7 +4,7 @@ import com.destroystokyo.paper.profile.PlayerProfile import com.google.auto.service.AutoService import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.friends.backend.repository.friendPlayerRepository -import dev.slne.surf.friends.core.loader.redisApi +import dev.slne.surf.friends.core.loader.redisLoader import dev.slne.surf.friends.core.service.FriendPlayerService import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import dev.slne.surf.surfapi.core.api.util.toObjectSet @@ -14,21 +14,17 @@ import java.util.* @AutoService(FriendPlayerService::class) class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { - val playerCache = redisApi.createSyncMap("surf-friends:player-cache") - override val players: ObjectSet - get() = playerCache.snapshot().values.toObjectSet() + get() = redisLoader.playerCache.snapshot().values.toObjectSet() override fun cachePlayer(friendPlayer: FriendPlayer) { - playerCache.put(friendPlayer.uuid, friendPlayer) + redisLoader.playerCache.put(friendPlayer.uuid, friendPlayer) } override fun invalidatePlayer(uuid: UUID) { - playerCache.remove(uuid) + redisLoader.playerCache.remove(uuid) } - override fun init() {} - override suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer { val cachedPlayer = players.firstOrNull { it.uuid == profile.idOrThrow() } if (cachedPlayer != null) { diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt index 1fc20d7..769415a 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendRequestsTable.kt @@ -4,8 +4,8 @@ import dev.slne.surf.database.columns.nativeUuid import dev.slne.surf.database.table.AuditableLongIdTable object FriendRequestsTable : AuditableLongIdTable("friend_requests") { - val senderUuid = nativeUuid("sender_uuid").references(FriendPlayerTable.playerUuid) - val receiverUuid = nativeUuid("receiver_uuid").references(FriendPlayerTable.playerUuid) + val senderUuid = nativeUuid("sender_uuid") + val receiverUuid = nativeUuid("receiver_uuid") val senderName = varchar("sender_name", 16) val receiverName = varchar("receiver_name", 16) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt index 3b19325..850ce4a 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/table/FriendShipsTable.kt @@ -4,8 +4,8 @@ import dev.slne.surf.database.columns.nativeUuid import dev.slne.surf.database.table.AuditableLongIdTable object FriendShipsTable : AuditableLongIdTable("friend_ships") { - val requesterUuid = nativeUuid("requester_uuid").references(FriendPlayerTable.playerUuid) - val acceptorUuid = nativeUuid("acceptor_uuid").references(FriendPlayerTable.playerUuid) + val requesterUuid = nativeUuid("requester_uuid") + val acceptorUuid = nativeUuid("acceptor_uuid") val requesterName = varchar("requester_name", 16) val acceptorName = varchar("acceptor_name", 16) diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt index ca7d89b..a9c1998 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/loader/RedisLoader.kt @@ -1,12 +1,17 @@ package dev.slne.surf.friends.core.loader +import dev.slne.surf.friends.api.player.FriendPlayer import dev.slne.surf.redis.RedisApi +import dev.slne.surf.redis.sync.map.SyncMap +import java.util.* val redisLoader = RedisLoader() val redisApi get() = redisLoader.redisApi class RedisLoader { val redisApi = RedisApi.create() + val playerCache: SyncMap = + redisApi.createSyncMap("surf-friends:player-cache") fun load() { diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index da68650..0e72f62 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -14,8 +14,6 @@ interface FriendPlayerService { fun cachePlayer(friendPlayer: FriendPlayer) fun invalidatePlayer(uuid: UUID) - fun init() - suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer suspend fun findOrLoadPlayer(name: String): FriendPlayer? suspend fun savePlayer(friendPlayer: FriendPlayer) diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts index ede3e6a..77ab4e4 100644 --- a/surf-friends-paper/build.gradle.kts +++ b/surf-friends-paper/build.gradle.kts @@ -15,4 +15,5 @@ surfPaperPluginApi { dependencies { api(project(":surf-friends-core")) + runtimeOnly(project(":surf-friends-backend")) } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt index 585106c..dbace3d 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.paper import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin import dev.slne.surf.core.api.common.surfCoreApi +import dev.slne.surf.friends.core.loader.databaseLoader import dev.slne.surf.friends.core.loader.redisLoader import dev.slne.surf.friends.paper.command.friendCommand import dev.slne.surf.friends.paper.listener.PlayerConnectionListener @@ -15,6 +16,8 @@ class PaperMain : SuspendingJavaPlugin() { override suspend fun onLoadAsync() { redisLoader.load() redisLoader.connect() + + databaseLoader.connect(plugin.dataPath) } override suspend fun onEnableAsync() { @@ -26,5 +29,6 @@ class PaperMain : SuspendingJavaPlugin() { override suspend fun onDisableAsync() { redisLoader.disconnect() + databaseLoader.disconnect() } } \ No newline at end of file From 67a2e596bfe9e53b807e14016f709246a33dafbf Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 16:19:34 +0100 Subject: [PATCH 33/43] feat: Prevent players from adding themselves as friends with an error message --- .../dev/slne/surf/friends/paper/command/FriendCommand.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 08ebc66..df894a2 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -43,6 +43,14 @@ fun friendCommand() = commandTree("friend") { return@playerExecutorSuspend } + if (target.uuid == player.uniqueId) { + player.sendText { + appendErrorPrefix() + error("Du kannst dich nicht selbst als Freund hinzufügen.") + } + return@playerExecutorSuspend + } + val friendPlayer = player.friendPlayer if (friendPlayer.hasFriend(target.uuid)) { From 5fcf0d77dbd7d68a145653414a21b5fdb7d75a3f Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Thu, 26 Feb 2026 16:23:44 +0100 Subject: [PATCH 34/43] feat: Prevent players from sending multiple friend requests to the same user --- .../friends/paper/command/FriendCommand.kt | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index df894a2..f97baa6 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -53,6 +53,14 @@ fun friendCommand() = commandTree("friend") { val friendPlayer = player.friendPlayer + if (friendPlayer.hasSentFriendRequest(target.uuid)) { + player.sendText { + appendErrorPrefix() + error("Du hast diesem Spieler bereits eine Freundschaftsanfrage gesendet.") + } + return@playerExecutorSuspend + } + if (friendPlayer.hasFriend(target.uuid)) { player.sendText { appendErrorPrefix() @@ -198,10 +206,10 @@ fun friendCommand() = commandTree("friend") { literalArgument("accept") { receivedFriendRequestArgument("receivedRequest") { playerExecutorSuspend { player, args -> - val friendRequest: FriendRequest by args + val receivedRequest: FriendRequest by args val friendPlayer = player.friendPlayer - if (friendPlayer.hasFriend(friendRequest.senderUuid)) { + if (friendPlayer.hasFriend(receivedRequest.senderUuid)) { player.sendText { appendErrorPrefix() error("Du bist bereits mit diesem Spieler befreundet.") @@ -210,19 +218,19 @@ fun friendCommand() = commandTree("friend") { } val friendship = friendship( - requestedBy = friendRequest.senderUuid, - acceptedBy = friendRequest.receiverUuid, - requesterName = friendRequest.senderName, - acceptorName = friendRequest.receiverName + requestedBy = receivedRequest.senderUuid, + acceptedBy = receivedRequest.receiverUuid, + requesterName = receivedRequest.senderName, + acceptorName = receivedRequest.receiverName ) - friendRequestService.deleteFriendRequest(friendRequest) + friendRequestService.deleteFriendRequest(receivedRequest) friendShipService.saveFriendShip(friendship) player.sendText { appendSuccessPrefix() success("Du hast die Freundschaftsanfrage von ") - variableValue(friendRequest.senderName) + variableValue(receivedRequest.senderName) success(" angenommen.") } @@ -234,14 +242,14 @@ fun friendCommand() = commandTree("friend") { literalArgument("decline") { receivedFriendRequestArgument("receivedRequest") { playerExecutorSuspend { player, args -> - val friendRequest: FriendRequest by args + val receivedRequest: FriendRequest by args - friendRequestService.deleteFriendRequest(friendRequest) + friendRequestService.deleteFriendRequest(receivedRequest) player.sendText { appendSuccessPrefix() success("Du hast die Freundschaftsanfrage von ") - variableValue(friendRequest.senderName) + variableValue(receivedRequest.senderName) success(" abgelehnt.") } @@ -253,14 +261,14 @@ fun friendCommand() = commandTree("friend") { literalArgument("revoke") { sentFriendRequestArgument("sentRequest") { playerExecutorSuspend { player, args -> - val friendRequest: FriendRequest by args + val sentRequest: FriendRequest by args - friendRequestService.deleteFriendRequest(friendRequest) + friendRequestService.deleteFriendRequest(sentRequest) player.sendText { appendSuccessPrefix() success("Du hast die Freundschaftsanfrage an ") - variableValue(friendRequest.receiverName) + variableValue(sentRequest.receiverName) success(" zurückgezogen.") } From d85234da85a051ff7bb2a3d03f9e22c5c9464da0 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 27 Feb 2026 15:13:02 +0100 Subject: [PATCH 35/43] feat: Implement friend request handling and notifications with Redis integration --- surf-friends-paper/build.gradle.kts | 8 + .../dev/slne/surf/friends/paper/PaperMain.kt | 2 + .../friends/paper/command/FriendCommand.kt | 159 ++++------------ .../friend/NonFriendOfflinePlayerArgument.kt | 74 ++++++++ .../paper/command/friend-command-util.kt | 175 ++++++++++++++++++ .../surf/friends/paper/hook/SettingsHook.kt | 27 +++ .../paper/redis/FriendRedisListener.kt | 35 ++++ .../redis/event/FriendNotifyRedisEvent.kt | 13 ++ .../event/FriendRequestNotifyRedisEvent.kt | 13 ++ 9 files changed, 384 insertions(+), 122 deletions(-) create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/NonFriendOfflinePlayerArgument.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendNotifyRedisEvent.kt create mode 100644 surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendRequestNotifyRedisEvent.kt diff --git a/surf-friends-paper/build.gradle.kts b/surf-friends-paper/build.gradle.kts index 77ab4e4..9dd5a72 100644 --- a/surf-friends-paper/build.gradle.kts +++ b/surf-friends-paper/build.gradle.kts @@ -1,3 +1,5 @@ +import dev.slne.surf.surfapi.gradle.util.registerSoft + plugins { id("dev.slne.surf.surfapi.gradle.paper-plugin") } @@ -11,9 +13,15 @@ surfPaperPluginApi { withSurfRedis() authors.add("red") + + serverDependencies { + registerSoft("surf-settings-paper") + } } dependencies { api(project(":surf-friends-core")) runtimeOnly(project(":surf-friends-backend")) + + compileOnly("dev.slne.surf.settings:surf-settings-api:1.21.11-2.0.0-SNAPSHOT") } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt index dbace3d..69b4080 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -7,6 +7,7 @@ import dev.slne.surf.friends.core.loader.redisLoader import dev.slne.surf.friends.paper.command.friendCommand import dev.slne.surf.friends.paper.listener.PlayerConnectionListener import dev.slne.surf.friends.paper.listener.SurfPlayerListener +import dev.slne.surf.friends.paper.redis.FriendRedisListener import dev.slne.surf.surfapi.bukkit.api.event.register import org.bukkit.plugin.java.JavaPlugin @@ -15,6 +16,7 @@ val plugin get() = JavaPlugin.getPlugin(PaperMain::class.java) class PaperMain : SuspendingJavaPlugin() { override suspend fun onLoadAsync() { redisLoader.load() + redisLoader.withListener(FriendRedisListener) redisLoader.connect() databaseLoader.connect(plugin.dataPath) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index f97baa6..35ca02c 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -5,18 +5,17 @@ import dev.jorel.commandapi.kotlindsl.getValue import dev.jorel.commandapi.kotlindsl.literalArgument import dev.jorel.commandapi.kotlindsl.playerExecutor import dev.slne.surf.core.api.common.player.SurfPlayer -import dev.slne.surf.core.api.paper.command.argument.surfOfflinePlayerArgument import dev.slne.surf.friends.api.friend.FriendRequest -import dev.slne.surf.friends.api.friend.Friendship -import dev.slne.surf.friends.api.friend.friendRequest -import dev.slne.surf.friends.api.friend.friendship import dev.slne.surf.friends.api.player.FriendPlayer +import dev.slne.surf.friends.core.loader.redisApi import dev.slne.surf.friends.core.service.friendRequestService import dev.slne.surf.friends.core.service.friendShipService +import dev.slne.surf.friends.paper.command.argument.friend.nonFriendOfflinePlayerArgument import dev.slne.surf.friends.paper.command.argument.friend.offlineFriendArgument import dev.slne.surf.friends.paper.command.argument.request.receivedFriendRequestArgument import dev.slne.surf.friends.paper.command.argument.request.sentFriendRequestArgument import dev.slne.surf.friends.paper.permission.PermissionRegistry +import dev.slne.surf.friends.paper.redis.event.FriendNotifyRedisEvent import dev.slne.surf.friends.paper.util.friendPlayer import dev.slne.surf.surfapi.bukkit.api.command.executors.playerExecutorSuspend import dev.slne.surf.surfapi.core.api.command.args.awaiting @@ -25,73 +24,16 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.buildText import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination import net.kyori.adventure.text.format.TextDecoration -import java.util.* fun friendCommand() = commandTree("friend") { withPermission(PermissionRegistry.PREFIX_COMMAND) literalArgument("add") { - surfOfflinePlayerArgument("target") { // TODO: Exclude self and online friends + nonFriendOfflinePlayerArgument("target") { playerExecutorSuspend { player, args -> val target = args.awaiting("target") - if (target == null) { - player.sendText { - appendErrorPrefix() - error("Der Spieler wurde nicht gefunden.") - } - return@playerExecutorSuspend - } - - if (target.uuid == player.uniqueId) { - player.sendText { - appendErrorPrefix() - error("Du kannst dich nicht selbst als Freund hinzufügen.") - } - return@playerExecutorSuspend - } - - val friendPlayer = player.friendPlayer - - if (friendPlayer.hasSentFriendRequest(target.uuid)) { - player.sendText { - appendErrorPrefix() - error("Du hast diesem Spieler bereits eine Freundschaftsanfrage gesendet.") - } - return@playerExecutorSuspend - } - - if (friendPlayer.hasFriend(target.uuid)) { - player.sendText { - appendErrorPrefix() - error("Du bist bereits mit diesem Spieler befreundet.") - } - return@playerExecutorSuspend - } - - if (friendPlayer.hasReceivedFriendRequest(target.uuid)) { - // TODO: Accept request - return@playerExecutorSuspend - } - - val friendRequest = friendRequest( - senderUuid = player.uniqueId, - receiverUuid = target.uuid, - senderName = player.name, - receiverName = target.lastKnownName ?: args.getRaw("target") - ?: error("Player has no name and argument has no raw!") - ) - - friendRequestService.saveFriendRequest(friendRequest) - - player.sendText { - appendSuccessPrefix() - success("Du hast eine Freundschaftsanfrage an ") - variableValue(target.lastKnownName ?: args.getRaw("target") ?: "#Unbekannt") - success(" gesendet.") - } - - // TODO: Notify target if online and if settings allow it + addFriend(player, target, args.getRaw("target") ?: "#Unknown") } } } @@ -139,27 +81,23 @@ fun friendCommand() = commandTree("friend") { success(" beendet.") } - // TODO: Notify old friend if online and if settings allow it + redisApi.publishEvent( + FriendNotifyRedisEvent( + playerUuid = friend.uuid, + buildText { + appendInfoPrefix() + info("Die Freundschaft mit ") + variableValue(player.name) + info(" wurde beendet.") + } + )) } } } literalArgument("list") { playerExecutor { player, _ -> - val friendPlayer = player.friendPlayer - - if (friendPlayer.friends.isEmpty()) { - player.sendText { - appendInfoPrefix() - info("Du hast keine Freunde.") - } - return@playerExecutor - } - - player.sendText { - appendNewline() - append(friendPagination(player.uniqueId).renderComponent(friendPlayer.friends)) - } + listFriends(player) } } @@ -207,34 +145,7 @@ fun friendCommand() = commandTree("friend") { receivedFriendRequestArgument("receivedRequest") { playerExecutorSuspend { player, args -> val receivedRequest: FriendRequest by args - val friendPlayer = player.friendPlayer - - if (friendPlayer.hasFriend(receivedRequest.senderUuid)) { - player.sendText { - appendErrorPrefix() - error("Du bist bereits mit diesem Spieler befreundet.") - } - return@playerExecutorSuspend - } - - val friendship = friendship( - requestedBy = receivedRequest.senderUuid, - acceptedBy = receivedRequest.receiverUuid, - requesterName = receivedRequest.senderName, - acceptorName = receivedRequest.receiverName - ) - - friendRequestService.deleteFriendRequest(receivedRequest) - friendShipService.saveFriendShip(friendship) - - player.sendText { - appendSuccessPrefix() - success("Du hast die Freundschaftsanfrage von ") - variableValue(receivedRequest.senderName) - success(" angenommen.") - } - - // TODO: Notify sender if online and if settings allow it + acceptFriend(player, receivedRequest) } } } @@ -253,7 +164,16 @@ fun friendCommand() = commandTree("friend") { success(" abgelehnt.") } - // TODO: Notify sender if online and if settings allow it + redisApi.publishEvent( + FriendNotifyRedisEvent( + playerUuid = receivedRequest.senderUuid, + buildText { + appendInfoPrefix() + info("Deine Freundschaftsanfrage an ") + variableValue(receivedRequest.receiverName) + info(" wurde abgelehnt.") + } + )) } } } @@ -272,26 +192,21 @@ fun friendCommand() = commandTree("friend") { success(" zurückgezogen.") } - // TODO: Notify receiver if online and if settings allow it + redisApi.publishEvent( + FriendNotifyRedisEvent( + playerUuid = sentRequest.receiverUuid, + buildText { + appendInfoPrefix() + info("Die Freundschaftsanfrage von ") + variableValue(player.name) + info(" wurde zurückgezogen.") + } + )) } } } } -private fun friendPagination(ownUuid: UUID) = Pagination { - title { primary("Deine Freunde".toSmallCaps(), TextDecoration.BOLD) } - - rowRenderer { friendship, _ -> - listOf(buildText { - spacer("-") - appendSpace() - variableValue(friendship.getOtherName(ownUuid)) - - // TODO: Display if online, if yes: where - }) - } -} - private val sendRequestsPagination = Pagination { title { primary("Gesendete Anfragen".toSmallCaps(), TextDecoration.BOLD) } diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/NonFriendOfflinePlayerArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/NonFriendOfflinePlayerArgument.kt new file mode 100644 index 0000000..b89ab56 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/NonFriendOfflinePlayerArgument.kt @@ -0,0 +1,74 @@ +package dev.slne.surf.friends.paper.command.argument.friend + +import dev.jorel.commandapi.CommandAPICommand +import dev.jorel.commandapi.CommandTree +import dev.jorel.commandapi.arguments.Argument +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.arguments.CustomArgument +import dev.jorel.commandapi.arguments.StringArgument +import dev.slne.surf.core.api.common.player.SurfPlayer +import dev.slne.surf.core.api.common.surfCoreApi +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.surfapi.core.api.messages.adventure.uuid +import dev.slne.surf.surfapi.core.api.util.logger +import kotlinx.coroutines.* +import kotlinx.coroutines.future.asDeferred +import kotlinx.coroutines.future.future + +class NonFriendOfflinePlayerArgument(nodeName: String) : + CustomArgument, String>(StringArgument(nodeName), { info -> + scope.future { + surfCoreApi.getOfflinePlayer(info.input) + }.asDeferred() + }) { + init { + this.replaceSuggestions( + ArgumentSuggestions.stringCollection { info -> + val friendPlayer = + friendPlayerService.players.find { it.uuid == info.sender.uuid() } + ?: return@stringCollection null + val friendUuids = friendPlayer.friends + .map { it.getOtherUuid(friendPlayer.uuid) } + .toSet() + + surfCoreApi.getOnlinePlayers() + .filterNot { it.uuid == friendPlayer.uuid } + .filterNot { it.uuid in friendUuids } + .mapNotNull { it.lastKnownName } + } + ) + } + + companion object { + private val log = logger() + private val scope = + CoroutineScope(Dispatchers.IO + CoroutineName("NonFriendOfflinePlayerArgument") + CoroutineExceptionHandler { _, throwable -> + log.atWarning() + .withCause(throwable) + .log("An error occurred in NonFriendOfflinePlayerArgument") + }) + } +} + +inline fun CommandTree.nonFriendOfflinePlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandTree = then( + NonFriendOfflinePlayerArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun Argument<*>.nonFriendOfflinePlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): Argument<*> = then( + NonFriendOfflinePlayerArgument(nodeName).setOptional(optional).apply(block) +) + +inline fun CommandAPICommand.nonFriendOfflinePlayerArgument( + nodeName: String, + optional: Boolean = false, + block: Argument<*>.() -> Unit = {} +): CommandAPICommand = + withArguments(NonFriendOfflinePlayerArgument(nodeName).setOptional(optional).apply(block)) \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt new file mode 100644 index 0000000..cb17c14 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt @@ -0,0 +1,175 @@ +package dev.slne.surf.friends.paper.command + +import dev.slne.surf.core.api.common.player.SurfPlayer +import dev.slne.surf.friends.api.friend.FriendRequest +import dev.slne.surf.friends.api.friend.Friendship +import dev.slne.surf.friends.api.friend.friendRequest +import dev.slne.surf.friends.api.friend.friendship +import dev.slne.surf.friends.core.loader.redisApi +import dev.slne.surf.friends.core.service.friendRequestService +import dev.slne.surf.friends.core.service.friendShipService +import dev.slne.surf.friends.paper.redis.event.FriendNotifyRedisEvent +import dev.slne.surf.friends.paper.util.friendPlayer +import dev.slne.surf.surfapi.core.api.font.toSmallCaps +import dev.slne.surf.surfapi.core.api.messages.adventure.buildText +import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination +import net.kyori.adventure.text.format.TextDecoration +import org.bukkit.entity.Player +import java.util.* + +suspend fun addFriend(player: Player, target: SurfPlayer?, rawTarget: String) { + if (target == null) { + player.sendText { + appendErrorPrefix() + error("Der Spieler wurde nicht gefunden.") + } + return + } + + if (target.uuid == player.uniqueId) { + player.sendText { + appendErrorPrefix() + error("Du kannst dich nicht selbst als Freund hinzufügen.") + } + return + } + + val friendPlayer = player.friendPlayer + + if (friendPlayer.hasSentFriendRequest(target.uuid)) { + player.sendText { + appendErrorPrefix() + error("Du hast diesem Spieler bereits eine Freundschaftsanfrage gesendet.") + } + return + } + + if (friendPlayer.hasFriend(target.uuid)) { + player.sendText { + appendErrorPrefix() + error("Du bist bereits mit diesem Spieler befreundet.") + } + return + } + + val existingRequest = friendPlayer.receivedFriendRequests.find { it.senderUuid == target.uuid } + + if (existingRequest != null) { + acceptFriend(player, existingRequest) + return + } + + val friendRequest = friendRequest( + senderUuid = player.uniqueId, + receiverUuid = target.uuid, + senderName = player.name, + receiverName = target.lastKnownName ?: rawTarget + ) + + friendRequestService.saveFriendRequest(friendRequest) + + player.sendText { + appendSuccessPrefix() + success("Du hast eine Freundschaftsanfrage an ") + variableValue(target.lastKnownName ?: rawTarget) + success(" gesendet.") + } + + redisApi.publishEvent( + FriendNotifyRedisEvent( + playerUuid = target.uuid, + buildText { + appendInfoPrefix() + info("Du hast eine Freundschaftsanfrage von ") + variableValue(player.name) + info(" erhalten.") + appendSpace() + append { + darkSpacer("[") + success("Annehmen") + darkSpacer("]") + clickRunsCommand("/friend accept ${player.name}") + } + appendSpace() + append { + darkSpacer("[") + error("Ablehnen") + darkSpacer("]") + clickRunsCommand("/friend decline ${player.name}") + } + } + )) +} + +suspend fun acceptFriend(player: Player, receivedRequest: FriendRequest) { + val friendPlayer = player.friendPlayer + + if (friendPlayer.hasFriend(receivedRequest.senderUuid)) { + player.sendText { + appendErrorPrefix() + error("Du bist bereits mit diesem Spieler befreundet.") + } + return + } + + val friendship = friendship( + requestedBy = receivedRequest.senderUuid, + acceptedBy = receivedRequest.receiverUuid, + requesterName = receivedRequest.senderName, + acceptorName = receivedRequest.receiverName + ) + + friendRequestService.deleteFriendRequest(receivedRequest) + friendShipService.saveFriendShip(friendship) + + player.sendText { + appendSuccessPrefix() + success("Du hast die Freundschaftsanfrage von ") + variableValue(receivedRequest.senderName) + success(" angenommen.") + } + + redisApi.publishEvent( + FriendNotifyRedisEvent( + playerUuid = receivedRequest.senderUuid, + buildText { + appendInfoPrefix() + info("Deine Freundschaftsanfrage an ") + variableValue(receivedRequest.receiverName) + info(" wurde angenommen.") + } + )) +} + +fun listFriends(player: Player) { + val friendPlayer = player.friendPlayer + + if (friendPlayer.friends.isEmpty()) { + player.sendText { + appendInfoPrefix() + info("Du hast keine Freunde.") + } + return + } + + player.sendText { + appendNewline() + append(friendPagination(player.uniqueId).renderComponent(friendPlayer.friends)) + } +} + +private fun friendPagination(ownUuid: UUID) = Pagination { + title { primary("Deine Freunde".toSmallCaps(), TextDecoration.BOLD) } + + rowRenderer { friendship, _ -> + listOf(buildText { + spacer("-") + appendSpace() + variableValue(friendship.getOtherName(ownUuid)) + + // TODO: Display if online, if yes: where + }) + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt new file mode 100644 index 0000000..97e13bb --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt @@ -0,0 +1,27 @@ +package dev.slne.surf.friends.paper.hook + +import dev.slne.surf.settings.api.surfSettingsApi +import org.bukkit.Bukkit +import java.util.* + +object SettingsHook { + fun isEnabled() = Bukkit.getPluginManager().isPluginEnabled("surf-settings-paper") + + fun hasFriendRequestsEnabled(playerUuid: UUID): Boolean { + return if (isEnabled()) { + surfSettingsApi.getPlayerSetting(playerUuid, "friend_requests")?.getBoolean() + ?: true + } else { + true + } + } + + fun hasFriendNotifyEnabled(playerUuid: UUID): Boolean { + return if (isEnabled()) { + surfSettingsApi.getPlayerSetting(playerUuid, "friend_notify")?.getBoolean() + ?: false + } else { + false + } + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt new file mode 100644 index 0000000..829e5cb --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt @@ -0,0 +1,35 @@ +package dev.slne.surf.friends.paper.redis + +import dev.slne.surf.friends.paper.hook.SettingsHook +import dev.slne.surf.friends.paper.redis.event.FriendNotifyRedisEvent +import dev.slne.surf.redis.event.OnRedisEvent +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import org.bukkit.Bukkit + +object FriendRedisListener { + @OnRedisEvent + fun onFriendNotifyMessage(event: FriendNotifyRedisEvent) { + val player = Bukkit.getPlayer(event.playerUuid) ?: return + + if (!SettingsHook.hasFriendNotifyEnabled(player.uniqueId)) { + return + } + + player.sendText { + append(event.message) + } + } + + @OnRedisEvent + fun onFriendRequestNotify(event: FriendNotifyRedisEvent) { + val player = Bukkit.getPlayer(event.playerUuid) ?: return + + if (!SettingsHook.hasFriendRequestsEnabled(player.uniqueId)) { + return + } + + player.sendText { + append(event.message) + } + } +} \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendNotifyRedisEvent.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendNotifyRedisEvent.kt new file mode 100644 index 0000000..b6bf9d1 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendNotifyRedisEvent.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.friends.paper.redis.event + +import dev.slne.surf.redis.event.RedisEvent +import dev.slne.surf.surfapi.core.api.serializer.adventure.component.SerializableComponent +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import java.util.* + +@Serializable +data class FriendNotifyRedisEvent( + val playerUuid: @Contextual UUID, + val message: SerializableComponent +) : RedisEvent() diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendRequestNotifyRedisEvent.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendRequestNotifyRedisEvent.kt new file mode 100644 index 0000000..d26d8f2 --- /dev/null +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/event/FriendRequestNotifyRedisEvent.kt @@ -0,0 +1,13 @@ +package dev.slne.surf.friends.paper.redis.event + +import dev.slne.surf.redis.event.RedisEvent +import dev.slne.surf.surfapi.core.api.serializer.adventure.component.SerializableComponent +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import java.util.* + +@Serializable +data class FriendRequestNotifyRedisEvent( + val playerUuid: @Contextual UUID, + val message: SerializableComponent +) : RedisEvent() From fdfb93fabf46f24743c6c20979efa7df58b13e7b Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 27 Feb 2026 15:27:18 +0100 Subject: [PATCH 36/43] feat: Enhance friend player argument handling to return real friend names --- .../paper/command/argument/friend/FriendPlayerArgument.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt index 2c4eb4e..46cac82 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/FriendPlayerArgument.kt @@ -13,7 +13,7 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.uuid class FriendPlayerArgument(nodeName: String) : CustomArgument(StringArgument(nodeName), { info -> - friendPlayerService.players.firstOrNull { it.name == info.input } // TODO: Only allow real friends, not every online friend player + friendPlayerService.players.firstOrNull { it.name == info.input } ?: throw CustomArgumentException.fromAdventureComponent( buildText { appendErrorPrefix() @@ -23,7 +23,11 @@ class FriendPlayerArgument(nodeName: String) : init { this.replaceSuggestions( ArgumentSuggestions.stringCollection { sender -> - friendPlayerService.players.find { it.uuid == sender.sender.uuid() }?.friends?.map { it.acceptorName } + friendPlayerService.players.find { it.uuid == sender.sender.uuid() }?.friends?.map { + it.getOtherName( + sender.sender.uuid() + ) + } } ) } From e83e50cb7b2e72d8cf9637f54836efb954bccea9 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 27 Feb 2026 15:30:05 +0100 Subject: [PATCH 37/43] feat: Update friend name retrieval to use getOtherName method for accuracy --- .../paper/command/argument/friend/OfflineFriendArgument.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt index 4a5f894..e8d7c2b 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/friend/OfflineFriendArgument.kt @@ -23,7 +23,11 @@ class OfflineFriendArgument(nodeName: String) : init { this.replaceSuggestions( ArgumentSuggestions.stringCollection { info -> - friendPlayerService.players.find { it.uuid == info.sender.uuid() }?.friends?.map { it.acceptorName } + friendPlayerService.players.find { it.uuid == info.sender.uuid() }?.friends?.map { + it.getOtherName( + info.sender.uuid() + ) + } } ) } From 6b322656876860097fcffb92e2bf86007b89d561 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 27 Feb 2026 15:49:04 +0100 Subject: [PATCH 38/43] feat: Add friend list and add commands with online status display --- .../dev/slne/surf/friends/paper/PaperMain.kt | 4 ++++ .../friends/paper/command/FriendCommand.kt | 20 ++++++++++++++++ .../paper/command/friend-command-util.kt | 24 +++++++++++++++---- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt index 69b4080..340f27a 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/PaperMain.kt @@ -4,7 +4,9 @@ import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin import dev.slne.surf.core.api.common.surfCoreApi import dev.slne.surf.friends.core.loader.databaseLoader import dev.slne.surf.friends.core.loader.redisLoader +import dev.slne.surf.friends.paper.command.friendAddCommand import dev.slne.surf.friends.paper.command.friendCommand +import dev.slne.surf.friends.paper.command.friendListCommand import dev.slne.surf.friends.paper.listener.PlayerConnectionListener import dev.slne.surf.friends.paper.listener.SurfPlayerListener import dev.slne.surf.friends.paper.redis.FriendRedisListener @@ -27,6 +29,8 @@ class PaperMain : SuspendingJavaPlugin() { surfCoreApi.registerListener(SurfPlayerListener) friendCommand() + friendAddCommand() + friendListCommand() } override suspend fun onDisableAsync() { diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt index 35ca02c..93ced21 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/FriendCommand.kt @@ -25,6 +25,26 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import dev.slne.surf.surfapi.core.api.messages.pagination.Pagination import net.kyori.adventure.text.format.TextDecoration +fun friendListCommand() = commandTree("fl") { + withPermission(PermissionRegistry.PREFIX_COMMAND) + + playerExecutor { player, _ -> + listFriends(player) + } +} + +fun friendAddCommand() = commandTree("fa") { + withPermission(PermissionRegistry.PREFIX_COMMAND) + + nonFriendOfflinePlayerArgument("target") { + playerExecutorSuspend { player, args -> + val target = args.awaiting("target") + + addFriend(player, target, args.getRaw("target") ?: "#Unknown") + } + } +} + fun friendCommand() = commandTree("friend") { withPermission(PermissionRegistry.PREFIX_COMMAND) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt index cb17c14..2052189 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt @@ -1,6 +1,7 @@ package dev.slne.surf.friends.paper.command import dev.slne.surf.core.api.common.player.SurfPlayer +import dev.slne.surf.core.api.common.surfCoreApi import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.api.friend.friendRequest @@ -156,7 +157,12 @@ fun listFriends(player: Player) { player.sendText { appendNewline() - append(friendPagination(player.uniqueId).renderComponent(friendPlayer.friends)) + append( + friendPagination(player.uniqueId).renderComponent( + friendPlayer.friends + .sortedBy { it.getOtherUuid(player.uniqueId).currentServerName() != null } + .sortedBy { it.getOtherName(player.uniqueId) }) + ) } } @@ -168,8 +174,18 @@ private fun friendPagination(ownUuid: UUID) = Pagination { spacer("-") appendSpace() variableValue(friendship.getOtherName(ownUuid)) - - // TODO: Display if online, if yes: where + appendSpace() + spacer("(") + friendship.getOtherUuid(ownUuid).currentServerName().let { + if (it != null) { + success("Online auf $it") + } else { + error("Offline") + } + } + spacer(")") }) } -} \ No newline at end of file +} + +fun UUID.currentServerName() = surfCoreApi.getPlayer(this)?.currentServer?.displayName \ No newline at end of file From 14746401e5e385c5c3130fd1df67fd9a0faac9ad Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 27 Feb 2026 17:03:19 +0100 Subject: [PATCH 39/43] feat: Implement friend request notifications and player loading enhancements --- .../repository/FriendPlayerRepository.kt | 13 ++++++ .../service/FriendPlayerServiceImpl.kt | 11 +++++ .../core/service/FriendPlayerService.kt | 1 + .../paper/command/friend-command-util.kt | 3 +- .../listener/PlayerConnectionListener.kt | 4 +- .../paper/listener/SurfPlayerListener.kt | 40 ++++++++++++++++++- .../paper/redis/FriendRedisListener.kt | 3 +- 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index 977c976..c5df573 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -13,6 +13,7 @@ import dev.slne.surf.friends.backend.table.FriendPlayerTable import dev.slne.surf.surfapi.bukkit.api.command.util.idOrThrow import it.unimi.dsi.fastutil.objects.ObjectSet import kotlinx.coroutines.flow.firstOrNull +import java.util.* val friendPlayerRepository = FriendPlayerRepository() @@ -62,6 +63,18 @@ class FriendPlayerRepository { } } + suspend fun loadPlayer(uuid: UUID) = suspendTransaction { + FriendPlayerTable.selectAll().where(FriendPlayerTable.playerUuid eq uuid).firstOrNull() + ?.let { + createPlayer( + row = it, + sentRequests = friendRequestRepository.loadSentRequests(uuid), + receivedRequests = friendRequestRepository.loadReceivedRequests(uuid), + friendShips = friendShipRepository.loadFriendShips(uuid) + ) + } + } + suspend fun savePlayer(player: FriendPlayer) = suspendTransaction { FriendPlayerTable.insert { it[playerUuid] = player.uuid diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt index 89b0ab7..e65ab01 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/service/FriendPlayerServiceImpl.kt @@ -48,6 +48,17 @@ class FriendPlayerServiceImpl : FriendPlayerService, Services.Fallback { return loadedPlayer } + override suspend fun findOrLoadPlayer(uuid: UUID): FriendPlayer? { + val cachedPlayer = players.firstOrNull { it.uuid == uuid } + if (cachedPlayer != null) { + return cachedPlayer + } + + val loadedPlayer = friendPlayerRepository.loadPlayer(uuid) + loadedPlayer?.let { cachePlayer(it) } + return loadedPlayer + } + override suspend fun savePlayer(friendPlayer: FriendPlayer) { friendPlayerRepository.savePlayer(friendPlayer) } diff --git a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt index 0e72f62..259bb4e 100644 --- a/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt +++ b/surf-friends-core/src/main/kotlin/dev/slne/surf/friends/core/service/FriendPlayerService.kt @@ -16,5 +16,6 @@ interface FriendPlayerService { suspend fun loadOrCreatePlayer(profile: PlayerProfile): FriendPlayer suspend fun findOrLoadPlayer(name: String): FriendPlayer? + suspend fun findOrLoadPlayer(uuid: UUID): FriendPlayer? suspend fun savePlayer(friendPlayer: FriendPlayer) } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt index 2052189..ac5492d 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt @@ -10,6 +10,7 @@ import dev.slne.surf.friends.core.loader.redisApi import dev.slne.surf.friends.core.service.friendRequestService import dev.slne.surf.friends.core.service.friendShipService import dev.slne.surf.friends.paper.redis.event.FriendNotifyRedisEvent +import dev.slne.surf.friends.paper.redis.event.FriendRequestNotifyRedisEvent import dev.slne.surf.friends.paper.util.friendPlayer import dev.slne.surf.surfapi.core.api.font.toSmallCaps import dev.slne.surf.surfapi.core.api.messages.adventure.buildText @@ -79,7 +80,7 @@ suspend fun addFriend(player: Player, target: SurfPlayer?, rawTarget: String) { } redisApi.publishEvent( - FriendNotifyRedisEvent( + FriendRequestNotifyRedisEvent( playerUuid = target.uuid, buildText { appendInfoPrefix() diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt index 0005e5f..33587f2 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/PlayerConnectionListener.kt @@ -29,7 +29,7 @@ object PlayerConnectionListener : Listener { info("Du hast noch ") variableValue(friendPlayer.receivedFriendRequests.size) info(" offene Freundschaftsanfragen.") - } // TODO: Only sent if enabled in player settings + } } if (friendPlayer.getOnlineFriendCount() > 0) { @@ -39,7 +39,7 @@ object PlayerConnectionListener : Listener { variableValue(friendPlayer.getOnlineFriendCount()) info(" Freunde online.") } - } // TODO: Only sent if enabled in player settings + } } @EventHandler diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt index 8a97ce0..5ed66ab 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/listener/SurfPlayerListener.kt @@ -1,17 +1,53 @@ package dev.slne.surf.friends.paper.listener +import com.github.shynixn.mccoroutine.folia.launch import dev.slne.surf.core.api.common.event.SurfEventHandler import dev.slne.surf.core.api.common.event.SurfPlayerConnectEvent import dev.slne.surf.core.api.common.event.SurfPlayerDisconnectEvent +import dev.slne.surf.friends.core.service.friendPlayerService +import dev.slne.surf.friends.paper.hook.SettingsHook +import dev.slne.surf.friends.paper.plugin +import dev.slne.surf.surfapi.core.api.messages.adventure.sendText +import org.bukkit.Bukkit object SurfPlayerListener { @SurfEventHandler fun onNetworkConnect(event: SurfPlayerConnectEvent) { - // TODO: Broadcast message to friends + plugin.launch { + val friendPlayer = + friendPlayerService.findOrLoadPlayer(event.player.uuid) ?: return@launch + + friendPlayer.friends + .mapNotNull { Bukkit.getPlayer(it.getOtherUuid(friendPlayer.uuid)) } + .filter { SettingsHook.hasFriendNotifyEnabled(it.uniqueId) } + .forEach { + it.sendText { + appendInfoPrefix() + info("Dein Freund ") + variableValue(friendPlayer.name) + info(" ist nun online.") + } + } + } } @SurfEventHandler fun onNetworkDisconnect(event: SurfPlayerDisconnectEvent) { - // TODO: Broadcast message to friends + plugin.launch { + val friendPlayer = + friendPlayerService.findOrLoadPlayer(event.player.uuid) ?: return@launch + + friendPlayer.friends + .mapNotNull { Bukkit.getPlayer(it.getOtherUuid(friendPlayer.uuid)) } + .filter { SettingsHook.hasFriendNotifyEnabled(it.uniqueId) } + .forEach { + it.sendText { + appendInfoPrefix() + info("Dein Freund ") + variableValue(friendPlayer.name) + info(" ist nun offline.") + } + } + } } } \ No newline at end of file diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt index 829e5cb..ce6f04b 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/redis/FriendRedisListener.kt @@ -2,6 +2,7 @@ package dev.slne.surf.friends.paper.redis import dev.slne.surf.friends.paper.hook.SettingsHook import dev.slne.surf.friends.paper.redis.event.FriendNotifyRedisEvent +import dev.slne.surf.friends.paper.redis.event.FriendRequestNotifyRedisEvent import dev.slne.surf.redis.event.OnRedisEvent import dev.slne.surf.surfapi.core.api.messages.adventure.sendText import org.bukkit.Bukkit @@ -21,7 +22,7 @@ object FriendRedisListener { } @OnRedisEvent - fun onFriendRequestNotify(event: FriendNotifyRedisEvent) { + fun onFriendRequestNotify(event: FriendRequestNotifyRedisEvent) { val player = Bukkit.getPlayer(event.playerUuid) ?: return if (!SettingsHook.hasFriendRequestsEnabled(player.uniqueId)) { From 930766bfee8bbf6f29287b08b0b4ea0ae197ce13 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 6 Mar 2026 19:45:41 +0100 Subject: [PATCH 40/43] feat: Improve friend request argument handling with null safety and error messaging --- .../argument/request/ReceivedFriendRequestArgument.kt | 7 ++++++- .../command/argument/request/SentFriendRequestArgument.kt | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt index e9841f0..0bbef0c 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/ReceivedFriendRequestArgument.kt @@ -13,7 +13,12 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.uuid class ReceivedFriendRequestArgument(nodeName: String) : CustomArgument(StringArgument(nodeName), { info -> - val senderPlayer = friendPlayerService.players.first { it.uuid == info.sender.uuid() } + val senderPlayer = friendPlayerService.players.firstOrNull { it.uuid == info.sender.uuid() } + ?: throw CustomArgumentException.fromAdventureComponent( + buildText { + appendErrorPrefix() + error("Dein Spielerprofil konnte nicht gefunden werden.") + }) senderPlayer.receivedFriendRequests.firstOrNull { it.senderName == info.input } ?: throw CustomArgumentException.fromAdventureComponent( diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt index c11165b..46dbf64 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/argument/request/SentFriendRequestArgument.kt @@ -13,7 +13,13 @@ import dev.slne.surf.surfapi.core.api.messages.adventure.uuid class SentFriendRequestArgument(nodeName: String) : CustomArgument(StringArgument(nodeName), { info -> - val senderPlayer = friendPlayerService.players.first { it.uuid == info.sender.uuid() } + val senderPlayer = + friendPlayerService.players.firstOrNull { it.uuid == info.sender.uuid() } + ?: throw CustomArgumentException.fromAdventureComponent( + buildText { + appendErrorPrefix() + error("Dein Spielerprofil konnte nicht gefunden werden.") + }) senderPlayer.sentFriendRequests.firstOrNull { it.receiverName == info.input } ?: throw CustomArgumentException.fromAdventureComponent( From 42886c762d44ceae8316d7a6e2746bb06572901d Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 6 Mar 2026 19:46:27 +0100 Subject: [PATCH 41/43] feat: Sort friend list by online status in descending order --- .../slne/surf/friends/paper/command/friend-command-util.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt index ac5492d..89ce2ac 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/command/friend-command-util.kt @@ -161,7 +161,9 @@ fun listFriends(player: Player) { append( friendPagination(player.uniqueId).renderComponent( friendPlayer.friends - .sortedBy { it.getOtherUuid(player.uniqueId).currentServerName() != null } + .sortedByDescending { + it.getOtherUuid(player.uniqueId).currentServerName() != null + } .sortedBy { it.getOtherName(player.uniqueId) }) ) } From edd9a33920757d5aa833759fdaec62970dedebcb Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 6 Mar 2026 19:47:19 +0100 Subject: [PATCH 42/43] feat: Replace insert with upsert for player data in FriendPlayerRepository --- .../surf/friends/backend/repository/FriendPlayerRepository.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt index c5df573..e0b88cc 100644 --- a/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt +++ b/surf-friends-backend/src/main/kotlin/dev/slne/surf/friends/backend/repository/FriendPlayerRepository.kt @@ -6,6 +6,7 @@ import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction +import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.upsert import dev.slne.surf.friends.api.friend.FriendRequest import dev.slne.surf.friends.api.friend.Friendship import dev.slne.surf.friends.api.player.FriendPlayer @@ -76,7 +77,7 @@ class FriendPlayerRepository { } suspend fun savePlayer(player: FriendPlayer) = suspendTransaction { - FriendPlayerTable.insert { + FriendPlayerTable.upsert { it[playerUuid] = player.uuid it[playerName] = player.name it[texture] = player.texture From baa7b6e0b4e079764db1a86036b02aaff46e4878 Mon Sep 17 00:00:00 2001 From: TheBjoRedCraft Date: Fri, 6 Mar 2026 19:54:02 +0100 Subject: [PATCH 43/43] feat: Update friend notification setting to default to enabled --- .../kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt index 97e13bb..1fbbdc8 100644 --- a/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt +++ b/surf-friends-paper/src/main/kotlin/dev/slne/surf/friends/paper/hook/SettingsHook.kt @@ -19,9 +19,9 @@ object SettingsHook { fun hasFriendNotifyEnabled(playerUuid: UUID): Boolean { return if (isEnabled()) { surfSettingsApi.getPlayerSetting(playerUuid, "friend_notify")?.getBoolean() - ?: false + ?: true } else { - false + true } } } \ No newline at end of file