flan/common/src/main/java/io/github/flemmli97/flan/event/EntityInteractEvents.java

396 lines
21 KiB
Java

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<ItemStack> 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<Claim> 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);
}
}