src: add 2 files

This commit is contained in:
luk
2026-01-27 21:18:44 +00:00
parent 49960726f8
commit d604fcdc7e
2 changed files with 924 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
package com.hypixel.hytale.server.core.event.events.ecs;
import com.hypixel.hytale.component.system.CancellableEcsEvent;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import javax.annotation.Nonnull;
/**
* Event fired when a player attempts to gather (harvest) a block.
* This event is cancellable - if cancelled, the block will not be gathered.
*/
public class GatherBlockEvent extends CancellableEcsEvent {
@Nonnull
private final Vector3i targetBlock;
@Nonnull
private final BlockType blockType;
public GatherBlockEvent(@Nonnull Vector3i targetBlock, @Nonnull BlockType blockType) {
this.targetBlock = targetBlock;
this.blockType = blockType;
}
@Nonnull
public Vector3i getTargetBlock() {
return this.targetBlock;
}
@Nonnull
public BlockType getBlockType() {
return this.blockType;
}
}

View File

@@ -0,0 +1,892 @@
package com.hypixel.hytale.server.core.modules.interaction;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockSoundEvent;
import com.hypixel.hytale.protocol.SoundCategory;
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
import com.hypixel.hytale.server.core.asset.type.blocksound.config.BlockSoundSet;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockBreakingDropType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockGathering;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.HarvestingDropType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.PhysicsDropType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.SoftBlockDropType;
import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
import com.hypixel.hytale.server.core.asset.type.gameplay.BrokenPenalties;
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
import com.hypixel.hytale.server.core.asset.type.gameplay.GatheringEffectsConfig;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.asset.type.item.config.ItemTool;
import com.hypixel.hytale.server.core.asset.type.item.config.ItemToolSpec;
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
import com.hypixel.hytale.server.core.entity.ItemUtils;
import com.hypixel.hytale.server.core.entity.LivingEntity;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;
import com.hypixel.hytale.server.core.event.events.ecs.GatherBlockEvent;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealth;
import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthChunk;
import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthModule;
import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.modules.item.ItemModule;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
import com.hypixel.hytale.server.core.universe.world.SoundUtil;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlocksUtil;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.FillerBlockUtil;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectLists;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class BlockHarvestUtils {
public BlockHarvestUtils() {
}
@Nullable
public static ItemToolSpec getSpecPowerDamageBlock(@Nullable Item item, @Nullable BlockType blockType, @Nullable ItemTool tool) {
if (blockType == null) {
return null;
} else {
BlockGathering gathering = blockType.getGathering();
if (gathering == null) {
return null;
} else {
BlockBreakingDropType breaking = gathering.getBreaking();
if (breaking == null) {
return null;
} else {
String gatherType = breaking.getGatherType();
if (gatherType == null) {
return null;
} else if (item == null || item.getWeapon() == null && item.getBuilderToolData() == null) {
int requiredQuality = breaking.getQuality();
if (tool == null) {
ItemToolSpec defaultSpec = ItemToolSpec.getAssetMap().getAsset(gatherType);
return defaultSpec != null && defaultSpec.getQuality() < requiredQuality ? null : defaultSpec;
} else {
if (tool.getSpecs() != null) {
for (ItemToolSpec spec : tool.getSpecs()) {
if (Objects.equals(spec.getGatherType(), gatherType)) {
if (spec.getQuality() < requiredQuality) {
return null;
}
return spec;
}
}
}
return null;
}
} else {
return null;
}
}
}
}
}
public static double calculateDurabilityUse(@Nonnull Item item, @Nullable BlockType blockType) {
if (blockType == null) {
return 0.0;
} else if (blockType.getGathering().isSoft()) {
return 0.0;
} else if (item.getTool() == null) {
return 0.0;
} else {
ItemTool itemTool = item.getTool();
ItemTool.DurabilityLossBlockTypes[] durabilityLossBlockTypes = itemTool.getDurabilityLossBlockTypes();
if (durabilityLossBlockTypes == null) {
return item.getDurabilityLossOnHit();
} else {
String hitBlockTypeId = blockType.getId();
int hitBlockTypeIndex = BlockType.getAssetMap().getIndex(hitBlockTypeId);
if (hitBlockTypeIndex == Integer.MIN_VALUE) {
throw new IllegalArgumentException("Unknown key! " + hitBlockTypeId);
} else {
BlockSetModule blockSetModule = BlockSetModule.getInstance();
for (ItemTool.DurabilityLossBlockTypes durabilityLossBlockType : durabilityLossBlockTypes) {
int[] blockTypeIndexes = durabilityLossBlockType.getBlockTypeIndexes();
if (blockTypeIndexes != null) {
for (int blockTypeIndex : blockTypeIndexes) {
if (blockTypeIndex == hitBlockTypeIndex) {
return durabilityLossBlockType.getDurabilityLossOnHit();
}
}
}
int[] blockSetIndexes = durabilityLossBlockType.getBlockSetIndexes();
if (blockSetIndexes != null) {
for (int blockSetIndex : blockSetIndexes) {
if (blockSetModule.blockInSet(blockSetIndex, hitBlockTypeId)) {
return durabilityLossBlockType.getDurabilityLossOnHit();
}
}
}
}
return item.getDurabilityLossOnHit();
}
}
}
}
public static boolean performBlockDamage(
@Nonnull Vector3i targetBlock,
@Nullable ItemStack itemStack,
@Nullable ItemTool tool,
float damageScale,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull CommandBuffer<EntityStore> commandBuffer,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
return performBlockDamage(null, null, targetBlock, itemStack, tool, null, false, damageScale, setBlockSettings, chunkReference, commandBuffer, chunkStore);
}
public static boolean performBlockDamage(
@Nullable LivingEntity entity,
@Nullable Ref<EntityStore> ref,
@Nonnull Vector3i targetBlockPos,
@Nullable ItemStack itemStack,
@Nullable ItemTool tool,
@Nullable String toolId,
boolean matchTool,
float damageScale,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
World world = entityStore.getExternalData().getWorld();
GameplayConfig gameplayConfig = world.getGameplayConfig();
WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType());
if (worldChunkComponent == null) {
return false;
} else {
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
BlockSection targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y);
int targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
boolean brokeBlock = false;
int environmentId = blockChunkComponent.getEnvironment(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
Environment environmentAsset = Environment.getAssetMap().getAsset(environmentId);
SpatialResource<Ref<EntityStore>, EntityStore> playerSpatialResource = entityStore.getResource(EntityModule.get().getPlayerSpatialResourceType());
if (environmentAsset != null && !environmentAsset.isBlockModificationAllowed()) {
targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
return false;
} else {
BlockType targetBlockType = worldChunkComponent.getBlockType(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
if (targetBlockType == null) {
return false;
} else {
BlockGathering blockGathering = targetBlockType.getGathering();
if (blockGathering == null) {
return false;
} else if (matchTool && !blockGathering.getToolData().containsKey(toolId)) {
return false;
} else {
Vector3d targetBlockCenterPos = new Vector3d();
targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos);
targetBlockCenterPos.add(targetBlockPos);
Vector3i originBlock = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
if (!targetBlockType.isUnknown()) {
int filler = targetSection.getFiller(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
int fillerX = FillerBlockUtil.unpackX(filler);
int fillerY = FillerBlockUtil.unpackY(filler);
int fillerZ = FillerBlockUtil.unpackZ(filler);
if (fillerX != 0 || fillerY != 0 || fillerZ != 0) {
originBlock = originBlock.clone().subtract(fillerX, fillerY, fillerZ);
String oldBlockTypeKey = targetBlockType.getId();
targetBlockType = world.getBlockType(originBlock.getX(), originBlock.getY(), originBlock.getZ());
if (targetBlockType == null) {
return false;
}
if (!oldBlockTypeKey.equals(targetBlockType.getId())) {
worldChunkComponent.breakBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
return true;
}
blockGathering = targetBlockType.getGathering();
if (blockGathering == null) {
return false;
}
}
}
Item heldItem = itemStack != null ? itemStack.getItem() : null;
if (tool == null && heldItem != null) {
tool = heldItem.getTool();
}
ItemToolSpec itemToolSpec = getSpecPowerDamageBlock(heldItem, targetBlockType, tool);
float specPower = itemToolSpec != null ? itemToolSpec.getPower() : 0.0F;
boolean canApplyItemStackPenalties = entity != null && entity.canApplyItemStackPenalties(ref, entityStore);
if (specPower != 0.0F && heldItem != null && heldItem.getTool() != null && itemStack.isBroken() && canApplyItemStackPenalties) {
BrokenPenalties brokenPenalties = gameplayConfig.getItemDurabilityConfig().getBrokenPenalties();
specPower *= 1.0F - (float)brokenPenalties.getTool(0.0);
}
int dropQuantity = 1;
String itemId;
String dropListId;
float damage;
if (specPower != 0.0F) {
BlockBreakingDropType breaking = blockGathering.getBreaking();
damage = specPower;
dropQuantity = breaking.getQuantity();
itemId = breaking.getItemId();
dropListId = breaking.getDropListId();
} else {
if (!blockGathering.isSoft()) {
if (heldItem != null && heldItem.getWeapon() == null) {
if (ref != null && entity != null) {
GatheringEffectsConfig unbreakableBlockConfig = gameplayConfig.getGatheringConfig().getUnbreakableBlockConfig();
if ((setBlockSettings & 4) == 0) {
String particleSystemId = unbreakableBlockConfig.getParticleSystemId();
if (particleSystemId != null) {
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results);
ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore);
}
}
if ((setBlockSettings & 1024) == 0) {
int soundEventIndex = unbreakableBlockConfig.getSoundEventIndex();
if (soundEventIndex != 0) {
SoundUtil.playSoundEvent3d(ref, soundEventIndex, targetBlockCenterPos, entityStore);
}
if (heldItem.getTool() != null) {
int hitSoundEventLayerIndex = heldItem.getTool().getIncorrectMaterialSoundLayerIndex();
if (hitSoundEventLayerIndex != 0) {
SoundUtil.playSoundEvent3d(ref, hitSoundEventLayerIndex, targetBlockCenterPos, entityStore);
}
}
}
}
return false;
}
return false;
}
SoftBlockDropType soft = blockGathering.getSoft();
if (!soft.isWeaponBreakable() && heldItem != null && heldItem.getWeapon() != null) {
return false;
}
damage = 1.0F;
itemId = soft.getItemId();
dropListId = soft.getDropListId();
damageScale = 1.0F;
}
damage *= damageScale;
ChunkColumn chunkColumnComponent = chunkStore.getComponent(chunkReference, ChunkColumn.getComponentType());
Ref<ChunkStore> chunkSectionRef = chunkColumnComponent != null
? chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(targetBlockPos.y))
: null;
if (targetBlockType.getGathering().shouldUseDefaultDropWhenPlaced()) {
BlockPhysics decoBlocks = chunkSectionRef != null ? chunkStore.getComponent(chunkSectionRef, BlockPhysics.getComponentType()) : null;
boolean isDeco = decoBlocks != null && decoBlocks.isDeco(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
if (isDeco) {
itemId = null;
dropListId = null;
}
}
TimeResource timeResource = entityStore.getResource(TimeResource.getResourceType());
BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, BlockHealthModule.get().getBlockHealthChunkComponentType());
assert blockHealthComponent != null;
float current = blockHealthComponent.getBlockHealth(originBlock);
DamageBlockEvent event = new DamageBlockEvent(itemStack, originBlock, targetBlockType, current, damage);
if (ref != null) {
entityStore.invoke(ref, event);
} else {
entityStore.invoke(event);
}
if (event.isCancelled()) {
targetSection.invalidateBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
return false;
} else {
damage = event.getDamage();
targetBlockType = event.getBlockType();
targetBlockPos = event.getTargetBlock();
targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y);
targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z);
targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos);
targetBlockCenterPos.add(targetBlockPos);
BlockHealth blockDamage = blockHealthComponent.damageBlock(timeResource.getNow(), world, targetBlockPos, damage);
if (blockHealthComponent.isBlockFragile(targetBlockPos) || blockDamage.isDestroyed()) {
BlockGathering.BlockToolData requiredTool = blockGathering.getToolData().get(toolId);
boolean toolsMatch = requiredTool != null;
if (!toolsMatch) {
performBlockBreak(
world,
targetBlockPos,
targetBlockType,
itemStack,
dropQuantity,
itemId,
dropListId,
setBlockSettings,
ref,
chunkReference,
entityStore,
chunkStore
);
brokeBlock = true;
} else {
String toolStateId = requiredTool.getStateId();
BlockType newBlockType = toolStateId != null ? targetBlockType.getBlockForState(toolStateId) : null;
boolean shouldChangeState = newBlockType != null && !targetBlockType.getId().equals(newBlockType.getId());
if (shouldChangeState) {
blockDamage.setHealth(1.0F);
worldChunkComponent.setBlock(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z, newBlockType);
if ((setBlockSettings & 1024) == 0) {
BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex());
if (soundSet != null) {
int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0);
if (soundEventIndexx != 0) {
SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore);
}
}
}
if ((setBlockSettings & 2048) == 0) {
List<ItemStack> itemStacks = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId());
Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5);
Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO);
entityStore.addEntities(itemEntityHolders, AddReason.SPAWN);
}
} else {
performBlockBreak(
world,
targetBlockPos,
targetBlockType,
itemStack,
dropQuantity,
itemId,
dropListId,
setBlockSettings | 2048,
ref,
chunkReference,
entityStore,
chunkStore
);
brokeBlock = true;
if ((setBlockSettings & 2048) == 0) {
List<ItemStack> toolDrops = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId());
if (!toolDrops.isEmpty()) {
Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5);
Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(
entityStore, toolDrops, dropPosition, Vector3f.ZERO
);
entityStore.addEntities(itemEntityHolders, AddReason.SPAWN);
}
}
}
}
} else if ((setBlockSettings & 1024) == 0) {
BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(targetBlockType.getBlockSoundSetIndex());
if (soundSet != null) {
int soundEventIndexx = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Hit, 0);
if (soundEventIndexx != 0) {
SoundUtil.playSoundEvent3d(soundEventIndexx, SoundCategory.SFX, targetBlockCenterPos, entityStore);
}
}
}
if (ref != null && entity != null) {
if ((setBlockSettings & 1024) == 0 && !targetBlockCenterPos.equals(Vector3d.MAX)) {
int hitSoundEventLayerIndex = 0;
if (itemToolSpec != null) {
hitSoundEventLayerIndex = itemToolSpec.getHitSoundLayerIndex();
}
if (hitSoundEventLayerIndex == 0 && heldItem != null && heldItem.getTool() != null) {
hitSoundEventLayerIndex = heldItem.getTool().getHitSoundLayerIndex();
}
if (hitSoundEventLayerIndex != 0) {
SoundUtil.playSoundEvent3d(
ref,
hitSoundEventLayerIndex,
targetBlockCenterPos.getX(),
targetBlockCenterPos.getY(),
targetBlockCenterPos.getZ(),
entityStore
);
}
}
if (itemToolSpec != null && itemToolSpec.isIncorrect()) {
GatheringEffectsConfig incorrectToolConfig = gameplayConfig.getGatheringConfig().getIncorrectToolConfig();
if (incorrectToolConfig != null) {
if ((setBlockSettings & 4) == 0) {
String particleSystemId = incorrectToolConfig.getParticleSystemId();
if (particleSystemId != null) {
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results);
ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore);
}
}
if ((setBlockSettings & 1024) == 0) {
int soundEventIndexx = incorrectToolConfig.getSoundEventIndex();
if (soundEventIndexx != 0) {
SoundUtil.playSoundEvent3d(ref, soundEventIndexx, targetBlockCenterPos, entityStore);
}
}
}
}
}
if (entity != null
&& ref != null
&& entity.canDecreaseItemStackDurability(ref, entityStore)
&& itemStack != null
&& !itemStack.isUnbreakable()) {
byte activeHotbarSlot = entity.getInventory().getActiveHotbarSlot();
if (activeHotbarSlot != -1) {
double durability = calculateDurabilityUse(heldItem, targetBlockType);
ItemContainer hotbar = entity.getInventory().getHotbar();
entity.updateItemStackDurability(ref, itemStack, hotbar, activeHotbarSlot, -durability, entityStore);
}
}
return brokeBlock;
}
}
}
}
}
}
public static void performBlockBreak(
@Nullable Ref<EntityStore> ref,
@Nullable ItemStack heldItemStack,
@Nonnull Vector3i targetBlock,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
performBlockBreak(ref, heldItemStack, targetBlock, 0, chunkReference, entityStore, chunkStore);
}
public static void performBlockBreak(
@Nullable Ref<EntityStore> ref,
@Nullable ItemStack heldItemStack,
@Nonnull Vector3i targetBlock,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
World world = chunkStore.getExternalData().getWorld();
int targetBlockX = targetBlock.getX();
int targetBlockY = targetBlock.getY();
int targetBlockZ = targetBlock.getZ();
WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType());
assert worldChunkComponent != null;
int targetBlockTypeIndex = worldChunkComponent.getBlock(targetBlockX, targetBlockY, targetBlockZ);
BlockType targetBlockTypeAsset = BlockType.getAssetMap().getAsset(targetBlockTypeIndex);
if (targetBlockTypeAsset != null) {
Vector3i affectedBlock = targetBlock;
if (!targetBlockTypeAsset.isUnknown()) {
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
BlockSection targetBlockSection = blockChunkComponent.getSectionAtBlockY(targetBlockY);
int filler = targetBlockSection.getFiller(targetBlockX, targetBlockY, targetBlockZ);
int fillerX = FillerBlockUtil.unpackX(filler);
int fillerY = FillerBlockUtil.unpackY(filler);
int fillerZ = FillerBlockUtil.unpackZ(filler);
if (fillerX != 0 || fillerY != 0 || fillerZ != 0) {
affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ);
BlockType originBlock = world.getBlockType(affectedBlock);
if (originBlock != null && !targetBlockTypeAsset.getId().equals(originBlock.getId())) {
world.breakBlock(targetBlockX, targetBlockY, targetBlockZ, setBlockSettings);
return;
}
}
}
performBlockBreak(
world, affectedBlock, targetBlockTypeAsset, heldItemStack, 0, null, null, setBlockSettings, ref, chunkReference, entityStore, chunkStore
);
}
}
public static void performBlockBreak(
@Nonnull World world,
@Nonnull Vector3i blockPosition,
@Nonnull BlockType targetBlockTypeKey,
@Nullable ItemStack heldItemStack,
int dropQuantity,
@Nullable String dropItemId,
@Nullable String dropListId,
int setBlockSettings,
@Nullable Ref<EntityStore> ref,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
Vector3i targetBlockPosition = blockPosition;
Ref<ChunkStore> targetChunkReference = chunkReference;
ComponentAccessor<ChunkStore> targetChunkStore = chunkStore;
if (ref != null) {
BreakBlockEvent event = new BreakBlockEvent(heldItemStack, blockPosition, targetBlockTypeKey);
entityStore.invoke(ref, event);
if (event.isCancelled()) {
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY());
blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
return;
}
targetBlockPosition = event.getTargetBlock();
targetChunkStore = world.getChunkStore().getStore();
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPosition.x, targetBlockPosition.z);
targetChunkReference = targetChunkStore.getExternalData().getChunkReference(chunkIndex);
if (targetChunkReference == null || !targetChunkReference.isValid()) {
return;
}
}
if (!targetBlockPosition.equals(blockPosition) || !world.equals(world)) {
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY());
blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
int x = blockPosition.getX();
int y = blockPosition.getY();
int z = blockPosition.getZ();
BlockChunk blockChunkComponent = chunkStore.getComponent(targetChunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y);
int filler = blockSection.getFiller(x, y, z);
int blockTypeIndex = blockSection.get(x, y, z);
BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeIndex);
boolean isNaturalBlockBreak = BlockInteractionUtils.isNaturalAction(ref, entityStore);
setBlockSettings |= 256;
if (!isNaturalBlockBreak) {
setBlockSettings |= 2048;
}
naturallyRemoveBlock(
targetBlockPosition,
blockTypeAsset,
filler,
dropQuantity,
dropItemId,
dropListId,
setBlockSettings,
targetChunkReference,
entityStore,
targetChunkStore
);
}
@Deprecated
public static void naturallyRemoveBlockByPhysics(
@Nonnull Vector3i blockPosition,
@Nonnull BlockType blockType,
int filler,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
int quantity = 1;
String itemId = null;
String dropListId = null;
BlockGathering blockGathering = blockType.getGathering();
if (blockGathering != null) {
PhysicsDropType physics = blockGathering.getPhysics();
BlockBreakingDropType breaking = blockGathering.getBreaking();
SoftBlockDropType soft = blockGathering.getSoft();
HarvestingDropType harvest = blockGathering.getHarvest();
if (physics != null) {
itemId = physics.getItemId();
dropListId = physics.getDropListId();
} else if (breaking != null) {
quantity = breaking.getQuantity();
itemId = breaking.getItemId();
dropListId = breaking.getDropListId();
} else if (soft != null) {
itemId = soft.getItemId();
dropListId = soft.getDropListId();
} else if (harvest != null) {
itemId = harvest.getItemId();
dropListId = harvest.getDropListId();
}
}
setBlockSettings |= 32;
naturallyRemoveBlock(blockPosition, blockType, filler, quantity, itemId, dropListId, setBlockSettings, chunkReference, entityStore, chunkStore);
}
public static void naturallyRemoveBlock(
@Nonnull Vector3i blockPosition,
@Nullable BlockType blockType,
int filler,
int quantity,
String itemId,
String dropListId,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
if (blockType != null) {
WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType());
assert worldChunkComponent != null;
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
Vector3i affectedBlock = blockPosition;
if (!blockType.isUnknown()) {
int fillerX = FillerBlockUtil.unpackX(filler);
int fillerY = FillerBlockUtil.unpackY(filler);
int fillerZ = FillerBlockUtil.unpackZ(filler);
if (fillerX != 0 || fillerY != 0 || fillerZ != 0) {
affectedBlock = blockPosition.clone().subtract(fillerX, fillerY, fillerZ);
String oldBlockTypeKey = blockType.getId();
blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ());
if (blockType == null) {
throw new IllegalStateException("Null block type fetched for " + affectedBlock + " during block break");
}
if (!oldBlockTypeKey.equals(blockType.getId())) {
worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings);
return;
}
}
}
if ((setBlockSettings & 1024) == 0) {
BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(blockType.getBlockSoundSetIndex());
if (soundSet != null) {
int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0);
if (soundEventIndex != 0) {
BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.getY());
int rotationIndex = section.getRotationIndex(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
Vector3d centerPosition = new Vector3d();
blockType.getBlockCenter(rotationIndex, centerPosition);
centerPosition.add(blockPosition);
SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore);
}
}
}
removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore);
if ((setBlockSettings & 2048) == 0 && quantity > 0) {
Vector3d dropPosition = blockPosition.toVector3d().add(0.5, 0.0, 0.5);
List<ItemStack> itemStacks = getDrops(blockType, quantity, itemId, dropListId);
Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO);
entityStore.addEntities(itemEntityHolders, AddReason.SPAWN);
}
}
}
public static boolean shouldPickupByInteraction(@Nullable BlockType blockType) {
return blockType != null && blockType.getGathering() != null && blockType.getGathering().isHarvestable();
}
public static void performPickupByInteraction(
@Nonnull Ref<EntityStore> ref,
@Nonnull Vector3i targetBlock,
@Nonnull BlockType blockType,
int filler,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<EntityStore> entityStore,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
// Fire GatherBlockEvent before proceeding - allows cancellation
GatherBlockEvent gatherEvent = new GatherBlockEvent(targetBlock, blockType);
entityStore.invoke(ref, gatherEvent);
if (gatherEvent.isCancelled()) {
// Invalidate block to sync client state
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
if (blockChunkComponent != null) {
BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY());
section.invalidateBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ());
}
return;
}
WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType());
assert worldChunkComponent != null;
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
Vector3i affectedBlock = targetBlock;
if (!blockType.isUnknown()) {
int fillerX = FillerBlockUtil.unpackX(filler);
int fillerY = FillerBlockUtil.unpackY(filler);
int fillerZ = FillerBlockUtil.unpackZ(filler);
if (fillerX != 0 || fillerY != 0 || fillerZ != 0) {
affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ);
String oldBlockTypeKey = blockType.getId();
blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ());
if (blockType == null) {
return;
}
if (!oldBlockTypeKey.equals(blockType.getId())) {
worldChunkComponent.breakBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ());
return;
}
}
}
BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY());
Vector3d centerPosition = new Vector3d();
int rotationIndex = section.getRotationIndex(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ());
blockType.getBlockCenter(rotationIndex, centerPosition);
centerPosition.add(targetBlock);
int setBlockSettings = 0;
setBlockSettings |= 256;
if (!BlockInteractionUtils.isNaturalAction(ref, entityStore)) {
setBlockSettings |= 2048;
}
removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore);
HarvestingDropType harvest = blockType.getGathering().getHarvest();
String itemId = harvest.getItemId();
String dropListId = harvest.getDropListId();
for (ItemStack itemStack : getDrops(blockType, 1, itemId, dropListId)) {
ItemUtils.interactivelyPickupItem(ref, itemStack, centerPosition, entityStore);
}
if ((setBlockSettings & 1024) == 0) {
BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(blockType.getBlockSoundSetIndex());
if (soundSet != null) {
int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Harvest, 0);
if (soundEventIndex != 0) {
SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore);
}
}
}
}
protected static void removeBlock(
@Nonnull Vector3i blockPosition,
@Nonnull BlockType blockType,
int setBlockSettings,
@Nonnull Ref<ChunkStore> chunkReference,
@Nonnull ComponentAccessor<ChunkStore> chunkStore
) {
World world = chunkStore.getExternalData().getWorld();
ComponentType<ChunkStore, BlockHealthChunk> blockHealthComponentType = BlockHealthModule.get().getBlockHealthChunkComponentType();
BlockHealthChunk blockHealthComponent = chunkStore.getComponent(chunkReference, blockHealthComponentType);
assert blockHealthComponent != null;
blockHealthComponent.removeBlock(world, blockPosition);
WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType());
assert worldChunkComponent != null;
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
assert blockChunkComponent != null;
worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings);
if ((setBlockSettings & 256) != 0) {
BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.y);
int rotationIndex = section.getRotationIndex(blockPosition.x, blockPosition.y, blockPosition.z);
BlockBoundingBoxes hitBoxType = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex());
if (hitBoxType != null) {
FillerBlockUtil.forEachFillerBlock(
hitBoxType.get(rotationIndex),
(x, y, z) -> world.performBlockUpdate(blockPosition.getX() + x, blockPosition.getY() + y, blockPosition.getZ() + z, false)
);
}
}
ConnectedBlocksUtil.setConnectedBlockAndNotifyNeighbors(
BlockType.getAssetMap().getIndex("Empty"), RotationTuple.NONE, Vector3i.ZERO, blockPosition, worldChunkComponent, blockChunkComponent
);
}
@Nonnull
public static List<ItemStack> getDrops(@Nonnull BlockType blockType, int quantity, @Nullable String itemId, @Nullable String dropListId) {
if (dropListId == null && itemId == null) {
Item item = blockType.getItem();
return item == null ? ObjectLists.emptyList() : ObjectLists.singleton(new ItemStack(item.getId(), quantity));
} else {
List<ItemStack> randomItemDrops = new ObjectArrayList<>();
if (dropListId != null) {
ItemModule itemModule = ItemModule.get();
if (itemModule.isEnabled()) {
for (int i = 0; i < quantity; i++) {
List<ItemStack> randomItemsToDrop = itemModule.getRandomItemDrops(dropListId);
randomItemDrops.addAll(randomItemsToDrop);
}
}
}
if (itemId != null) {
randomItemDrops.add(new ItemStack(itemId, quantity));
}
return randomItemDrops;
}
}
}