Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements door permission check (and necessary subsystems) #2

Merged
merged 2 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/src/main/kotlin/br/com/gamemods/minecity/api/MineCity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package br.com.gamemods.minecity.api

import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.service.MineCityInternal
import br.com.gamemods.minecity.api.service.claim.ClaimService
import br.com.gamemods.minecity.api.service.namedplayer.NamedPlayerService
import br.com.gamemods.minecity.api.service.permission.PermissionService

Expand All @@ -25,6 +26,11 @@ public interface MineCity {
*/
public val permission: PermissionService

/**
* Allows to get the claims with easy
*/
public val claims: ClaimService

/**
* This companion object allows MineCity interface to be used directly in kotlin delegating all API calls to
* the instance that is set at [MineCityInternal.implementation].
Expand Down
27 changes: 25 additions & 2 deletions api/src/main/kotlin/br/com/gamemods/minecity/api/claim/Claim.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
package br.com.gamemods.minecity.api.claim

import br.com.gamemods.minecity.api.MineCity
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.ClaimId
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.serializer.UniqueId
import br.com.gamemods.minecity.api.service.MineCityInternal
import kotlinx.serialization.Serializable

@Serializable
public data class Claim(
val id: ClaimId = ClaimId(),
val id: ClaimId,
val shape: Set<ClaimShape>,
val name: String,
val parentId: ClaimId? = null,
val owner: UniqueId? = null,
val rent: ClaimRentContract? = null,
val city: City? = null,
val isAdmin: Boolean = false,
val settings: ClaimSettings = ClaimSettings(),
)
) {
@OptIn(InternalMineCityApi::class)
public fun hasPermission(playerId: UniqueId, permissionId: ClaimPermissionId, silent: Boolean = false): Boolean {
if (MineCity.players.isAdminMode(playerId)) {
return true
}
if (rent != null && rent.rentedTo == playerId) {
return true
} else if (playerId == owner) {
return true
}

val allow = settings.hasPermission(playerId, permissionId)
if (!allow && !silent) {
MineCity.players.sendMessage(playerId, MineCityInternal.permissionDeniedMessage(this, permissionId))
}
return allow
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package br.com.gamemods.minecity.api.claim

import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.id.ClamFlagId
import br.com.gamemods.minecity.api.id.ClamPermissionId
import br.com.gamemods.minecity.api.serializer.UniqueId
import kotlinx.serialization.Serializable

/**
Expand All @@ -14,6 +15,22 @@ import kotlinx.serialization.Serializable
@Serializable
public data class ClaimSettings(
val defaultFlags: Map<ClamFlagId, ClaimFlagValue> = emptyMap(),
val defaultPermissions: Map<ClamPermissionId, Boolean?> = emptyMap(),
val defaultPermissions: Map<ClaimPermissionId, Boolean?> = emptyMap(),
val trustLevels: List<TrustLevel> = emptyList(),
)
) {
/**
* Calculates the effective flags of this claim.
*
* @param playerId The player that is checking the flags
* @return The effective flags of this claim
*/
public fun hasPermission(playerId: UniqueId, permissionId: ClaimPermissionId): Boolean {
return defaultPermissions[permissionId] ?: trustLevels.fold(false) { currentResult, level ->
if (playerId !in level.players) {
currentResult
} else {
level.permissions[permissionId] ?: currentResult
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package br.com.gamemods.minecity.api.claim

import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.id.ClamFlagId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, small typo here. You fixed ClaimPermission, but we also have another typo: "ClamFlagId"

import br.com.gamemods.minecity.api.id.ClamPermissionId
import br.com.gamemods.minecity.api.id.TrustLevelId
import br.com.gamemods.minecity.api.serializer.MiniComponent
import br.com.gamemods.minecity.api.serializer.UniqueId
Expand All @@ -21,5 +21,5 @@ public data class TrustLevel(
val displayName: MiniComponent,
val players: Set<UniqueId> = emptySet(),
val flags: Map<ClamFlagId, ClaimFlagValue> = emptyMap(),
val permissions: Map<ClamPermissionId, Boolean?> = emptyMap()
val permissions: Map<ClaimPermissionId, Boolean?> = emptyMap()
)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package br.com.gamemods.minecity.api.service

import br.com.gamemods.minecity.api.MineCity
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.claim.Claim
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor

/**
* Internal MineCity states and services, should not be used by API users.
Expand All @@ -12,4 +16,19 @@ public object MineCityInternal {
* Access to the MineCity API implementation, must be modified only by MineCity itself, can be accessed freely.
*/
public lateinit var implementation: MineCity

/**
* Creates a message to be sent to the player when a permission is denied.
*/
public fun permissionDeniedMessage(claim: Claim, permissionId: ClaimPermissionId): Component {
return Component.text()
.content("MineCity> ").color(NamedTextColor.DARK_RED)
.append(
Component.text().color(NamedTextColor.RED)
.append(Component.text("You don't have "))
.append(MineCity.permission[permissionId].name.color(NamedTextColor.YELLOW))
.append(Component.text(" permission in "))
.append(Component.text(claim.name, NamedTextColor.YELLOW))
).build()
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
package br.com.gamemods.minecity.api.service.namedplayer

import br.com.gamemods.minecity.api.annotation.threading.ASyncFriendly
import br.com.gamemods.minecity.api.annotation.threading.SyncFriendly
import br.com.gamemods.minecity.api.id.NamedPlayer
import br.com.gamemods.minecity.api.serializer.UniqueId
import kotlinx.coroutines.Deferred
import java.util.*
import net.kyori.adventure.text.Component

/**
* Manages the [NamedPlayer] objects.
*/
public interface NamedPlayerService {
/**
* Gets [NamedPlayer] for the given player [uuid], attempts to use a cached value before starting a query.
* Gets [NamedPlayer] for the given player [playerId], attempts to use a cached value before starting a query.
*/
public operator fun get(uuid: UUID): Deferred<NamedPlayer?>
public operator fun get(playerId: UniqueId): Deferred<NamedPlayer?>

/**
* Gets [NamedPlayer] for the given player [name], attempts to use a cached value before starting a query.
*/
public operator fun get(name: String): Deferred<NamedPlayer?>

/**
* Checks if the given player [uuid] has ready-to-use cached value.
* Checks if the given player [playerId] has ready-to-use cached value.
*/
@SyncFriendly
public operator fun contains(uuid: UUID): Boolean
public operator fun contains(playerId: UniqueId): Boolean

/**
* Checks if the given player [name] has ready-to-use cached value.
*/
@SyncFriendly
public operator fun contains(name: String): Boolean

/**
* Sends a message to the given player [playerId] if the player is online.
*/
@SyncFriendly
@ASyncFriendly
public fun sendMessage(playerId: UniqueId, message: Component)

/**
* Checks if the given player [playerId] is in admin mode.
*/
public fun isAdminMode(playerId: UniqueId): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import br.com.gamemods.minecity.api.MineCity
import br.com.gamemods.minecity.api.MineCityPlatform
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.annotation.side.ServerSideOnly
import br.com.gamemods.minecity.api.service.claim.ClaimService
import br.com.gamemods.minecity.api.service.namedplayer.NamedPlayerService
import br.com.gamemods.minecity.api.service.permission.PermissionService
import br.com.gamemods.minecity.core.service.world.WorldService
Expand All @@ -23,6 +24,7 @@ class MineCityCore(
val worlds: WorldService,
override val players: NamedPlayerService,
override val permission: PermissionService,
override val claims: ClaimService,
): MineCity {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package br.com.gamemods.minecity.core.service.claim

import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.claim.Claim
import br.com.gamemods.minecity.api.id.ClaimId
import br.com.gamemods.minecity.api.id.WorldId
import br.com.gamemods.minecity.api.math.pos.BlockGridPositioned
import br.com.gamemods.minecity.api.service.claim.ClaimService
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred

@InternalMineCityApi
class CoreClaimService: ClaimService {
@Suppress("CanBeVal", "UnnecessaryVariable")
override fun get(worldId: WorldId, pos: BlockGridPositioned): Claim? {
//TODO Implement
var result: Claim? = Claim(
id = ClaimId(),
shape = emptySet(),
name = "TestClaim",
)
//result = null
return result
}

override fun load(worldId: WorldId, pos: BlockGridPositioned): Deferred<Claim?> {
//TODO Implement
return CompletableDeferred(null)
}

override fun create(claim: Claim): Deferred<Claim> {
//TODO Implement
return CompletableDeferred(claim)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import br.com.gamemods.minecity.api.service.MineCityInternal
import br.com.gamemods.minecity.core.MineCityCore
import br.com.gamemods.minecity.core.dispatchers.Async
import br.com.gamemods.minecity.core.dispatchers.Sync
import br.com.gamemods.minecity.core.service.claim.CoreClaimService
import br.com.gamemods.minecity.core.service.permission.CorePermissionService
import br.com.gamemods.minecity.fabric.math.pos.FabricBlockLocation
import br.com.gamemods.minecity.fabric.math.pos.FabricChunkLocation
Expand Down Expand Up @@ -63,6 +64,7 @@ object MineCityFabric : ModInitializer, MineCityPlatform {
worlds = FabricWorldService(this),
players = FabricNamedPlayerService(this),
permission = CorePermissionService(),
claims = CoreClaimService(),
)
MineCityInternal.implementation = core
core.onInitialize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package br.com.gamemods.minecity.fabric.service

import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.NamedPlayer
import br.com.gamemods.minecity.api.serializer.UniqueId
import br.com.gamemods.minecity.api.service.namedplayer.NamedPlayerService
import br.com.gamemods.minecity.fabric.MineCityFabric
import com.google.common.cache.CacheBuilder
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.datetime.Clock
import net.kyori.adventure.text.Component
import java.util.*
import java.util.concurrent.TimeUnit

Expand All @@ -17,12 +19,27 @@ class FabricNamedPlayerService(private val platform: MineCityFabric): NamedPlaye
private val uuid2named = CacheBuilder.newBuilder().expireAfterAccess(30L, TimeUnit.MINUTES).build<UUID, Deferred<NamedPlayer?>>()
private val name2named = CacheBuilder.newBuilder().expireAfterAccess(30L, TimeUnit.MINUTES).build<String, Deferred<NamedPlayer?>>()

override fun get(uuid: UUID): Deferred<NamedPlayer?> {
override fun isAdminMode(playerId: UniqueId): Boolean {
//TODO Implement
return false
}

@Suppress("DeferredResultUnused")
override fun sendMessage(playerId: UniqueId, message: Component) {
platform.runOnServer { server ->
server.syncOnly {
server.mcServer.native.playerManager.getPlayer(playerId)?.sendMessage(message)
}
}
}

@Suppress("CheckedExceptionsKotlin")
override fun get(playerId: UUID): Deferred<NamedPlayer?> {
@Suppress("DuplicatedCode")
return uuid2named.get(uuid) {
return uuid2named.get(playerId) {
platform.runOnServer { server ->
server.syncOnly {
server.mcServer.native.playerManager.getPlayer(uuid)?.let { player ->
server.mcServer.native.playerManager.getPlayer(playerId)?.let { player ->
NamedPlayer(player.uuid, player.entityName, Clock.System.now()).also { named ->
name2named.put(named.name, CompletableDeferred(named))
}
Expand All @@ -32,6 +49,7 @@ class FabricNamedPlayerService(private val platform: MineCityFabric): NamedPlaye
}
}

@Suppress("CheckedExceptionsKotlin")
override fun get(name: String): Deferred<NamedPlayer?> {
@Suppress("DuplicatedCode")
return name2named.get(name) {
Expand All @@ -48,8 +66,8 @@ class FabricNamedPlayerService(private val platform: MineCityFabric): NamedPlaye
}

@OptIn(ExperimentalCoroutinesApi::class)
override fun contains(uuid: UUID): Boolean {
return uuid2named.getIfPresent(uuid)?.let { deferred ->
override fun contains(playerId: UUID): Boolean {
return uuid2named.getIfPresent(playerId)?.let { deferred ->
if (deferred.isCompleted) {
try {
deferred.getCompleted() != null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package br.com.gamemods.minecity.fabric.service.claim

import br.com.gamemods.minecity.api.annotation.PlatformDependent
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.WorldId
import br.com.gamemods.minecity.api.service.claim.ClaimService
import br.com.gamemods.minecity.fabric.wrapper.FabricBlockPosWrapper.Companion.wrapper
import net.minecraft.registry.RegistryKey
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World

@InternalMineCityApi
class FabricClaimService {
companion object {
operator fun ClaimService.get(world: World, pos: BlockPos) = this[world.mineCityWorldId, pos.wrapper]

@OptIn(PlatformDependent::class)
val RegistryKey<World>.mineCityWorldId get() = WorldId(value.toString())

val World.mineCityWorldId get() = registryKey.mineCityWorldId
}
}
Loading
Loading