mirror of
https://github.com/Minestom/Minestom.git
synced 2025-03-13 07:09:51 +01:00
Remove CustomBlock and fix some compiler errors.
This commit is contained in:
parent
37aa7c5aa2
commit
a95a89932e
@ -18,7 +18,6 @@ import net.minestom.server.gamedata.tags.TagManager;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.Material;
|
||||
@ -333,7 +332,7 @@ public final class MinecraftServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling {@link CustomBlock} and {@link BlockPlacementRule}.
|
||||
* Gets the manager handling {@link BlockPlacementRule}.
|
||||
*
|
||||
* @return the block manager
|
||||
*/
|
||||
|
@ -23,7 +23,6 @@ import net.minestom.server.event.handler.EventHandler;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.network.packet.server.play.*;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.permission.Permission;
|
||||
@ -567,10 +566,8 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
float drag;
|
||||
if (onGround) {
|
||||
final BlockPosition blockPosition = position.toBlockPosition();
|
||||
final CustomBlock customBlock = null;//finalChunk.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
if (customBlock != null) {
|
||||
// Custom drag
|
||||
drag = customBlock.getDrag(instance, blockPosition);
|
||||
if (false) {
|
||||
// TODO: Handle drag for CustomBlock
|
||||
} else {
|
||||
// Default ground drag
|
||||
drag = 0.5f;
|
||||
@ -621,17 +618,19 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
continue;
|
||||
}
|
||||
|
||||
final CustomBlock customBlock = null;//chunk.getCustomBlock(x, y, z);
|
||||
if (customBlock != null) {
|
||||
tmpPosition.setX(x);
|
||||
tmpPosition.setY(y);
|
||||
tmpPosition.setZ(z);
|
||||
// checks that we are actually in the block, and not just here because of a rounding error
|
||||
if (boundingBox.intersect(tmpPosition)) {
|
||||
// TODO: replace with check with custom block bounding box
|
||||
customBlock.handleContact(instance, tmpPosition, this);
|
||||
}
|
||||
}
|
||||
// TODO: Handle Block Contact with custom Block
|
||||
// old code: feel free to remove:
|
||||
// final CustomBlock customBlock = null;//chunk.getCustomBlock(x, y, z);
|
||||
// if (customBlock != null) {
|
||||
// tmpPosition.setX(x);
|
||||
// tmpPosition.setY(y);
|
||||
// tmpPosition.setZ(z);
|
||||
// // checks that we are actually in the block, and not just here because of a rounding error
|
||||
// if (boundingBox.intersect(tmpPosition)) {
|
||||
// // TODO: replace with check with custom block bounding box
|
||||
// customBlock.handleContact(instance, tmpPosition, this);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ import net.minestom.server.event.item.PickupExperienceEvent;
|
||||
import net.minestom.server.event.player.*;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.message.ChatMessageType;
|
||||
import net.minestom.server.message.ChatPosition;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
@ -141,8 +140,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Game state (https://wiki.vg/Protocol#Change_Game_State)
|
||||
private boolean enableRespawnScreen;
|
||||
|
||||
// CustomBlock break delay
|
||||
private CustomBlock targetCustomBlock;
|
||||
// TODO: CustomBlock break delay
|
||||
private BlockPosition targetBlockPosition;
|
||||
// The last break delay requested
|
||||
private long targetBreakDelay;
|
||||
@ -330,50 +328,52 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
super.update(time); // Super update (item pickup/fire management)
|
||||
|
||||
// Target block stage
|
||||
if (targetCustomBlock != null) {
|
||||
this.targetBlockBreakCount++;
|
||||
|
||||
final boolean processStage = targetBreakDelay < 0 || targetBlockBreakCount >= targetBreakDelay;
|
||||
|
||||
// Check if the player did finish his current break delay
|
||||
if (processStage) {
|
||||
|
||||
// Negative value should skip abs(value) stage
|
||||
final byte stageIncrease = (byte) (targetBreakDelay > 0 ? 1 : Math.abs(targetBreakDelay));
|
||||
|
||||
// Should increment the target block stage
|
||||
if (targetCustomBlock.enableMultiPlayerBreaking()) {
|
||||
// Let the custom block object manages the breaking
|
||||
final boolean canContinue = targetCustomBlock.processStage(instance, targetBlockPosition, this, stageIncrease);
|
||||
if (canContinue) {
|
||||
final Set<Player> breakers = targetCustomBlock.getBreakers(instance, targetBlockPosition);
|
||||
refreshBreakDelay(breakers);
|
||||
} else {
|
||||
resetTargetBlock();
|
||||
}
|
||||
} else {
|
||||
// Let the player object manages the breaking
|
||||
// The custom block doesn't support multi player breaking
|
||||
if (targetStage + stageIncrease >= CustomBlock.MAX_STAGE) {
|
||||
// Break the block
|
||||
instance.breakBlock(this, targetBlockPosition);
|
||||
resetTargetBlock();
|
||||
} else {
|
||||
// Send the new block break animation packet and refresh data
|
||||
|
||||
final Chunk chunk = instance.getChunkAt(targetBlockPosition);
|
||||
final int entityId = targetCustomBlock.getBreakEntityId(this);
|
||||
final BlockBreakAnimationPacket blockBreakAnimationPacket =
|
||||
new BlockBreakAnimationPacket(entityId, targetBlockPosition, targetStage);
|
||||
Check.notNull(chunk, "Tried to interact with an unloaded chunk.");
|
||||
chunk.sendPacketToViewers(blockBreakAnimationPacket);
|
||||
|
||||
refreshBreakDelay(targetBreakers);
|
||||
this.targetStage += stageIncrease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: CustomBlock break delay
|
||||
// Old code feel free to remove.
|
||||
// if (targetCustomBlock != null) {
|
||||
// this.targetBlockBreakCount++;
|
||||
//
|
||||
// final boolean processStage = targetBreakDelay < 0 || targetBlockBreakCount >= targetBreakDelay;
|
||||
//
|
||||
// // Check if the player did finish his current break delay
|
||||
// if (processStage) {
|
||||
//
|
||||
// // Negative value should skip abs(value) stage
|
||||
// final byte stageIncrease = (byte) (targetBreakDelay > 0 ? 1 : Math.abs(targetBreakDelay));
|
||||
//
|
||||
// // Should increment the target block stage
|
||||
// if (targetCustomBlock.enableMultiPlayerBreaking()) {
|
||||
// // Let the custom block object manages the breaking
|
||||
// final boolean canContinue = targetCustomBlock.processStage(instance, targetBlockPosition, this, stageIncrease);
|
||||
// if (canContinue) {
|
||||
// final Set<Player> breakers = targetCustomBlock.getBreakers(instance, targetBlockPosition);
|
||||
// refreshBreakDelay(breakers);
|
||||
// } else {
|
||||
// resetTargetBlock();
|
||||
// }
|
||||
// } else {
|
||||
// // Let the player object manages the breaking
|
||||
// // The custom block doesn't support multi player breaking
|
||||
// if (targetStage + stageIncrease >= CustomBlock.MAX_STAGE) {
|
||||
// // Break the block
|
||||
// instance.breakBlock(this, targetBlockPosition);
|
||||
// resetTargetBlock();
|
||||
// } else {
|
||||
// // Send the new block break animation packet and refresh data
|
||||
//
|
||||
// final Chunk chunk = instance.getChunkAt(targetBlockPosition);
|
||||
// final int entityId = targetCustomBlock.getBreakEntityId(this);
|
||||
// final BlockBreakAnimationPacket blockBreakAnimationPacket =
|
||||
// new BlockBreakAnimationPacket(entityId, targetBlockPosition, targetStage);
|
||||
// Check.notNull(chunk, "Tried to interact with an unloaded chunk.");
|
||||
// chunk.sendPacketToViewers(blockBreakAnimationPacket);
|
||||
//
|
||||
// refreshBreakDelay(targetBreakers);
|
||||
// this.targetStage += stageIncrease;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Experience orb pickup
|
||||
if (experiencePickupCooldown.isReady(time)) {
|
||||
@ -1830,16 +1830,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
this.belowNameTag = belowNameTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get the {@link CustomBlock} that the player is currently mining.
|
||||
*
|
||||
* @return the currently mined {@link CustomBlock} by the player, null if there is not
|
||||
*/
|
||||
@Nullable
|
||||
public CustomBlock getCustomBlockTarget() {
|
||||
return targetCustomBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player open inventory.
|
||||
*
|
||||
@ -2307,21 +2297,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
return itemUpdateStateEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the player digging a custom block, see {@link #resetTargetBlock()} to rewind.
|
||||
*
|
||||
* @param targetCustomBlock the custom block to dig
|
||||
* @param targetBlockPosition the custom block position
|
||||
* @param breakers the breakers of the block, can be null if {@code this} is the only breaker
|
||||
*/
|
||||
public void setTargetBlock(@NotNull CustomBlock targetCustomBlock, @NotNull BlockPosition targetBlockPosition,
|
||||
@Nullable Set<Player> breakers) {
|
||||
this.targetCustomBlock = targetCustomBlock;
|
||||
this.targetBlockPosition = targetBlockPosition;
|
||||
|
||||
refreshBreakDelay(breakers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the break delay for the next block break stage.
|
||||
*
|
||||
@ -2333,12 +2308,13 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Refresh the last tick update
|
||||
this.targetBlockBreakCount = 0;
|
||||
|
||||
// Get if multi player breaking is enabled
|
||||
final boolean multiPlayerBreaking = targetCustomBlock.enableMultiPlayerBreaking();
|
||||
// Get the stage from the custom block object if it is, otherwise use the local field
|
||||
final byte stage = multiPlayerBreaking ? targetCustomBlock.getBreakStage(instance, targetBlockPosition) : targetStage;
|
||||
// Retrieve the break delay for the current stage
|
||||
this.targetBreakDelay = targetCustomBlock.getBreakDelay(this, targetBlockPosition, stage, breakers);
|
||||
// TODO: Handle custom blocks break delay.
|
||||
// // Get if multi player breaking is enabled
|
||||
// final boolean multiPlayerBreaking = targetCustomBlock.enableMultiPlayerBreaking();
|
||||
// // Get the stage from the custom block object if it is, otherwise use the local field
|
||||
// final byte stage = multiPlayerBreaking ? targetCustomBlock.getBreakStage(instance, targetBlockPosition) : targetStage;
|
||||
// // Retrieve the break delay for the current stage
|
||||
// this.targetBreakDelay = targetCustomBlock.getBreakDelay(this, targetBlockPosition, stage, breakers);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2349,14 +2325,15 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
// Remove effect
|
||||
PlayerDiggingListener.removeEffect(this);
|
||||
|
||||
if (targetCustomBlock != null) {
|
||||
targetCustomBlock.stopDigging(instance, targetBlockPosition, this);
|
||||
this.targetCustomBlock = null;
|
||||
this.targetBlockPosition = null;
|
||||
this.targetBreakDelay = 0;
|
||||
this.targetBlockBreakCount = 0;
|
||||
this.targetStage = 0;
|
||||
}
|
||||
// TODO: Handle custom blocks break delay.
|
||||
// if (targetCustomBlock != null) {
|
||||
// targetCustomBlock.stopDigging(instance, targetBlockPosition, this);
|
||||
// this.targetCustomBlock = null;
|
||||
// this.targetBlockPosition = null;
|
||||
// this.targetBreakDelay = 0;
|
||||
// this.targetBlockBreakCount = 0;
|
||||
// this.targetStage = 0;
|
||||
// }
|
||||
}
|
||||
|
||||
public void refreshVehicleSteer(float sideways, float forward, boolean jump, boolean unmount) {
|
||||
|
@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.shorts.Short2ShortArrayMap;
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.ai.GoalSelector;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -48,8 +49,8 @@ public class EatBlockGoal extends GoalSelector {
|
||||
}
|
||||
|
||||
final BlockPosition blockPosition = entityCreature.getPosition().toBlockPosition();
|
||||
final short blockStateIdIn = instance.getBlockStateId(blockPosition.clone().subtract(0, 1, 0));
|
||||
final short blockStateIdBelow = instance.getBlockStateId(blockPosition.clone().subtract(0, 2, 0));
|
||||
final short blockStateIdIn = instance.getBlock(blockPosition.clone().subtract(0, 1, 0)).getStateId();
|
||||
final short blockStateIdBelow = instance.getBlock(blockPosition.clone().subtract(0, 2, 0)).getStateId();
|
||||
|
||||
return eatInMap.containsKey(blockStateIdIn) || eatBelowMap.containsKey(blockStateIdBelow);
|
||||
}
|
||||
@ -72,12 +73,12 @@ public class EatBlockGoal extends GoalSelector {
|
||||
final BlockPosition currentPosition = entityCreature.getPosition().toBlockPosition().clone().subtract(0, 1, 0);
|
||||
final BlockPosition belowPosition = currentPosition.clone().subtract(0, 1, 0);
|
||||
|
||||
final short blockStateIdIn = instance.getBlockStateId(currentPosition);
|
||||
final short blockStateIdBelow = instance.getBlockStateId(belowPosition);
|
||||
final short blockStateIdIn = instance.getBlock(currentPosition).getStateId();
|
||||
final short blockStateIdBelow = instance.getBlock(belowPosition).getStateId();
|
||||
if (eatInMap.containsKey(blockStateIdIn)) {
|
||||
instance.setBlockStateId(currentPosition, eatInMap.get(blockStateIdIn));
|
||||
instance.setBlock(currentPosition, Block.fromStateId(eatInMap.get(blockStateIdIn)));
|
||||
} else if (eatBelowMap.containsKey(blockStateIdBelow)) {
|
||||
instance.setBlockStateId(belowPosition, eatBelowMap.get(blockStateIdBelow));
|
||||
instance.setBlock(belowPosition, Block.fromStateId(eatBelowMap.get(blockStateIdBelow)));
|
||||
}
|
||||
// TODO: Call Entity Eat Animation
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import net.minestom.server.event.instance.InstanceTickEvent;
|
||||
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.network.packet.server.play.BlockActionPacket;
|
||||
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
|
||||
import net.minestom.server.storage.StorageLocation;
|
||||
@ -824,18 +823,6 @@ public abstract class Instance implements BlockModifier, Tickable, EventHandler,
|
||||
return chunkEntities.computeIfAbsent(index, i -> ConcurrentHashMap.newKeySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a block update at a given {@link BlockPosition}.
|
||||
* Does nothing if no {@link CustomBlock} is present at {@code position}.
|
||||
* <p>
|
||||
* Cancelled if the block changes between this call and the actual update.
|
||||
*
|
||||
* @param time in how long this update must be performed?
|
||||
* @param unit in what unit is the time expressed
|
||||
* @param position the location of the block to update
|
||||
*/
|
||||
public abstract void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position);
|
||||
|
||||
/**
|
||||
* Performs a single tick in the instance, including scheduled tasks from {@link #scheduleNextTick(Consumer)}.
|
||||
* <p>
|
||||
|
@ -9,7 +9,6 @@ import net.minestom.server.event.instance.InstanceChunkUnloadEvent;
|
||||
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||||
import net.minestom.server.instance.batch.ChunkGenerationBatch;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
import net.minestom.server.network.packet.server.play.BlockChangePacket;
|
||||
import net.minestom.server.network.packet.server.play.EffectPacket;
|
||||
@ -22,7 +21,6 @@ import net.minestom.server.utils.callback.OptionalCallback;
|
||||
import net.minestom.server.utils.chunk.ChunkCallback;
|
||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
@ -181,20 +179,6 @@ public class InstanceContainer extends Instance {
|
||||
return changedBlock.getBlockId() == block.getBlockId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link CustomBlock#onDestroy(Instance, BlockPosition, Data)} for {@code previousBlock}.
|
||||
* <p>
|
||||
* WARNING {@code chunk} needs to be synchronized.
|
||||
*
|
||||
* @param previousBlock the block which has been destroyed
|
||||
* @param previousBlockData the data of the destroyed block
|
||||
* @param blockPosition the block position
|
||||
*/
|
||||
private void callBlockDestroy(@NotNull CustomBlock previousBlock, @Nullable Data previousBlockData,
|
||||
@NotNull BlockPosition blockPosition) {
|
||||
previousBlock.onDestroy(this, blockPosition, previousBlockData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the {@link BlockPlacementRule} for the specified block state id.
|
||||
*
|
||||
@ -646,24 +630,6 @@ public class InstanceContainer extends Instance {
|
||||
chunk.sendPacketToViewers(blockChangePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position) {
|
||||
final CustomBlock toUpdate = getCustomBlock(position);
|
||||
if (toUpdate == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
||||
final CustomBlock currentBlock = getCustomBlock(position);
|
||||
if (currentBlock == null)
|
||||
return;
|
||||
if (currentBlock.getCustomBlockId() != toUpdate.getCustomBlockId()) { // block changed
|
||||
return;
|
||||
}
|
||||
currentBlock.scheduledUpdate(this, position, getBlockData(position));
|
||||
}).delay(time, unit).schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(long time) {
|
||||
// Unload all waiting chunks
|
||||
|
@ -6,7 +6,6 @@ import net.minestom.server.storage.StorageLocation;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.chunk.ChunkCallback;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -117,11 +116,6 @@ public class SharedInstance extends Instance {
|
||||
return instanceContainer.isInVoid(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleUpdate(int time, @NotNull TimeUnit unit, @NotNull BlockPosition position) {
|
||||
this.instanceContainer.scheduleUpdate(time, unit, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link InstanceContainer} from where this instance takes its chunks from.
|
||||
*
|
||||
|
@ -54,45 +54,6 @@ public class RelativeBlockBatch implements Batch<Runnable> {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
|
||||
|
||||
// Save the offsets if it is the first entry
|
||||
if (firstEntry) {
|
||||
this.firstEntry = false;
|
||||
|
||||
this.offsetX = x;
|
||||
this.offsetY = y;
|
||||
this.offsetZ = z;
|
||||
}
|
||||
|
||||
// Subtract offset
|
||||
x -= offsetX;
|
||||
y -= offsetY;
|
||||
z -= offsetZ;
|
||||
|
||||
// Verify that blocks are not too far from each other
|
||||
Check.argCondition(Math.abs(x) > Short.MAX_VALUE, "Relative x position may not be more than 16 bits long.");
|
||||
Check.argCondition(Math.abs(y) > Short.MAX_VALUE, "Relative y position may not be more than 16 bits long.");
|
||||
Check.argCondition(Math.abs(z) > Short.MAX_VALUE, "Relative z position may not be more than 16 bits long.");
|
||||
|
||||
long pos = x;
|
||||
pos = (pos << 16) | (short) y;
|
||||
pos = (pos << 16) | (short) z;
|
||||
|
||||
final int block = (blockStateId << 16) | customBlockId;
|
||||
synchronized (blockIdMap) {
|
||||
this.blockIdMap.put(pos, block);
|
||||
|
||||
// Save data if present
|
||||
if (data != null) {
|
||||
synchronized (blockDataMap) {
|
||||
this.blockDataMap.put(pos, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
synchronized (blockIdMap) {
|
||||
|
@ -59,6 +59,9 @@ public interface Block extends Keyed, TagReadable, BlockConstants {
|
||||
static @Nullable Block fromStateId(short stateId) {
|
||||
return BlockRegistry.fromStateId(stateId);
|
||||
}
|
||||
static @Nullable Block fromBlockId(int blockId) {
|
||||
return BlockRegistry.fromBlockId(blockId);
|
||||
}
|
||||
|
||||
static void register(@NotNull NamespaceID namespaceID, @NotNull Block block,
|
||||
@NotNull IntRange range,
|
||||
|
@ -5,37 +5,11 @@ import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BlockManager {
|
||||
|
||||
// custom block id -> custom block
|
||||
private final CustomBlock[] customBlocksInternalId = new CustomBlock[Short.MAX_VALUE];
|
||||
// custom block identifier -> custom block
|
||||
private final Map<String, CustomBlock> customBlocksId = new HashMap<>();
|
||||
|
||||
// block id -> block placement rule
|
||||
private final BlockPlacementRule[] placementRules = new BlockPlacementRule[Short.MAX_VALUE];
|
||||
|
||||
/**
|
||||
* Registers a {@link CustomBlock}.
|
||||
*
|
||||
* @param customBlock the custom block to register
|
||||
* @throws IllegalArgumentException if <code>customBlock</code> block id is not greater than 0
|
||||
* @throws IllegalStateException if the id of <code>customBlock</code> is already registered
|
||||
*/
|
||||
public synchronized void registerCustomBlock(@NotNull CustomBlock customBlock) {
|
||||
final short id = customBlock.getCustomBlockId();
|
||||
Check.argCondition(id <= 0, "Custom block ID must be greater than 0, got: " + id);
|
||||
Check.stateCondition(customBlocksInternalId[id] != null, "a CustomBlock with the id " + id + " already exists");
|
||||
|
||||
final String identifier = customBlock.getIdentifier();
|
||||
this.customBlocksInternalId[id] = customBlock;
|
||||
this.customBlocksId.put(identifier, customBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link BlockPlacementRule}.
|
||||
*
|
||||
@ -59,37 +33,4 @@ public class BlockManager {
|
||||
public BlockPlacementRule getBlockPlacementRule(@NotNull Block block) {
|
||||
return this.placementRules[block.getBlockId()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link CustomBlock} with the specific identifier {@link CustomBlock#getIdentifier()}.
|
||||
*
|
||||
* @param identifier the custom block identifier
|
||||
* @return the {@link CustomBlock} associated with the identifier, null if not any
|
||||
*/
|
||||
@Nullable
|
||||
public CustomBlock getCustomBlock(@NotNull String identifier) {
|
||||
return customBlocksId.get(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link CustomBlock} with the specific custom block id {@link CustomBlock#getCustomBlockId()}.
|
||||
*
|
||||
* @param id the custom block id
|
||||
* @return the {@link CustomBlock} associated with the id, null if not any
|
||||
*/
|
||||
@Nullable
|
||||
public CustomBlock getCustomBlock(short id) {
|
||||
return customBlocksInternalId[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the registered custom blocks.
|
||||
*
|
||||
* @return a {@link Collection} containing the registered custom blocks
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<CustomBlock> getCustomBlocks() {
|
||||
return customBlocksId.values();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.minestom.server.instance.block;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectSortedMap;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
@ -14,6 +16,7 @@ import java.util.stream.IntStream;
|
||||
class BlockRegistry {
|
||||
|
||||
private static final Map<NamespaceID, Block> namespaceMap = new HashMap<>();
|
||||
private static final Int2ObjectSortedMap<Block> blockSet = new Int2ObjectAVLTreeMap<>();
|
||||
private static final Short2ObjectSortedMap<Block.Supplier> stateSet = new Short2ObjectAVLTreeMap<>();
|
||||
|
||||
public static synchronized @Nullable Block fromNamespaceId(@NotNull NamespaceID namespaceID) {
|
||||
@ -25,9 +28,15 @@ class BlockRegistry {
|
||||
return supplier.get(stateId);
|
||||
}
|
||||
|
||||
public static synchronized @Nullable Block fromBlockId(int blockId) {
|
||||
return blockSet.get(blockId);
|
||||
}
|
||||
|
||||
public static synchronized void register(@NotNull NamespaceID namespaceID, @NotNull Block block,
|
||||
@NotNull IntRange range, @NotNull Block.Supplier blockSupplier) {
|
||||
namespaceMap.put(namespaceID, block);
|
||||
IntStream.range(range.getMinimum(), range.getMaximum()).forEach(value -> stateSet.put((short) value, blockSupplier));
|
||||
blockSet.put(block.getBlockId(), block);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,496 +0,0 @@
|
||||
package net.minestom.server.instance.block;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ByteMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.instance.BlockModifier;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.network.packet.server.play.BlockBreakAnimationPacket;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the handler of a custom block type which can be registered with {@link BlockManager#registerCustomBlock(CustomBlock)}.
|
||||
* <p>
|
||||
* There should be only one instance of this class for each custom block type,
|
||||
* every individual blocks will execute the callbacks present there. Each of which contains the
|
||||
* custom block position and the instance concerned.
|
||||
* <p>
|
||||
* Each block in a chunk contains 2 id, the block state id (only visual) and a custom block id corresponding to
|
||||
* {@link CustomBlock#getCustomBlockId()}. A custom block is responsible for the blocks tick, the custom break time feature,
|
||||
* and some useful callbacks.
|
||||
*/
|
||||
public abstract class CustomBlock {
|
||||
|
||||
public static final byte MAX_STAGE = 10;
|
||||
|
||||
/**
|
||||
* Instance -> break data
|
||||
* Used to store block break stage data when {@link #enableMultiPlayerBreaking()} is enabled
|
||||
*/
|
||||
private final Map<Instance, InstanceBreakData> instanceBreakDataMap = new HashMap<>();
|
||||
|
||||
public int getBreakEntityId(Player firstBreaker) {
|
||||
return -firstBreaker.getEntityId();
|
||||
}
|
||||
|
||||
private final short defaultBlockStateId;
|
||||
private final String identifier;
|
||||
|
||||
/**
|
||||
* @param defaultBlockStateId the default block state id
|
||||
* @param identifier the custom block identifier
|
||||
*/
|
||||
public CustomBlock(short defaultBlockStateId, @NotNull String identifier) {
|
||||
this.defaultBlockStateId = defaultBlockStateId;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public CustomBlock(@NotNull Block block, @NotNull String identifier) {
|
||||
this(block.getBlockId(), identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling delay depends on {@link #getUpdateOption()} which should be overridden.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param data the data associated with the block
|
||||
* @throws UnsupportedOperationException if {@link #getUpdateOption()}
|
||||
* is not null but the update method is not overridden
|
||||
*/
|
||||
public void update(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data) {
|
||||
throw new UnsupportedOperationException("Update method not overridden, check #getUpdateOption()");
|
||||
}
|
||||
|
||||
/**
|
||||
* The update option is used to define the delay between two
|
||||
* {@link #update(Instance, BlockPosition, Data)} execution.
|
||||
* <p>
|
||||
* If this is not null, {@link #update(Instance, BlockPosition, Data)}
|
||||
* should be overridden or errors with occurs.
|
||||
*
|
||||
* @return the update option of the block, null if not any
|
||||
*/
|
||||
@Nullable
|
||||
public UpdateOption getUpdateOption() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a custom block has been placed.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param data the data associated with the block
|
||||
*/
|
||||
public abstract void onPlace(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data);
|
||||
|
||||
/**
|
||||
* Called when a custom block has been destroyed or replaced.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param data the data associated with the block
|
||||
*/
|
||||
public abstract void onDestroy(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data);
|
||||
|
||||
/**
|
||||
* Handles interactions with this block. Can also block normal item use (containers should block when opening the
|
||||
* menu, this prevents the player from placing a block when opening it for instance).
|
||||
*
|
||||
* @param player the player interacting
|
||||
* @param hand the hand used to interact
|
||||
* @param blockPosition the position of this block
|
||||
* @param data the data at this position
|
||||
* @return true if this block blocks normal item use, false otherwise
|
||||
*/
|
||||
public abstract boolean onInteract(@NotNull Player player, @NotNull Player.Hand hand,
|
||||
@NotNull BlockPosition blockPosition, @Nullable Data data);
|
||||
|
||||
/**
|
||||
* This id can be serialized in chunk file, meaning no duplicate should exist
|
||||
* Changing this value halfway should mean potentially breaking the world
|
||||
*
|
||||
* @return the custom block id
|
||||
*/
|
||||
public abstract short getCustomBlockId();
|
||||
|
||||
/**
|
||||
* Called when the player requests the next stage break delay
|
||||
*
|
||||
* @param player the player who is trying to break the block
|
||||
* @param position the block position
|
||||
* @param stage the current break stage of the block (0-10)
|
||||
* @param breakers the list containing all the players currently digging this block
|
||||
* @return the time in tick to pass to the next state, 0 to instant break it.
|
||||
* negative value allow to skip stages (-2 will skip 2 stages per tick)
|
||||
* @see #enableCustomBreakDelay() to enable/disable it
|
||||
*/
|
||||
public int getBreakDelay(@NotNull Player player, @NotNull BlockPosition position, byte stage, Set<Player> breakers) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable the custom break delay from {@link #getBreakDelay(Player, BlockPosition, byte, Set)}
|
||||
* Disabling it would result in having vanilla time
|
||||
*
|
||||
* @return true to enable custom break delay
|
||||
*/
|
||||
public boolean enableCustomBreakDelay() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this block breaking time can be reduced by having multiple players
|
||||
* digging it.
|
||||
* <p>
|
||||
* WARNING: this should be constant, do not change this value halfway.
|
||||
*
|
||||
* @return true to enable the multi-player breaking feature
|
||||
*/
|
||||
public boolean enableMultiPlayerBreaking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this {@link CustomBlock} requires any tick update.
|
||||
*
|
||||
* @return true if {@link #getUpdateOption()} is not null and the value is positive
|
||||
*/
|
||||
public boolean hasUpdate() {
|
||||
final UpdateOption updateOption = getUpdateOption();
|
||||
if (updateOption == null)
|
||||
return false;
|
||||
|
||||
return updateOption.getValue() > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines custom behaviour for entities touching this block.
|
||||
*
|
||||
* @param instance the instance
|
||||
* @param position the position at which the block is
|
||||
* @param touching the entity currently touching the block
|
||||
*/
|
||||
public void handleContact(@NotNull Instance instance, @NotNull BlockPosition position, @NotNull Entity touching) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the default block state id when the custom block is set,
|
||||
* it is possible to change this value per block using
|
||||
* {@link BlockModifier#setSeparateBlocks(int, int, int, short, short)}
|
||||
* <p>
|
||||
* Meaning that you should not believe that your custom blocks id will always be this one.
|
||||
*
|
||||
* @return the default visual block id
|
||||
*/
|
||||
public short getDefaultBlockStateId() {
|
||||
return defaultBlockStateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom block identifier, used to retrieve the custom block object with
|
||||
* {@link BlockManager#getCustomBlock(String)} and to set custom block in the instance.
|
||||
*
|
||||
* @return the custom block identifier
|
||||
*/
|
||||
@NotNull
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises data for this block.
|
||||
*
|
||||
* @param blockPosition the position of the targeted block
|
||||
* @param data data given to 'setBlock', can be null
|
||||
* @return Data for this block. Can be null, 'data', or a new object
|
||||
*/
|
||||
@Nullable
|
||||
public Data createData(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this block from a neighbor. By default calls 'update' if directNeighbor is true.
|
||||
*
|
||||
* @param instance current instance
|
||||
* @param thisPosition this block's position
|
||||
* @param neighborPosition the neighboring block which triggered the update
|
||||
* @param directNeighbor is the neighbor directly connected to this block? (No diagonals)
|
||||
*/
|
||||
public void updateFromNeighbor(@NotNull Instance instance, @NotNull BlockPosition thisPosition,
|
||||
@NotNull BlockPosition neighborPosition, boolean directNeighbor) {
|
||||
if (directNeighbor && hasUpdate()) {
|
||||
update(instance, thisPosition, instance.getBlockData(thisPosition));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a scheduled update on this block happens. By default, calls 'update'.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param position the position of the block
|
||||
* @param blockData the data of the block
|
||||
*/
|
||||
public void scheduledUpdate(@NotNull Instance instance, @NotNull BlockPosition position, @Nullable Data blockData) {
|
||||
update(instance, position, blockData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drag of this block.
|
||||
* <p>
|
||||
* It has to be between 0 and 1.
|
||||
*
|
||||
* @return the drag of this block
|
||||
*/
|
||||
public float getDrag(@NotNull Instance instance, @NotNull BlockPosition blockPosition) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows custom block to write block entity data to a given NBT compound.
|
||||
* Used to send block entity data to the client over the network.
|
||||
* Can also be used to save block entity data on disk for compatible chunk savers.
|
||||
*
|
||||
* @param position position of the block
|
||||
* @param blockData equivalent to <pre>instance.getBlockData(position)</pre>
|
||||
* @param nbt the nbt to write in the {@link net.minestom.server.network.packet.server.play.ChunkDataPacket}
|
||||
*/
|
||||
public void writeBlockEntity(@NotNull BlockPosition position, @Nullable Data blockData, @NotNull NBTCompound nbt) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an explosion wants to destroy this block.
|
||||
*
|
||||
* @param instance the instance
|
||||
* @param lootTableArguments arguments used in the loot table loot generation
|
||||
* @return 'true' if the explosion should happen on this block, 'false' to cancel the destruction.
|
||||
* Returning true does NOT block the explosion rays, ie it does not change the block explosion resistance
|
||||
*/
|
||||
public boolean onExplode(@NotNull Instance instance, @NotNull BlockPosition position, Data lootTableArguments) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loot table associated to this block. Return null to use vanilla behavior.
|
||||
*
|
||||
* @param tableManager the loot table manager
|
||||
* @return the loot table associated to this block
|
||||
*/
|
||||
@Nullable
|
||||
public LootTable getLootTable(@NotNull LootTableManager tableManager) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// BLOCK BREAK METHODS
|
||||
|
||||
/**
|
||||
* Called when a player start digging this custom block,
|
||||
* process all necessary data if {@link #enableMultiPlayerBreaking()} is enabled.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param player the player who started digging
|
||||
*/
|
||||
public void startDigging(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @NotNull Player player) {
|
||||
// Stay null if multi player breaking is disabled
|
||||
Set<Player> breakers = null;
|
||||
|
||||
if (enableMultiPlayerBreaking()) {
|
||||
// Multi player breaking enabled, get the breakers and cache some values
|
||||
InstanceBreakData instanceBreakData = instanceBreakDataMap.computeIfAbsent(instance, i -> new InstanceBreakData());
|
||||
|
||||
Map<BlockPosition, Set<Player>> breakersMap = instanceBreakData.breakersMap;
|
||||
breakers = breakersMap.computeIfAbsent(blockPosition, pos -> new HashSet<>(1));
|
||||
breakers.add(player);
|
||||
|
||||
Object2ByteMap<BlockPosition> breakStageMap = instanceBreakData.breakStageMap;
|
||||
// Set the block stage to 0, use the previous one if any
|
||||
if (!breakStageMap.containsKey(blockPosition)) {
|
||||
breakStageMap.put(blockPosition, (byte) 0);
|
||||
}
|
||||
|
||||
Object2IntMap<BlockPosition> breakIdMap = instanceBreakData.breakIdMap;
|
||||
// Set the entity id used for the packet, otherwise use the previous one
|
||||
if (!breakIdMap.containsKey(blockPosition)) {
|
||||
breakIdMap.put(blockPosition, getBreakEntityId(player));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the player target block
|
||||
player.setTargetBlock(this, blockPosition, breakers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player stop digging a block,
|
||||
* does remove the block break animation if he was the only breaker.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param player the player who stopped digging
|
||||
*/
|
||||
public void stopDigging(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @NotNull Player player) {
|
||||
if (enableMultiPlayerBreaking()) {
|
||||
// Remove cache data
|
||||
if (instanceBreakDataMap.containsKey(instance)) {
|
||||
InstanceBreakData instanceBreakData = instanceBreakDataMap.get(instance);
|
||||
|
||||
Set<Player> breakers = instanceBreakData.breakersMap.get(blockPosition);
|
||||
if (breakers != null) {
|
||||
breakers.remove(player);
|
||||
if (breakers.isEmpty()) {
|
||||
// No remaining breakers
|
||||
|
||||
// Get the entity id assigned to the block break
|
||||
final int entityId = instanceBreakData.breakIdMap.getInt(blockPosition);
|
||||
|
||||
final Chunk chunk = instance.getChunkAt(blockPosition);
|
||||
Check.notNull(chunk, "Tried to interact with an unloaded chunk.");
|
||||
chunk.sendPacketToViewers(new BlockBreakAnimationPacket(entityId, blockPosition, (byte) -1));
|
||||
|
||||
// Clear cache
|
||||
removeDiggingInformation(instance, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// Stop the breaking animation for the specific player id
|
||||
final Chunk chunk = instance.getChunkAt(blockPosition);
|
||||
final int entityId = getBreakEntityId(player);
|
||||
Check.notNull(chunk, "Tried to interact with an unloaded chunk.");
|
||||
chunk.sendPacketToViewers(new BlockBreakAnimationPacket(entityId, blockPosition, (byte) -1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one stage on the block, break it if it excess {@link #MAX_STAGE},
|
||||
* only if {@link #enableMultiPlayerBreaking()} is enabled.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @param player the player who processed one stage on the block
|
||||
* @param stageIncrease the number of stage increase
|
||||
* @return true if the block can continue being digged
|
||||
* @throws IllegalStateException if {@link #enableMultiPlayerBreaking()} is disabled
|
||||
*/
|
||||
public synchronized boolean processStage(@NotNull Instance instance, @NotNull BlockPosition blockPosition,
|
||||
@NotNull Player player, byte stageIncrease) {
|
||||
Check.stateCondition(!enableMultiPlayerBreaking(),
|
||||
"CustomBlock#processState requires having the multi player breaking feature enabled");
|
||||
|
||||
if (instanceBreakDataMap.containsKey(instance)) {
|
||||
InstanceBreakData instanceBreakData = instanceBreakDataMap.get(instance);
|
||||
Object2ByteMap<BlockPosition> breakStageMap = instanceBreakData.breakStageMap;
|
||||
byte stage = breakStageMap.getByte(blockPosition);
|
||||
if (stage + stageIncrease >= MAX_STAGE) {
|
||||
instance.breakBlock(player, blockPosition);
|
||||
return false;
|
||||
} else {
|
||||
|
||||
// Get the entity id assigned to the block break
|
||||
final int entityId = instanceBreakData.breakIdMap.getInt(blockPosition);
|
||||
|
||||
// Send the block break animation
|
||||
final Chunk chunk = instance.getChunkAt(blockPosition);
|
||||
Check.notNull(chunk, "Tried to interact with an unloaded chunk.");
|
||||
chunk.sendPacketToViewers(new BlockBreakAnimationPacket(entityId, blockPosition, stage));
|
||||
|
||||
// Refresh the stage
|
||||
stage += stageIncrease;
|
||||
breakStageMap.put(blockPosition, stage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeDiggingInformation(@NotNull Instance instance, @NotNull BlockPosition blockPosition) {
|
||||
if (!enableMultiPlayerBreaking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instanceBreakDataMap.containsKey(instance)) {
|
||||
InstanceBreakData instanceBreakData = instanceBreakDataMap.get(instance);
|
||||
// Remove the block position from all maps
|
||||
instanceBreakData.clear(blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the breakers of a block, only if {@link #enableMultiPlayerBreaking()} is enabled.
|
||||
*
|
||||
* @param instance the instance of the block
|
||||
* @param blockPosition the position of the block
|
||||
* @return the {@link Set} of breakers of a block
|
||||
* @throws IllegalStateException if {@link #enableMultiPlayerBreaking()} is disabled
|
||||
*/
|
||||
@Nullable
|
||||
public Set<Player> getBreakers(@NotNull Instance instance, @NotNull BlockPosition blockPosition) {
|
||||
Check.stateCondition(!enableMultiPlayerBreaking(),
|
||||
"CustomBlock#getBreakers requires having the multi player breaking feature enabled");
|
||||
|
||||
if (instanceBreakDataMap.containsKey(instance)) {
|
||||
InstanceBreakData instanceBreakData = instanceBreakDataMap.get(instance);
|
||||
return instanceBreakData.breakersMap.get(blockPosition);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block break stage at a position,
|
||||
* only work if {@link #enableMultiPlayerBreaking()} is enabled.
|
||||
*
|
||||
* @param instance the instance of the custom block
|
||||
* @param blockPosition the position of the custom block
|
||||
* @return the break stage at the position. Can also be 0 when nonexistent
|
||||
*/
|
||||
public byte getBreakStage(@NotNull Instance instance, @NotNull BlockPosition blockPosition) {
|
||||
Check.stateCondition(!enableMultiPlayerBreaking(),
|
||||
"CustomBlock#getBreakStage requires having the multi player breaking feature enabled");
|
||||
|
||||
if (!instanceBreakDataMap.containsKey(instance))
|
||||
return 0;
|
||||
final InstanceBreakData instanceBreakData = instanceBreakDataMap.get(instance);
|
||||
return instanceBreakData.breakStageMap.getByte(blockPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used to store block break stage.
|
||||
* Only if multi player breaking is enabled.
|
||||
*/
|
||||
private static class InstanceBreakData {
|
||||
// Contains all the breakers of a block
|
||||
private final Map<BlockPosition, Set<Player>> breakersMap = new HashMap<>();
|
||||
// Contains the current break stage of a block
|
||||
private final Object2ByteMap<BlockPosition> breakStageMap = new Object2ByteOpenHashMap<>();
|
||||
// Contains the entity id used by the block break packet
|
||||
private final Object2IntMap<BlockPosition> breakIdMap = new Object2IntOpenHashMap<>();
|
||||
|
||||
private void clear(@NotNull BlockPosition blockPosition) {
|
||||
this.breakersMap.remove(blockPosition);
|
||||
this.breakStageMap.removeByte(blockPosition);
|
||||
this.breakIdMap.removeInt(blockPosition);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -89,9 +89,11 @@ public class StairsPlacementRule extends BlockPlacementRule {
|
||||
}
|
||||
Block state = instance.getBlock(blockPosition);
|
||||
try {
|
||||
Shape shape = Shape.valueOf(state.getProperty("shape").toUpperCase());
|
||||
Facing facing = Facing.valueOf(state.getProperty("facing").toUpperCase());
|
||||
return Pair.of(shape, facing);
|
||||
// TODO: Get properties from state
|
||||
// Shape shape = Shape.valueOf(state.getProperty("shape").toUpperCase());
|
||||
// Facing facing = Facing.valueOf(state.getProperty("facing").toUpperCase());
|
||||
// return Pair.of(shape, facing);
|
||||
return Pair.of(null, null);
|
||||
} catch (Exception ex) {
|
||||
return Pair.of(null, null);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import net.minestom.server.event.player.PlayerStartDiggingEvent;
|
||||
import net.minestom.server.event.player.PlayerSwapItemEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.StackingRule;
|
||||
@ -57,13 +56,12 @@ public class PlayerDiggingListener {
|
||||
|
||||
final boolean instantBreak = player.isCreative() ||
|
||||
player.isInstantBreak() ||
|
||||
block.breaksInstantaneously();
|
||||
block.getData().getHardness() == 0;
|
||||
|
||||
if (instantBreak) {
|
||||
// No need to check custom block
|
||||
breakBlock(instance, player, blockPosition, block, status);
|
||||
} else {
|
||||
final CustomBlock customBlock = instance.getCustomBlock(blockPosition);
|
||||
PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(player, block, blockPosition);
|
||||
player.callEvent(PlayerStartDiggingEvent.class, playerStartDiggingEvent);
|
||||
|
||||
@ -73,15 +71,8 @@ public class PlayerDiggingListener {
|
||||
// Unsuccessful digging
|
||||
sendAcknowledgePacket(player, blockPosition, block,
|
||||
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
|
||||
} else if (customBlock != null) {
|
||||
// Start digging the custom block
|
||||
if (customBlock.enableCustomBreakDelay()) {
|
||||
customBlock.startDigging(instance, blockPosition, player);
|
||||
addEffect(player);
|
||||
}
|
||||
|
||||
sendAcknowledgePacket(player, blockPosition, block,
|
||||
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, true);
|
||||
} else if (false) {
|
||||
// TODO: Handle Custom Block here
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,11 +88,9 @@ public class PlayerDiggingListener {
|
||||
} else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) {
|
||||
|
||||
final Block block = instance.getBlock(blockPosition);
|
||||
final CustomBlock customBlock = instance.getCustomBlock(blockPosition);
|
||||
if (customBlock != null && customBlock.enableCustomBreakDelay()) {
|
||||
// Is not supposed to happen, probably a bug
|
||||
sendAcknowledgePacket(player, blockPosition, block,
|
||||
ClientPlayerDiggingPacket.Status.FINISHED_DIGGING, false);
|
||||
|
||||
if (false) {
|
||||
// TODO: Handle custom block with block break delay here
|
||||
} else {
|
||||
// Vanilla block
|
||||
breakBlock(instance, player, blockPosition, block, status);
|
||||
@ -204,7 +193,7 @@ public class PlayerDiggingListener {
|
||||
/**
|
||||
* Adds the effect {@link PotionEffect#MINING_FATIGUE} to the player.
|
||||
* <p>
|
||||
* Used for {@link CustomBlock} break delay or when the {@link PlayerStartDiggingEvent} is cancelled
|
||||
* Used for CustomBlock break delay or when the {@link PlayerStartDiggingEvent} is cancelled
|
||||
* to remove the player break animation.
|
||||
*
|
||||
* @param player the player to add the effect to
|
||||
@ -228,7 +217,7 @@ public class PlayerDiggingListener {
|
||||
/**
|
||||
* Used to remove the affect from {@link #addEffect(Player)}.
|
||||
* <p>
|
||||
* Called when the player cancelled or finished digging the {@link CustomBlock}.
|
||||
* Called when the player cancelled or finished digging the CustomBlock.
|
||||
*
|
||||
* @param player the player to remove the effect to
|
||||
*/
|
||||
|
@ -8,7 +8,6 @@ import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.palette.PaletteStorage;
|
||||
import net.minestom.server.instance.palette.Section;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
@ -145,14 +144,15 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
|
||||
.setInt("y", blockPosition.getY())
|
||||
.setInt("z", blockPosition.getZ());
|
||||
|
||||
if (customBlockPaletteStorage != null) {
|
||||
final short customBlockId = customBlockPaletteStorage.getBlockAt(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
|
||||
if (customBlock != null) {
|
||||
final Data data = blocksData.get(index);
|
||||
customBlock.writeBlockEntity(blockPosition, data, nbt);
|
||||
}
|
||||
}
|
||||
// TODO: Handle custom blocks
|
||||
// if (customBlockPaletteStorage != null) {
|
||||
// final short customBlockId = customBlockPaletteStorage.getBlockAt(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
// final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
|
||||
// if (customBlock != null) {
|
||||
// final Data data = blocksData.get(index);
|
||||
// customBlock.writeBlockEntity(blockPosition, data, nbt);
|
||||
// }
|
||||
// }
|
||||
writer.writeNBT("", nbt);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class TagsPacket implements ServerPacket {
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
writeTags(writer, blockTags, name -> Registries.getBlock(name).ordinal());
|
||||
writeTags(writer, blockTags, name -> Block.fromNamespaceId(name).getBlockId());
|
||||
writeTags(writer, itemTags, name -> Registries.getMaterial(name).ordinal());
|
||||
writeTags(writer, fluidTags, name -> Registries.getFluid(name).ordinal());
|
||||
writeTags(writer, entityTags, name -> Registries.getEntityType(name).ordinal());
|
||||
@ -48,7 +48,7 @@ public class TagsPacket implements ServerPacket {
|
||||
|
||||
@Override
|
||||
public void read(@NotNull BinaryReader reader) {
|
||||
readTags(reader, blockTags, id -> NamespaceID.from("minecraft", Block.values()[id].getName()));
|
||||
readTags(reader, blockTags, id -> NamespaceID.from("minecraft", Block.fromBlockId(id).getName()));
|
||||
readTags(reader, itemTags, id -> NamespaceID.from("minecraft", Material.values()[id].getName()));
|
||||
readTags(reader, fluidTags, id -> NamespaceID.from(Fluid.values()[id].getNamespaceID()));
|
||||
readTags(reader, entityTags, id -> NamespaceID.from(EntityType.values()[id].getNamespaceID()));
|
||||
|
@ -1,32 +0,0 @@
|
||||
package net.minestom.server.utils.block;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
|
||||
public class CustomBlockUtils {
|
||||
|
||||
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
|
||||
|
||||
/**
|
||||
* Gets if a custom block id has an update method.
|
||||
*
|
||||
* @param customBlockId the custom block id
|
||||
* @return true if <code>customBlockId</code> has an update method
|
||||
*/
|
||||
public static boolean hasUpdate(short customBlockId) {
|
||||
final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
|
||||
return hasUpdate(customBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if a {@link CustomBlock} has an update method.
|
||||
*
|
||||
* @param customBlock the {@link CustomBlock}
|
||||
* @return true if <code>customBlock</code> has an update method
|
||||
*/
|
||||
public static boolean hasUpdate(CustomBlock customBlock) {
|
||||
return customBlock != null && customBlock.hasUpdate();
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package demo.blocks;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.LivingEntity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.damage.DamageType;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Custom block which burns entities that touch it
|
||||
*/
|
||||
public class BurningTorchBlock extends CustomBlock {
|
||||
public BurningTorchBlock() {
|
||||
super(Block.TORCH.getBlockId(), "torch_block");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleContact(@NotNull Instance instance, @NotNull BlockPosition position, @NotNull Entity touching) {
|
||||
System.out.println("touching " + touching);
|
||||
if (touching instanceof LivingEntity) {
|
||||
((LivingEntity) touching).damage(DamageType.GRAVITY, 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
System.out.println(blockPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInteract(@NotNull Player player, @NotNull Player.Hand hand, @NotNull BlockPosition blockPosition, Data data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCustomBlockId() {
|
||||
return 3;
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
package demo.blocks;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class CustomBlockSample extends CustomBlock {
|
||||
|
||||
private static final UpdateOption UPDATE_OPTION = new UpdateOption(3, TimeUnit.TICK);
|
||||
|
||||
public CustomBlockSample() {
|
||||
super(Block.GOLD_BLOCK, "custom_block");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
BlockPosition above = blockPosition.clone().add(0, 1, 0);
|
||||
CustomBlock blockAbove = instance.getCustomBlock(above);
|
||||
if (blockAbove == this) {
|
||||
instance.setBlock(above, Block.AIR);
|
||||
instance.setBlock(blockPosition, Block.AIR); // this should NOT create a stack overflow simply because we are trying to remove this same block
|
||||
}
|
||||
|
||||
System.out.println("position "+blockPosition);
|
||||
instance.setBlock(blockPosition, Block.DIAMOND_BLOCK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull Instance instance, @NotNull BlockPosition blockPosition, @Nullable Data data) {
|
||||
final short blockId = instance.getBlockStateId(blockPosition);
|
||||
//instance.refreshBlockStateId(blockPosition, (short) (blockId+1));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UpdateOption getUpdateOption() {
|
||||
return UPDATE_OPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromNeighbor(@NotNull Instance instance, @NotNull BlockPosition thisPosition, @NotNull BlockPosition neighborPosition, boolean directNeighbor) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInteract(@NotNull Player player, @NotNull Player.Hand hand, @NotNull BlockPosition blockPosition, Data data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBreakDelay(@NotNull Player player, @NotNull BlockPosition position, byte stage, Set<Player> breakers) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableCustomBreakDelay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableMultiPlayerBreaking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCustomBlockId() {
|
||||
return 2;
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package demo.blocks;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class UpdatableBlockDemo extends CustomBlock {
|
||||
|
||||
private static final UpdateOption UPDATE_OPTION = new UpdateOption(20, TimeUnit.TICK);
|
||||
|
||||
public UpdatableBlockDemo() {
|
||||
super(Block.DIRT, "updatable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
System.out.println("BLOCK UPDATE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlace(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(@NotNull Instance instance, @NotNull BlockPosition blockPosition, Data data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInteract(@NotNull Player player, @NotNull Player.Hand hand, @NotNull BlockPosition blockPosition, Data data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateOption getUpdateOption() {
|
||||
return UPDATE_OPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBreakDelay(@NotNull Player player, @NotNull BlockPosition position, byte stage, Set<Player> breakers) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableCustomBreakDelay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCustomBlockId() {
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user