Skip to content

Commit

Permalink
PC-1059 Partial implementation of build permission
Browse files Browse the repository at this point in the history
  • Loading branch information
Polyana committed Dec 28, 2023
1 parent c10268a commit c3a5f80
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 20 deletions.
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.ClaimFlagId
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.id.ClamFlagId
import br.com.gamemods.minecity.api.serializer.UniqueId
import kotlinx.serialization.Serializable

Expand All @@ -14,7 +14,7 @@ import kotlinx.serialization.Serializable
*/
@Serializable
public data class ClaimSettings(
val defaultFlags: Map<ClamFlagId, ClaimFlagValue> = emptyMap(),
val defaultFlags: Map<ClaimFlagId, ClaimFlagValue> = emptyMap(),
val defaultPermissions: Map<ClaimPermissionId, Boolean?> = emptyMap(),
val trustLevels: List<TrustLevel> = emptyList(),
) {
Expand Down
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.ClaimFlagId
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.id.ClamFlagId
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 @@ -20,6 +20,6 @@ public data class TrustLevel(
val id: TrustLevelId,
val displayName: MiniComponent,
val players: Set<UniqueId> = emptySet(),
val flags: Map<ClamFlagId, ClaimFlagValue> = emptyMap(),
val flags: Map<ClaimFlagId, ClaimFlagValue> = emptyMap(),
val permissions: Map<ClaimPermissionId, Boolean?> = emptyMap()
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import kotlinx.serialization.Serializable
*/
@Serializable
@JvmInline
public value class ClamFlagId(private val id: String) {
public value class ClaimFlagId(private val id: String) {
override fun toString(): String = id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package br.com.gamemods.minecity.fabric.common.mixin;

import net.minecraft.fluid.Fluid;
import net.minecraft.item.BucketItem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(BucketItem.class)
public interface BlockItemAccessor {
@Accessor
Fluid getFluid();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package br.com.gamemods.minecity.fabric.common.mixin;

import br.com.gamemods.minecity.fabric.event.BlockCanPlaceCallback;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(BlockItem.class)
public class PlayerCanPlaceBlockMixin {
@Inject(method = "canPlace(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z", at = @At("HEAD"), cancellable = true)
private void restrict(ItemPlacementContext context, BlockState state, CallbackInfoReturnable<Boolean> cir) {
if (!BlockCanPlaceCallback.EVENT.invoker().canPlace(context, state)) {
cir.setReturnValue(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import br.com.gamemods.minecity.fabric.math.pos.FabricEntityLocation
import br.com.gamemods.minecity.fabric.server.MineCityFabricServer
import br.com.gamemods.minecity.fabric.service.FabricNamedPlayerService
import br.com.gamemods.minecity.fabric.service.FabricWorldService
import br.com.gamemods.minecity.fabric.service.permission.FabricBuildClaimPermission
import br.com.gamemods.minecity.fabric.service.permission.FabricDoorClaimPermission
import br.com.gamemods.minecity.fabric.wrapper.FabricBlockPosWrapper
import br.com.gamemods.minecity.fabric.wrapper.FabricChunkPosWrapper
Expand Down Expand Up @@ -80,6 +81,7 @@ object MineCityFabric : ModInitializer, MineCityPlatform {

private fun registerPermissions() {
core.permission += FabricDoorClaimPermission()
core.permission += FabricBuildClaimPermission()
}

@ServerSideOnly
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package br.com.gamemods.minecity.fabric.event

import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import net.fabricmc.fabric.api.event.Event
import net.fabricmc.fabric.api.event.EventFactory
import net.minecraft.block.BlockState
import net.minecraft.item.ItemPlacementContext

@InternalMineCityApi
interface BlockCanPlaceCallback {

fun canPlace(context: ItemPlacementContext, state: BlockState): Boolean

companion object {
@JvmField
val EVENT: Event<BlockCanPlaceCallback> = EventFactory.createArrayBacked(BlockCanPlaceCallback::class.java) { listeners ->
object : BlockCanPlaceCallback {
override fun canPlace(context: ItemPlacementContext, state: BlockState): Boolean {
for (listener in listeners) {
if (!listener.canPlace(context, state))
return false
}

return true
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package br.com.gamemods.minecity.fabric.service.permission

import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.fabric.MineCityFabric
import br.com.gamemods.minecity.fabric.common.mixin.BlockItemAccessor
import br.com.gamemods.minecity.fabric.event.BlockCanPlaceCallback
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents
import net.fabricmc.fabric.api.event.player.UseBlockCallback
import net.kyori.adventure.text.Component
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.Fluids
import net.minecraft.item.BucketItem
import net.minecraft.item.ItemPlacementContext
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.HitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World

/**
* Represents a permission that can be granted to a player in a claim.
*
* @property id The unique identifier for the permission.
* @property name The name of the permission.
* @property description The description of the permission.
*/
@InternalMineCityApi
class FabricBuildClaimPermission: FabricClaimPermission(
id = ClaimPermissionId.BUILD,
name = Component.text("Build"),
description = Component.text("Allows the player to build in the claim.")
) {
override fun onRegister() {
PlayerBlockBreakEvents.BEFORE.register(OnBlockBreak())
BlockCanPlaceCallback.EVENT.register(OnBlockCanPlace())
UseBlockCallback.EVENT.register(OnUseBlock())
}

private inner class OnUseBlock : UseBlockCallback {
override fun interact(player: PlayerEntity, world: World, hand: Hand, hitResult: BlockHitResult): ActionResult {
if (world.isClient) {
return ActionResult.PASS
}

if (hand != Hand.MAIN_HAND || hitResult.type != HitResult.Type.BLOCK) {
return ActionResult.PASS
}

val bucket = player.mainHandStack.item as? BucketItem ?: return ActionResult.PASS
val fluid = (bucket as BlockItemAccessor).fluid
val affectedBlockPos = if (fluid == Fluids.EMPTY) {
hitResult.blockPos
} else {
hitResult.blockPos.offset(hitResult.side)
}
MineCityFabric.core.log.info { "Bucket: $bucket, fluid: $fluid, affectedBlockPos: $affectedBlockPos, hitResult.blockPos: ${hitResult.blockPos}" }
return if (checkPermission(world, player, affectedBlockPos, ClaimPermissionId.BUILD)) {
ActionResult.PASS
} else {
ActionResult.FAIL
}
}
}

private inner class OnBlockBreak : PlayerBlockBreakEvents.Before {
override fun beforeBlockBreak(world: World, player: PlayerEntity, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?): Boolean {
return checkPermission(world, player, pos, ClaimPermissionId.BUILD)
}
}

private inner class OnBlockCanPlace : BlockCanPlaceCallback {
override fun canPlace(context: ItemPlacementContext, state: BlockState): Boolean {
val player = context.player ?: return true // TODO: If the player is null, might be a piston, CC turtle, etc; Must check if the owner of the block has permission to build on the target claim
return checkPermission(context.world, player, context.blockPos, ClaimPermissionId.BUILD)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package br.com.gamemods.minecity.fabric.service.permission

import br.com.gamemods.minecity.api.MineCity
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.service.permission.ClaimPermission
import br.com.gamemods.minecity.fabric.service.claim.FabricClaimService.Companion.get
import net.kyori.adventure.text.Component
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World

/**
* Represents a permission that can be granted to a player in a claim.
*
* @property id The unique identifier for the permission.
* @property name The name of the permission.
* @property description The description of the permission.
*/
@InternalMineCityApi
abstract class FabricClaimPermission(
id: ClaimPermissionId,
name: Component,
description: Component
) : ClaimPermission(id, name, description) {

/**
* Checks if the player has the permission in the claim.
*
* @param world The world where the permission is being checked.
* @param player The player that is being checked.
* @param pos The position where the permission is being checked.
* @param permissionId The permission that is being checked.
* @return `true` if the player has the permission, `false` otherwise.
*/
protected fun checkPermission(world: World, player: PlayerEntity, pos: BlockPos, permissionId: ClaimPermissionId): Boolean {
if (world.isClient) {
return true
}

val claim = MineCity.claims[world, pos] ?: return true
return claim.hasPermission(player.identity().uuid(), permissionId)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package br.com.gamemods.minecity.fabric.service.permission

import br.com.gamemods.minecity.api.MineCity
import br.com.gamemods.minecity.api.annotation.internal.InternalMineCityApi
import br.com.gamemods.minecity.api.id.ClaimPermissionId
import br.com.gamemods.minecity.api.service.permission.ClaimPermission
import br.com.gamemods.minecity.fabric.service.claim.FabricClaimService.Companion.get
import net.fabricmc.fabric.api.event.player.UseBlockCallback
import net.kyori.adventure.text.Component
import net.minecraft.block.DoorBlock
Expand All @@ -16,8 +13,15 @@ import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.HitResult
import net.minecraft.world.World

/**
* Represents a permission that can be granted to a player in a claim.
*
* @property id The unique identifier for the permission.
* @property name The name of the permission.
* @property description The description of the permission.
*/
@InternalMineCityApi
class FabricDoorClaimPermission: ClaimPermission(
class FabricDoorClaimPermission: FabricClaimPermission(
id = ClaimPermissionId.DOORS,
name = Component.text("Doors"),
description = Component.text("Allows the player to open and close doors in the claim.")
Expand All @@ -41,11 +45,10 @@ class FabricDoorClaimPermission: ClaimPermission(
val block = blockState.block

if (block !is DoorBlock && block !is TrapdoorBlock) {
return ActionResult.SUCCESS
return ActionResult.PASS
}

val claim = MineCity.claims[world, clickPos] ?: return ActionResult.PASS
return if (claim.hasPermission(player.identity().uuid(), ClaimPermissionId.DOORS)) {
return if (checkPermission(world, player, clickPos, ClaimPermissionId.DOORS)) {
ActionResult.PASS
} else {
ActionResult.FAIL
Expand Down
18 changes: 10 additions & 8 deletions platform/fabric/src/main/resources/minecity.mixins.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"required": true,
"package": "br.com.gamemods.minecity.fabric.common.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ExampleMixin"
],
"injectors": {
"defaultRequire": 1
"required": true,
"package": "br.com.gamemods.minecity.fabric.common.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"BlockItemAccessor",
"ExampleMixin",
"PlayerCanPlaceBlockMixin"
],
"injectors": {
"defaultRequire": 1
}
}

0 comments on commit c3a5f80

Please sign in to comment.