Remove CustomBlock and fix some compiler errors.

This commit is contained in:
Articdive 2021-05-24 22:51:34 +02:00
parent 37aa7c5aa2
commit a95a89932e
No known key found for this signature in database
GPG Key ID: B069585F0F7D90DE
20 changed files with 122 additions and 1016 deletions

View File

@ -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
*/

View File

@ -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);
// }
// }
}
}
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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>

View File

@ -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

View File

@ -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.
*

View File

@ -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) {

View File

@ -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,

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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
*/

View File

@ -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);
}
}

View File

@ -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()));

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}