summaryrefslogtreecommitdiff
path: root/platform-paper/src
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-01-27 18:53:26 +0900
committerSho Sakuma <me@m1sk9.dev>2026-01-27 18:53:26 +0900
commit406481cff49ad7116eaae0c9d5f6b8b0728d961b (patch)
tree43ff7c85529b3f86ba02f081bd8ab9f1bbb7523d /platform-paper/src
parent35a87dc3fcd0d33712a81e7a91affea592141c8a (diff)
downloadLunaticChat-406481cff49ad7116eaae0c9d5f6b8b0728d961b.tar.gz
LunaticChat-406481cff49ad7116eaae0c9d5f6b8b0728d961b.tar.bz2
LunaticChat-406481cff49ad7116eaae0c9d5f6b8b0728d961b.zip
feat: Update Channel membership
Diffstat (limited to 'platform-paper/src')
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelManager.kt162
-rw-r--r--platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMembershipManager.kt53
2 files changed, 208 insertions, 7 deletions
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelManager.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelManager.kt
index 679d300..f5e1bf2 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelManager.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelManager.kt
@@ -9,6 +9,8 @@ import dev.m1sk9.lunaticChat.engine.exception.ChannelLimitExceededException
import dev.m1sk9.lunaticChat.engine.exception.ChannelMemberLimitExceededException
import dev.m1sk9.lunaticChat.engine.exception.ChannelNoOwnerPermissionException
import dev.m1sk9.lunaticChat.engine.exception.ChannelNotFoundException
+import dev.m1sk9.lunaticChat.engine.exception.ChannelPlayerAlreadyBannedException
+import dev.m1sk9.lunaticChat.engine.exception.ChannelPlayerNotBannedException
import dev.m1sk9.lunaticChat.paper.config.key.ChannelChatFeatureConfig
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@@ -86,18 +88,20 @@ class ChannelManager(
*
* @param channelId The ID of the channel to delete.
* @param requesterId The ID of the player requesting the deletion.
+ * @param hasBypassPermission Whether the requester has bypass permission.
* @return Result indicating success or failure of the deletion.
* @throws ChannelNotFoundException if the channel does not exist.
*/
fun deleteChannel(
channelId: String,
requesterId: UUID,
+ hasBypassPermission: Boolean = false,
): Result<Unit> {
val channel =
channelsCache[channelId]
?: return Result.failure(ChannelNotFoundException(channelId))
- if (channel.ownerId != requesterId) {
+ if (channel.ownerId != requesterId && !hasBypassPermission) {
return Result.failure(ChannelNoOwnerPermissionException(requesterId))
}
@@ -231,6 +235,162 @@ class ChannelManager(
}
/**
+ * Checks if a player is banned from a channel.
+ *
+ * @param channelId The ID of the channel.
+ * @param playerId The UUID of the player to check.
+ * @return Result containing true if banned, false otherwise.
+ * @throws ChannelNotFoundException if the channel does not exist.
+ */
+ fun isPlayerBanned(
+ channelId: String,
+ playerId: UUID,
+ ): Result<Boolean> {
+ val channel =
+ channelsCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ return Result.success(channel.bannedPlayers.contains(playerId))
+ }
+
+ /**
+ * Bans a player from a channel.
+ *
+ * @param channelId The ID of the channel.
+ * @param playerId The UUID of the player to ban.
+ * @return Result containing the updated channel or an error.
+ * @throws ChannelNotFoundException if the channel does not exist.
+ */
+ fun banPlayer(
+ channelId: String,
+ playerId: UUID,
+ ): Result<Channel> {
+ val channel =
+ channelsCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ // Check if player is already banned
+ if (channel.bannedPlayers.contains(playerId)) {
+ return Result.failure(ChannelPlayerAlreadyBannedException(playerId, channelId))
+ }
+
+ // Add player to banned list
+ val updatedChannel = channel.copy(bannedPlayers = channel.bannedPlayers + playerId)
+ channelsCache[channelId] = updatedChannel
+
+ // Remove from members if currently a member
+ membersCache[channelId]?.removeIf { it.playerId == playerId }
+
+ // Clear active channel if this was the player's active channel
+ if (activeChannels[playerId] == channelId) {
+ activeChannels.remove(playerId)
+ }
+
+ saveToStorage()
+ logger.info("Player $playerId banned from channel $channelId.")
+ return Result.success(updatedChannel)
+ }
+
+ /**
+ * Unbans a player from a channel.
+ *
+ * @param channelId The ID of the channel.
+ * @param playerId The UUID of the player to unban.
+ * @return Result containing the updated channel or an error.
+ * @throws ChannelNotFoundException if the channel does not exist.
+ * @throws ChannelPlayerNotBannedException if the player is not banned.
+ */
+ fun unbanPlayer(
+ channelId: String,
+ playerId: UUID,
+ ): Result<Channel> {
+ val channel =
+ channelsCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ // Check if player is actually banned
+ if (!channel.bannedPlayers.contains(playerId)) {
+ return Result.failure(ChannelPlayerNotBannedException(playerId, channelId))
+ }
+
+ // Remove player from banned list
+ val updatedChannel = channel.copy(bannedPlayers = channel.bannedPlayers - playerId)
+ channelsCache[channelId] = updatedChannel
+
+ saveToStorage()
+ logger.info("Player $playerId unbanned from channel $channelId.")
+ return Result.success(updatedChannel)
+ }
+
+ /**
+ * Updates a member's role in a channel.
+ *
+ * @param channelId The ID of the channel.
+ * @param playerId The UUID of the player whose role to update.
+ * @param newRole The new role for the member.
+ * @return Result indicating success or failure of the operation.
+ * @throws ChannelNotFoundException if the channel does not exist.
+ */
+ fun updateMemberRole(
+ channelId: String,
+ playerId: UUID,
+ newRole: ChannelRole,
+ ): Result<Unit> {
+ channelsCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ val members =
+ membersCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ val member = members.find { it.playerId == playerId } ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ // Update member role
+ members.remove(member)
+ members.add(member.copy(role = newRole))
+
+ saveToStorage()
+ return Result.success(Unit)
+ }
+
+ /**
+ * Updates the channel owner.
+ *
+ * @param channelId The ID of the channel.
+ * @param newOwnerId The UUID of the new owner.
+ * @return Result containing the updated channel or an error.
+ * @throws ChannelNotFoundException if the channel does not exist.
+ */
+ fun updateChannelOwner(
+ channelId: String,
+ newOwnerId: UUID,
+ ): Result<Channel> {
+ val channel =
+ channelsCache[channelId]
+ ?: return Result.failure(ChannelNotFoundException(channelId))
+
+ // Update channel owner
+ val updatedChannel = channel.copy(ownerId = newOwnerId)
+ channelsCache[channelId] = updatedChannel
+
+ // Update member roles - new owner becomes OWNER, old owner becomes MODERATOR
+ val members = membersCache[channelId]
+ if (members != null) {
+ members.replaceAll { member ->
+ when (member.playerId) {
+ newOwnerId -> member.copy(role = ChannelRole.OWNER)
+ channel.ownerId -> member.copy(role = ChannelRole.MODERATOR)
+ else -> member
+ }
+ }
+ }
+
+ saveToStorage()
+ logger.info("Channel $channelId owner changed from ${channel.ownerId} to $newOwnerId.")
+ return Result.success(updatedChannel)
+ }
+
+ /**
* Saves the current state of channels and members to storage asynchronously.
*/
private fun saveToStorage() {
diff --git a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMembershipManager.kt b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMembershipManager.kt
index 3c45eb4..07ce839 100644
--- a/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMembershipManager.kt
+++ b/platform-paper/src/main/kotlin/dev/m1sk9/lunaticChat/paper/chat/channel/ChannelMembershipManager.kt
@@ -5,8 +5,10 @@ import dev.m1sk9.lunaticChat.engine.exception.ChannelAlreadyActiveException
import dev.m1sk9.lunaticChat.engine.exception.ChannelMemberAlreadyException
import dev.m1sk9.lunaticChat.engine.exception.ChannelNotFoundException
import dev.m1sk9.lunaticChat.engine.exception.ChannelNotMemberException
+import dev.m1sk9.lunaticChat.engine.exception.ChannelPlayerBannedException
+import dev.m1sk9.lunaticChat.engine.exception.ChannelPlayerMembershipLimitExceededException
+import dev.m1sk9.lunaticChat.engine.exception.ChannelPrivateRequiresInvitationException
import dev.m1sk9.lunaticChat.engine.exception.ChannelRuntimeException
-import dev.m1sk9.lunaticChat.engine.exception.PlayerChannelLimitExceededException
import dev.m1sk9.lunaticChat.paper.config.key.ChannelChatFeatureConfig
import java.util.UUID
import java.util.logging.Logger
@@ -51,6 +53,23 @@ class ChannelMembershipManager(
}
/**
+ * Gets the role of a member in a channel without throwing exceptions.
+ *
+ * @param playerId The UUID of the player.
+ * @param channelId The ID of the channel.
+ * @return The ChannelRole if the player is a member, null otherwise.
+ */
+ fun getMemberRoleOrNull(
+ playerId: UUID,
+ channelId: String,
+ ): ChannelRole? =
+ channelManager
+ .getChannelMembers(channelId)
+ .getOrNull()
+ ?.find { it.playerId == playerId }
+ ?.role
+
+ /**
* Checks if a player has a specific role or higher in a channel.
*
* @param playerId The UUID of the player.
@@ -83,15 +102,17 @@ class ChannelMembershipManager(
*
* @param playerId The UUID of the player.
* @param channelId The ID of the channel.
+ * @param bypassPrivateCheck If true, allows joining private channels without invitation (used for invites).
* @return Result indicating success or failure.
* @throws ChannelNotFoundException if the channel does not exist.
* @throws ChannelMemberAlreadyException if the player is already a member of this channel.
- * @throws PlayerChannelLimitExceededException if the player has reached the maximum channel membership limit.
+ * @throws ChannelPlayerMembershipLimitExceededException if the player has reached the maximum channel membership limit.
* @throws ChannelRuntimeException for other runtime errors.
*/
fun joinChannel(
playerId: UUID,
channelId: String,
+ bypassPrivateCheck: Boolean = false,
): Result<Unit> {
// Check if channel exists
val channel =
@@ -109,6 +130,21 @@ class ChannelMembershipManager(
)
}
+ // Check if player is banned
+ val isBanned = channelManager.isPlayerBanned(channelId, playerId).getOrElse { false }
+ if (isBanned) {
+ return Result.failure(
+ ChannelPlayerBannedException(playerId, channelId),
+ )
+ }
+
+ // Check if this is a private channel (only invited members can join)
+ if (channel.isPrivate && !bypassPrivateCheck) {
+ return Result.failure(
+ ChannelPrivateRequiresInvitationException(playerId, channelId),
+ )
+ }
+
// Check if player is already a member
val isAlreadyMember =
isMember(playerId, channelId).getOrElse {
@@ -136,7 +172,7 @@ class ChannelMembershipManager(
if (playerChannelCount >= config.maxMembershipPerPlayer) {
return Result.failure(
- PlayerChannelLimitExceededException(playerId, config.maxMembershipPerPlayer),
+ ChannelPlayerMembershipLimitExceededException(playerId, config.maxMembershipPerPlayer),
)
}
}
@@ -153,8 +189,8 @@ class ChannelMembershipManager(
}
/**
- * Clears the player's active channel without removing them from channel membership.
- * The player remains a member of the channel and can rejoin by using the join command.
+ * Removes the player from the active channel completely.
+ * The player will be removed from the channel membership and the active channel will be cleared.
*
* @param playerId The UUID of the player.
* @return Result indicating success or failure.
@@ -167,9 +203,14 @@ class ChannelMembershipManager(
ChannelNotMemberException(playerId, "no active channel"),
)
+ // Remove from channel members
+ channelManager.removeMember(currentChannel, playerId).getOrElse {
+ return Result.failure(it)
+ }
+
// Clear active channel
channelManager.setPlayerChannel(playerId, null)
- logger.info("Player $playerId left active channel $currentChannel (still a member)")
+ logger.info("Player $playerId left channel $currentChannel (removed from members)")
return Result.success(Unit)
}