Init
This commit is contained in:
721
content/core-concepts/events/event-reference/block-events.en.md
Normal file
721
content/core-concepts/events/event-reference/block-events.en.md
Normal file
@@ -0,0 +1,721 @@
|
||||
---
|
||||
title: Block Events
|
||||
type: docs
|
||||
weight: 3
|
||||
---
|
||||
|
||||
Events triggered by block interactions and changes. These events use the ECS (Entity Component System) pattern.
|
||||
|
||||
## Breaking & Placing Events
|
||||
|
||||
### BreakBlockEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when a block is being broken.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| targetBlock | `Vector3i` | Block position |
|
||||
| blockType | `BlockType` | Type of block |
|
||||
| itemInHand | `ItemStack` | Tool being used |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getTargetBlock()` - Returns block position
|
||||
- `getBlockType()` - Returns block type
|
||||
- `getItemInHand()` - Returns tool being used
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel breaking
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(BreakBlockEvent.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
ItemStack tool = event.getItemInHand();
|
||||
|
||||
// Protect certain blocks
|
||||
if (blockType.getId().equals("special_ore")) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log block break
|
||||
getLogger().at(Level.INFO).log("Block broken at " + position + ": " + blockType.getId());
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
### PlaceBlockEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when a block is placed.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| targetBlock | `Vector3i` | Position where block will be placed |
|
||||
| rotation | `RotationTuple` | Block rotation/orientation |
|
||||
| itemInHand | `ItemStack` | Block item being placed |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getTargetBlock()` - Returns target position
|
||||
- `setTargetBlock(Vector3i)` - Change placement position
|
||||
- `getRotation()` - Returns block rotation
|
||||
- `setRotation(RotationTuple)` - Change rotation
|
||||
- `getItemInHand()` - Returns block item
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel placement
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(PlaceBlockEvent.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
RotationTuple rotation = event.getRotation();
|
||||
ItemStack item = event.getItemInHand();
|
||||
|
||||
// Check build permissions
|
||||
if (isProtectedArea(position)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Force certain rotation
|
||||
event.setRotation(RotationTuple.of(Rotation.North, Rotation.None, Rotation.None));
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
### DamageBlockEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when damage is applied to a block during breaking (before the block is fully broken).
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| targetBlock | `Vector3i` | Block position |
|
||||
| blockType | `BlockType` | Type of block |
|
||||
| itemInHand | `ItemStack` | Tool being used (may be null) |
|
||||
| currentDamage | `float` | Damage already applied to block |
|
||||
| damage | `float` | Damage being applied this tick |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getTargetBlock()` - Returns block position
|
||||
- `setTargetBlock(Vector3i)` - Change target block
|
||||
- `getBlockType()` - Returns block type
|
||||
- `getItemInHand()` - Returns tool being used
|
||||
- `getCurrentDamage()` - Returns accumulated damage
|
||||
- `getDamage()` - Returns damage this tick
|
||||
- `setDamage(float)` - Modify damage amount
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel damage
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(DamageBlockEvent.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
// Make certain blocks indestructible
|
||||
if (blockType.getId().equals("bedrock")) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce damage to hardened blocks
|
||||
if (blockType.getId().startsWith("hardened_")) {
|
||||
event.setDamage(event.getDamage() * 0.5f);
|
||||
}
|
||||
|
||||
// Log mining progress
|
||||
float progress = event.getCurrentDamage() / 100f;
|
||||
getLogger().at(Level.INFO).log("Block " + blockType.getId() + " at " + progress + "% damage");
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
## Interaction Events
|
||||
|
||||
### UseBlockEvent
|
||||
|
||||
Base class for block use events. Has Pre and Post variants.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
#### UseBlockEvent.Pre
|
||||
|
||||
{{< badge "Cancellable" >}}
|
||||
|
||||
Fired before a block use action occurs (right-click on block).
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| interactionType | `InteractionType` | Type of interaction |
|
||||
| context | `InteractionContext` | Interaction context |
|
||||
| targetBlock | `Vector3i` | Block position |
|
||||
| blockType | `BlockType` | The block type |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getInteractionType()` - Returns interaction type
|
||||
- `getContext()` - Returns interaction context
|
||||
- `getTargetBlock()` - Returns block position
|
||||
- `getBlockType()` - Returns block type
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel use action
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(UseBlockEvent.Pre.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
InteractionContext context = event.getContext();
|
||||
|
||||
// Prevent opening locked containers
|
||||
if (isLocked(position)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
#### UseBlockEvent.Post
|
||||
|
||||
Fired after a block use action completes successfully.
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| interactionType | `InteractionType` | Type of interaction |
|
||||
| context | `InteractionContext` | Interaction context |
|
||||
| targetBlock | `Vector3i` | Block position |
|
||||
| blockType | `BlockType` | The block type |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getInteractionType()` - Returns interaction type
|
||||
- `getContext()` - Returns interaction context
|
||||
- `getTargetBlock()` - Returns block position
|
||||
- `getBlockType()` - Returns block type
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(UseBlockEvent.Post.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
// Track interactions for quests
|
||||
if (isQuestBlock(blockType)) {
|
||||
completeQuestObjective("interact_with_" + blockType.getId());
|
||||
}
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
## Item Events
|
||||
|
||||
### DropItemEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when an item is dropped. Has two variants: `Drop` and `PlayerRequest`.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
#### DropItemEvent.Drop
|
||||
|
||||
The actual drop event with item details.
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| itemStack | `ItemStack` | Item being dropped |
|
||||
| throwSpeed | `float` | Speed of the throw |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getItemStack()` - Returns the item stack
|
||||
- `setItemStack(ItemStack)` - Change the dropped item
|
||||
- `getThrowSpeed()` - Returns throw speed
|
||||
- `setThrowSpeed(float)` - Change throw speed
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel drop
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(DropItemEvent.Drop.class, event -> {
|
||||
ItemStack item = event.getItemStack();
|
||||
|
||||
// Prevent dropping certain items
|
||||
if (item.getId().equals("quest_item")) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Modify throw speed
|
||||
event.setThrowSpeed(event.getThrowSpeed() * 1.5f);
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
#### DropItemEvent.PlayerRequest
|
||||
|
||||
Fired when a player requests to drop an item (before the actual drop).
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| inventorySectionId | `int` | Inventory section ID |
|
||||
| slotId | `short` | Slot being dropped from |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getInventorySectionId()` - Returns inventory section
|
||||
- `getSlotId()` - Returns slot index
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel request
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(DropItemEvent.PlayerRequest.class, event -> {
|
||||
int sectionId = event.getInventorySectionId();
|
||||
short slot = event.getSlotId();
|
||||
|
||||
// Prevent dropping from certain slots
|
||||
if (sectionId == Inventory.HOTBAR && slot == 0) {
|
||||
event.setCancelled(true); // Can't drop first hotbar item
|
||||
}
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
### InteractivelyPickupItemEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when a player picks up an item interactively (manual pickup, not auto-pickup).
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| itemStack | `ItemStack` | The item being picked up |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getItemStack()` - Returns the item stack
|
||||
- `setItemStack(ItemStack)` - Change the item being picked up
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel pickup
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(InteractivelyPickupItemEvent.class, event -> {
|
||||
ItemStack item = event.getItemStack();
|
||||
|
||||
// Prevent picking up quest items that don't belong to player
|
||||
if (item.getId().startsWith("quest_") && !canPickupQuestItem(player, item)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Transform items when picking up
|
||||
if (item.getId().equals("raw_ore")) {
|
||||
event.setItemStack(item.withCount(item.getCount() * 2));
|
||||
}
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
## Crafting Events
|
||||
|
||||
### CraftRecipeEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when a crafting recipe is executed. Has Pre and Post variants.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| craftedRecipe | `CraftingRecipe` | The recipe being crafted |
|
||||
| quantity | `int` | Number of items being crafted |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getCraftedRecipe()` - Returns the crafting recipe
|
||||
- `getQuantity()` - Returns craft quantity
|
||||
- `isCancelled()` - Check if cancelled (Pre only)
|
||||
- `setCancelled(boolean)` - Cancel crafting (Pre only)
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
// Pre-craft check
|
||||
getEventRegistry().register(CraftRecipeEvent.Pre.class, event -> {
|
||||
CraftingRecipe recipe = event.getCraftedRecipe();
|
||||
int quantity = event.getQuantity();
|
||||
|
||||
// Block certain recipes
|
||||
if (isRestrictedRecipe(recipe)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Post-craft tracking
|
||||
getEventRegistry().register(CraftRecipeEvent.Post.class, event -> {
|
||||
CraftingRecipe recipe = event.getCraftedRecipe();
|
||||
int quantity = event.getQuantity();
|
||||
|
||||
// Track crafting statistics
|
||||
incrementCraftCount(recipe.getId(), quantity);
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
## Other Events
|
||||
|
||||
### SwitchActiveSlotEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when a player switches their active hotbar slot.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| inventorySectionId | `int` | The inventory section ID |
|
||||
| previousSlot | `int` | The slot before switching |
|
||||
| newSlot | `byte` | The target slot |
|
||||
| serverRequest | `boolean` | If server initiated the switch |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getInventorySectionId()` - Returns inventory section
|
||||
- `getPreviousSlot()` - Returns previous slot index
|
||||
- `getNewSlot()` - Returns new slot index
|
||||
- `setNewSlot(byte)` - Change the target slot
|
||||
- `isServerRequest()` - Check if server initiated
|
||||
- `isClientRequest()` - Check if client initiated
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel slot switch
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(SwitchActiveSlotEvent.class, event -> {
|
||||
int fromSlot = event.getPreviousSlot();
|
||||
int toSlot = event.getNewSlot();
|
||||
|
||||
// Prevent switching during cooldown
|
||||
if (isOnCooldown(player)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Track slot usage for analytics
|
||||
if (event.isClientRequest()) {
|
||||
trackSlotSwitch(player, fromSlot, toSlot);
|
||||
}
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
### DiscoverZoneEvent
|
||||
|
||||
{{< badge "ECS" >}}
|
||||
|
||||
Fired when a player discovers a new zone. Has a `Display` variant that is cancellable.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| discoveryInfo | `ZoneDiscoveryInfo` | Information about the discovered zone |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getDiscoveryInfo()` - Returns zone discovery info
|
||||
- `isCancelled()` - Check if cancelled (Display only)
|
||||
- `setCancelled(boolean)` - Cancel display (Display only)
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
// Cancel zone discovery notification display
|
||||
getEventRegistry().register(DiscoverZoneEvent.Display.class, event -> {
|
||||
WorldMapTracker.ZoneDiscoveryInfo info = event.getDiscoveryInfo();
|
||||
|
||||
// Hide certain zones from discovery UI
|
||||
if (isHiddenZone(info)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log discovery for achievements
|
||||
recordZoneDiscovery(player, info);
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
### ChangeGameModeEvent
|
||||
|
||||
{{< badge "Cancellable" >}} {{< badge "ECS" >}}
|
||||
|
||||
Fired when an entity's game mode changes.
|
||||
|
||||
**Package:** `com.hypixel.hytale.server.core.event.events.ecs`
|
||||
|
||||
{{< tabs items="Fields,Methods,Example" >}}
|
||||
{{< tab >}}
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| gameMode | `GameMode` | The new game mode |
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
- `getGameMode()` - Returns the new game mode
|
||||
- `setGameMode(GameMode)` - Change the target game mode
|
||||
- `isCancelled()` - Check if cancelled
|
||||
- `setCancelled(boolean)` - Cancel mode change
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
getEventRegistry().register(ChangeGameModeEvent.class, event -> {
|
||||
GameMode newMode = event.getGameMode();
|
||||
|
||||
// Prevent creative mode in certain worlds
|
||||
if (newMode == GameMode.CREATIVE && isRestrictedWorld(player.getWorld())) {
|
||||
event.setCancelled(true);
|
||||
player.sendMessage(Message.raw("Creative mode not allowed here!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Force adventure mode override
|
||||
if (shouldForceAdventure(player)) {
|
||||
event.setGameMode(GameMode.ADVENTURE);
|
||||
}
|
||||
|
||||
getLogger().at(Level.INFO).log(player.getDisplayName() + " changed to " + newMode);
|
||||
});
|
||||
```
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
---
|
||||
|
||||
## Practical Examples
|
||||
|
||||
### Block Logger Plugin
|
||||
|
||||
```java
|
||||
public class BlockLoggerPlugin extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
getEventRegistry().register(PlaceBlockEvent.class, event -> {
|
||||
logAction("PLACE", event.getTargetBlock(), event.getItemInHand());
|
||||
});
|
||||
|
||||
getEventRegistry().register(BreakBlockEvent.class, event -> {
|
||||
logAction("BREAK", event.getTargetBlock(), event.getBlockType());
|
||||
});
|
||||
}
|
||||
|
||||
private void logAction(String action, Vector3i pos, Object data) {
|
||||
getLogger().at(Level.INFO).log(String.format("%s: %d,%d,%d - %s",
|
||||
action, pos.x(), pos.y(), pos.z(), data));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Region Protection System
|
||||
|
||||
```java
|
||||
public class RegionPlugin extends JavaPlugin {
|
||||
|
||||
private final Set<Region> regions = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// Protect against breaking
|
||||
getEventRegistry().register(EventPriority.EARLY, BreakBlockEvent.class, event -> {
|
||||
if (isInProtectedRegion(event.getTargetBlock())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Protect against placing
|
||||
getEventRegistry().register(EventPriority.EARLY, PlaceBlockEvent.class, event -> {
|
||||
if (isInProtectedRegion(event.getTargetBlock())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Protect against use
|
||||
getEventRegistry().register(EventPriority.EARLY, UseBlockEvent.Pre.class, event -> {
|
||||
if (isInProtectedRegion(event.getTargetBlock())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isInProtectedRegion(Vector3i pos) {
|
||||
return regions.stream().anyMatch(r -> r.contains(pos));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Ore System
|
||||
|
||||
```java
|
||||
public class CustomOrePlugin extends JavaPlugin {
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
getEventRegistry().register(BreakBlockEvent.class, event -> {
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
if (isCustomOre(blockType)) {
|
||||
// Apply fortune multiplier
|
||||
ItemStack tool = event.getItemInHand();
|
||||
int fortuneLevel = getFortuneLevel(tool);
|
||||
int dropMultiplier = 1 + random.nextInt(fortuneLevel + 1);
|
||||
|
||||
// Schedule custom drops
|
||||
Vector3i pos = event.getTargetBlock();
|
||||
scheduleOreDrops(pos, blockType, dropMultiplier);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isCustomOre(BlockType blockType) {
|
||||
return blockType.getId().startsWith("custom_ore_");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Anti-Grief System
|
||||
|
||||
```java
|
||||
public class AntiGriefPlugin extends JavaPlugin {
|
||||
|
||||
private final Map<Vector3i, BlockAction> recentActions = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// Track block breaks for rollback
|
||||
getEventRegistry().register(BreakBlockEvent.class, event -> {
|
||||
Vector3i pos = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
recentActions.put(pos, new BlockAction(
|
||||
ActionType.BREAK,
|
||||
blockType,
|
||||
System.currentTimeMillis()
|
||||
));
|
||||
});
|
||||
|
||||
// Track block places for rollback
|
||||
getEventRegistry().register(PlaceBlockEvent.class, event -> {
|
||||
Vector3i pos = event.getTargetBlock();
|
||||
|
||||
recentActions.put(pos, new BlockAction(
|
||||
ActionType.PLACE,
|
||||
null,
|
||||
System.currentTimeMillis()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
public void rollback(World world, int seconds) {
|
||||
long cutoff = System.currentTimeMillis() - (seconds * 1000L);
|
||||
|
||||
recentActions.entrySet().stream()
|
||||
.filter(e -> e.getValue().timestamp() >= cutoff)
|
||||
.forEach(e -> {
|
||||
Vector3i pos = e.getKey();
|
||||
BlockAction action = e.getValue();
|
||||
|
||||
if (action.type() == ActionType.PLACE) {
|
||||
// Remove placed blocks
|
||||
world.breakBlock(pos.x(), pos.y(), pos.z());
|
||||
} else if (action.blockType() != null) {
|
||||
// Restore broken blocks
|
||||
world.setBlock(pos.x(), pos.y(), pos.z(), action.blockType());
|
||||
}
|
||||
});
|
||||
|
||||
recentActions.clear();
|
||||
}
|
||||
|
||||
record BlockAction(ActionType type, BlockType blockType, long timestamp) {}
|
||||
enum ActionType { BREAK, PLACE }
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**Block Event Guidelines:**
|
||||
- Use `EventPriority.EARLY` for protection systems
|
||||
- ECS events provide detailed control over block operations
|
||||
- Always validate positions before modifying blocks
|
||||
- Consider chunk load state when working with distant blocks
|
||||
- Clean up tracking data when no longer needed
|
||||
{{< /callout >}}
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
**Performance Note:** Block events can fire very frequently. Avoid expensive operations in handlers and cache results where possible.
|
||||
{{< /callout >}}
|
||||
Reference in New Issue
Block a user