package io.github.flemmli97.flan.event; import io.github.flemmli97.flan.api.data.IPermissionContainer; import io.github.flemmli97.flan.api.permission.ClaimPermission; import io.github.flemmli97.flan.api.permission.ObjectToPermissionMap; import io.github.flemmli97.flan.api.permission.PermissionRegistry; import io.github.flemmli97.flan.claim.Claim; import io.github.flemmli97.flan.claim.ClaimStorage; import io.github.flemmli97.flan.config.ConfigHandler; import io.github.flemmli97.flan.mixin.IHungerAccessor; import io.github.flemmli97.flan.mixin.IPersistentProjectileVars; import io.github.flemmli97.flan.platform.CrossPlatformStuff; import io.github.flemmli97.flan.player.IOwnedItem; import io.github.flemmli97.flan.player.PlayerClaimData; import io.github.flemmli97.flan.player.TeleportUtils; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import net.minecraft.core.BlockPos; import net.minecraft.core.NonNullList; import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.DamageTypeTags; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.OwnableEntity; import net.minecraft.world.entity.animal.SnowGolem; import net.minecraft.world.entity.boss.wither.WitherBoss; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.decoration.ItemFrame; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.EnderMan; import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ThrownEgg; import net.minecraft.world.entity.projectile.ThrownEnderpearl; import net.minecraft.world.entity.projectile.ThrownPotion; import net.minecraft.world.entity.vehicle.AbstractMinecart; import net.minecraft.world.entity.vehicle.AbstractMinecartContainer; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import java.util.function.Consumer; public class EntityInteractEvents { public static InteractionResult attackEntity(Player player, Level world, InteractionHand hand, Entity entity, EntityHitResult hitResult) { return attackSimple(player, entity, true); } public static InteractionResult useAtEntity(Player player, Level world, InteractionHand hand, Entity entity, /* Nullable */ EntityHitResult hitResult) { if (!(player instanceof ServerPlayer) || player.isSpectator() || canInteract(entity)) return InteractionResult.PASS; if (entity instanceof Enemy) return InteractionResult.PASS; ClaimStorage storage = ClaimStorage.get((ServerLevel) world); BlockPos pos = entity.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim != null) { if (entity instanceof ArmorStand) { if (!claim.canInteract((ServerPlayer) player, PermissionRegistry.ARMORSTAND, pos, true)) return InteractionResult.FAIL; } if (entity instanceof Mob) return claim.canInteract((ServerPlayer) player, PermissionRegistry.ANIMALINTERACT, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; } return InteractionResult.PASS; } public static InteractionResult useEntity(Player p, Level world, InteractionHand hand, Entity entity) { if (!(p instanceof ServerPlayer player) || p.isSpectator() || canInteract(entity)) return InteractionResult.PASS; if (entity instanceof Enemy) return InteractionResult.PASS; ClaimStorage storage = ClaimStorage.get((ServerLevel) world); BlockPos pos = entity.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim != null) { if (entity instanceof Boat) return claim.canInteract(player, PermissionRegistry.BOAT, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; if (entity instanceof AbstractMinecart) { if (entity instanceof AbstractMinecartContainer) return claim.canInteract(player, PermissionRegistry.OPENCONTAINER, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; return claim.canInteract(player, PermissionRegistry.MINECART, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; } if (entity instanceof Villager) return claim.canInteract(player, PermissionRegistry.TRADING, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; if (entity instanceof ItemFrame) return claim.canInteract(player, PermissionRegistry.ITEMFRAMEROTATE, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; if (entity instanceof OwnableEntity tame) { if (tame.getOwnerUUID() != null && tame.getOwnerUUID().equals(player.getUUID())) return InteractionResult.PASS; } return claim.canInteract(player, PermissionRegistry.ANIMALINTERACT, pos, true) ? InteractionResult.PASS : InteractionResult.FAIL; } return InteractionResult.PASS; } public static boolean canInteract(Entity entity) { ResourceLocation id = CrossPlatformStuff.INSTANCE.registryEntities().getIDFrom(entity.getType()); return ConfigHandler.config.ignoredEntityTypes.contains(id.getNamespace()) || ConfigHandler.config.ignoredEntityTypes.contains(id.toString()) || entity.getTags().stream().anyMatch(ConfigHandler.config.entityTagIgnore::contains); } public static boolean projectileHit(Projectile proj, HitResult res) { if (proj.level().isClientSide) return false; Entity owner = proj.getOwner(); if (owner instanceof ServerPlayer player) { if (res.getType() == HitResult.Type.BLOCK) { BlockHitResult blockRes = (BlockHitResult) res; BlockPos pos = blockRes.getBlockPos(); BlockState state = proj.level().getBlockState(pos); ClaimPermission perm; if (proj instanceof ThrownEnderpearl) perm = PermissionRegistry.ENDERPEARL; else if (proj instanceof ThrownEgg || proj instanceof ThrownPotion) perm = PermissionRegistry.PROJECTILES; else perm = ObjectToPermissionMap.getFromBlock(state.getBlock()); if (perm != PermissionRegistry.ENDERPEARL && perm != PermissionRegistry.TARGETBLOCK && perm != PermissionRegistry.PROJECTILES) return false; ClaimStorage storage = ClaimStorage.get((ServerLevel) proj.level()); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim == null) return false; boolean flag = !claim.canInteract(player, perm, pos, true); if (flag) { if (proj instanceof AbstractArrow pers) { ((IPersistentProjectileVars) pers).setInBlockState(pers.level().getBlockState(pos)); Vec3 vec3d = blockRes.getLocation().subtract(pers.getX(), pers.getY(), pers.getZ()); pers.setDeltaMovement(vec3d); Vec3 vec3d2 = vec3d.normalize().scale(0.05000000074505806D); pers.setPosRaw(pers.getX() - vec3d2.x, pers.getY() - vec3d2.y, pers.getZ() - vec3d2.z); pers.playSound(((IPersistentProjectileVars) pers).getSoundEvent(), 1.0F, 1.2F / (pers.level().random.nextFloat() * 0.2F + 0.9F)); ((IPersistentProjectileVars) pers).setInGround(true); pers.shakeTime = 7; pers.setCritArrow(false); pers.setPierceLevel((byte) 0); pers.setSoundEvent(SoundEvents.ARROW_HIT); pers.setShotFromCrossbow(false); ((IPersistentProjectileVars) pers).resetPiercingStatus(); } if (proj instanceof ThrownEnderpearl) proj.remove(Entity.RemovalReason.KILLED); //TODO: find a way to properly update chorus fruit break on hit //player.getServer().send(new ServerTask(player.getServer().getTicks()+2, ()->player.world.updateListeners(pos, state, state, 2))); } return flag; } else if (res.getType() == HitResult.Type.ENTITY) { if (proj instanceof ThrownEnderpearl) { ClaimStorage storage = ClaimStorage.get((ServerLevel) proj.level()); IPermissionContainer claim = storage.getForPermissionCheck(proj.blockPosition()); return claim.canInteract(player, PermissionRegistry.ENDERPEARL, proj.blockPosition(), true); } Entity hit = ((EntityHitResult) res).getEntity(); boolean fail = attackSimple(player, hit, true) != InteractionResult.PASS; if (fail && proj instanceof AbstractArrow pers && ((AbstractArrow) proj).getPierceLevel() > 0) { IntOpenHashSet pierced = ((IPersistentProjectileVars) pers).getPiercedEntities(); if (pierced == null) pierced = new IntOpenHashSet(5); pierced.add(hit.getId()); ((IPersistentProjectileVars) pers).setPiercedEntities(pierced); pers.setPierceLevel((byte) (pers.getPierceLevel() + 1)); } return fail; } } return false; } public static boolean preventDamage(Entity entity, DamageSource source) { if (source.getEntity() instanceof ServerPlayer) return attackSimple((ServerPlayer) source.getEntity(), entity, true) != InteractionResult.PASS; else if (source.is(DamageTypeTags.IS_EXPLOSION) && !entity.level().isClientSide && !(entity instanceof ServerPlayer || entity instanceof Enemy)) { IPermissionContainer claim = ClaimStorage.get((ServerLevel) entity.level()).getForPermissionCheck(entity.blockPosition()); return claim != null && !claim.canInteract(null, PermissionRegistry.EXPLOSIONS, entity.blockPosition()); } return false; } public static InteractionResult attackSimple(Player p, Entity entity, boolean message) { if (!(p instanceof ServerPlayer player) || p.isSpectator() || canInteract(entity)) return InteractionResult.PASS; if (entity instanceof Enemy) return InteractionResult.PASS; ClaimStorage storage = ClaimStorage.get(player.serverLevel()); BlockPos pos = entity.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim != null) { if (entity instanceof ArmorStand || !(entity instanceof LivingEntity)) return claim.canInteract(player, PermissionRegistry.BREAKNONLIVING, pos, message) ? InteractionResult.PASS : InteractionResult.FAIL; if (entity instanceof Player) return claim.canInteract(player, PermissionRegistry.HURTPLAYER, pos, message) ? InteractionResult.PASS : InteractionResult.FAIL; return claim.canInteract(player, PermissionRegistry.HURTANIMAL, pos, message) ? InteractionResult.PASS : InteractionResult.FAIL; } return InteractionResult.PASS; } public static boolean xpAbsorb(Player player) { if (player instanceof ServerPlayer) { ClaimStorage storage = ClaimStorage.get((ServerLevel) player.level()); BlockPos pos = player.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim != null) return !claim.canInteract((ServerPlayer) player, PermissionRegistry.XP, pos, false); } return false; } public static boolean canCollideWith(Player player, Entity entity) { if (player instanceof ServerPlayer sPlayer) { if (entity instanceof ItemEntity) { IOwnedItem ownedItem = (IOwnedItem) entity; if (ownedItem.getDeathPlayer() != null) { ServerPlayer other = sPlayer.getServer().getPlayerList().getPlayer(ownedItem.getDeathPlayer()); if (other == null) return false; return ownedItem.getDeathPlayer().equals(player.getUUID()) || PlayerClaimData.get(other).deathItemsUnlocked(); } if (sPlayer.getUUID().equals(ownedItem.getPlayerOrigin())) return true; ClaimStorage storage = ClaimStorage.get(sPlayer.serverLevel()); BlockPos pos = sPlayer.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (claim != null) return claim.canInteract(sPlayer, PermissionRegistry.PICKUP, pos, false); } } return true; } public static boolean canDropItem(Player player, ItemStack stack) { if (!player.isDeadOrDying() && player instanceof ServerPlayer) { ClaimStorage storage = ClaimStorage.get((ServerLevel) player.level()); BlockPos pos = player.blockPosition(); IPermissionContainer claim = storage.getForPermissionCheck(pos); boolean allow = true; if (claim != null) allow = claim.canInteract((ServerPlayer) player, PermissionRegistry.DROP, pos, false); if (!allow) { player.getInventory().add(stack); NonNullList stacks = NonNullList.create(); for (int j = 0; j < player.containerMenu.slots.size(); ++j) { ItemStack itemStack2 = player.containerMenu.slots.get(j).getItem(); stacks.add(itemStack2.isEmpty() ? ItemStack.EMPTY : itemStack2); } ((ServerPlayer) player).connection.send(new ClientboundContainerSetContentPacket(player.containerMenu.containerId, 0, stacks, player.inventoryMenu.getCarried())); } return allow; } return true; } public static boolean witherCanDestroy(WitherBoss wither) { if (wither.level().isClientSide) return true; ClaimStorage storage = ClaimStorage.get((ServerLevel) wither.level()); BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); for (int x = -1; x <= 1; x++) for (int z = -1; z <= 1; z++) { pos.setWithOffset(wither.blockPosition(), x, 3, z); IPermissionContainer claim = storage.getForPermissionCheck(pos); if (!claim.canInteract(null, PermissionRegistry.WITHER, pos, false)) return false; } return true; } public static boolean canEndermanInteract(EnderMan enderman, BlockPos pos) { if (enderman.level().isClientSide) return true; ClaimStorage storage = ClaimStorage.get((ServerLevel) enderman.level()); IPermissionContainer claim = storage.getForPermissionCheck(pos); return claim.canInteract(null, PermissionRegistry.ENDERMAN, pos, false); } public static boolean canSnowGolemInteract(SnowGolem snowgolem) { if (snowgolem.level().isClientSide) return true; int x, y, z; for (int l = 0; l < 4; ++l) { x = Mth.floor(snowgolem.getX() + (l % 2 * 2 - 1) * 0.25F); y = Mth.floor(snowgolem.getY()); z = Mth.floor(snowgolem.getZ() + (l / 2 % 2 * 2 - 1) * 0.25F); BlockPos pos = new BlockPos(x, y, z); IPermissionContainer claim = ClaimStorage.get((ServerLevel) snowgolem.level()).getForPermissionCheck(pos); if (!claim.canInteract(null, PermissionRegistry.SNOWGOLEM, pos, false)) return false; } return true; } public static void updateDroppedItem(Player player, ItemEntity entity) { ((IOwnedItem) entity).setOriginPlayer((player)); } public static void updateClaim(ServerPlayer player, Claim currentClaim, Consumer cons) { Vec3 pos = player.position(); BlockPos rounded = TeleportUtils.roundedBlockPos(pos.add(0, player.getStandingEyeHeight(player.getPose(), player.getDimensions(player.getPose())), 0)); ClaimStorage storage = ClaimStorage.get(player.serverLevel()); if (currentClaim != null) { if (!currentClaim.intersects(player.getBoundingBox())) { boolean isSub = currentClaim.parentClaim() != null; Claim claim = isSub ? storage.getClaimAt(rounded) : currentClaim.parentClaim(); if (claim == null) currentClaim.displayLeaveTitle(player); else { Claim sub = claim.getSubClaim(rounded); boolean display = true; if (sub != null) claim = sub; else { display = currentClaim.enterTitle != null; if (claim.enterTitle == null) currentClaim.displayLeaveTitle(player); } if (display) claim.displayEnterTitle(player); } cons.accept(claim); } else { if (currentClaim.parentClaim() == null) { Claim sub = currentClaim.getSubClaim(rounded); if (sub != null) { currentClaim = sub; currentClaim.displayEnterTitle(player); cons.accept(currentClaim); } } if (!player.isSpectator()) { BlockPos.MutableBlockPos bPos = rounded.mutable(); boolean isSub = currentClaim.parentClaim() != null; Claim mainClaim = isSub ? currentClaim.parentClaim() : currentClaim; if (!mainClaim.canInteract(player, PermissionRegistry.CANSTAY, bPos, true)) { Claim sub = isSub ? currentClaim : null; Vec3 tp = TeleportUtils.getTeleportPos(player, pos, storage, sub != null ? sub.getDimensions() : mainClaim.getDimensions(), true, bPos, (claim, nPos) -> claim.canInteract(player, PermissionRegistry.CANSTAY, nPos, false)); if (player.isPassenger()) player.stopRiding(); player.teleportToWithTicket(tp.x(), tp.y(), tp.z()); } if (player.getAbilities().flying && !player.isCreative() && !mainClaim.canInteract(player, PermissionRegistry.FLIGHT, rounded, true)) { player.getAbilities().flying = false; player.connection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); } if (player.getFoodData().getSaturationLevel() < 2 && mainClaim.canInteract(player, PermissionRegistry.NOHUNGER, bPos, false)) { ((IHungerAccessor) player.getFoodData()).setSaturation(2); } currentClaim.applyEffects(player); } } } else if (player.tickCount % 3 == 0) { Claim claim = storage.getClaimAt(rounded); Claim sub = claim != null ? claim.getSubClaim(rounded) : null; if (sub != null) claim = sub; if (claim != null) claim.displayEnterTitle(player); cons.accept(claim); } } public static boolean canFrostwalkerFreeze(ServerLevel world, BlockPos pos, LivingEntity entity) { if (entity instanceof ServerPlayer) { IPermissionContainer claim = ClaimStorage.get(world).getForPermissionCheck(pos); return claim.canInteract((ServerPlayer) entity, PermissionRegistry.FROSTWALKER, pos, false); } return true; } public static boolean preventLightningConvert(Entity entity) { if (entity.level().isClientSide || entity instanceof Enemy) return false; ClaimStorage storage = ClaimStorage.get((ServerLevel) entity.level()); IPermissionContainer claim = storage.getForPermissionCheck(entity.blockPosition()); return !claim.canInteract(null, PermissionRegistry.LIGHTNING, entity.blockPosition(), false); } }