Init
This commit is contained in:
329
content/world/blocks.en.md
Normal file
329
content/world/blocks.en.md
Normal file
@@ -0,0 +1,329 @@
|
||||
---
|
||||
title: Blocks
|
||||
type: docs
|
||||
weight: 3
|
||||
---
|
||||
|
||||
Block manipulation is a core part of world modification in Hytale plugins. The `World` class provides block access through the `IChunkAccessorSync` interface, while `WorldChunk` implements the full `BlockAccessor` interface for rotation-aware placement.
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
**Important:** `World` does NOT implement `BlockAccessor` directly. For `placeBlock()` with rotation, you must get the chunk first using `world.getChunk()`.
|
||||
{{< /callout >}}
|
||||
|
||||
## Getting Blocks
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.universe.Universe;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
|
||||
World world = Universe.get().getWorld("default");
|
||||
|
||||
// Get block ID (int index) at position
|
||||
int blockId = world.getBlock(x, y, z);
|
||||
int blockId = world.getBlock(new Vector3i(x, y, z));
|
||||
|
||||
// Get BlockType at position
|
||||
BlockType blockType = world.getBlockType(x, y, z);
|
||||
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));
|
||||
|
||||
// Check if block is empty
|
||||
if (blockType == BlockType.EMPTY) {
|
||||
// Air or empty space
|
||||
}
|
||||
```
|
||||
|
||||
## Setting Blocks
|
||||
|
||||
The `setBlock` method takes a **String block type key**, not a BlockType object or int ID:
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
|
||||
World world = player.getWorld();
|
||||
|
||||
// Set block by block type key (string ID)
|
||||
world.setBlock(x, y, z, "stone");
|
||||
world.setBlock(x, y, z, "oak_planks", 0); // with settings
|
||||
|
||||
// Using BlockType - get the ID string first
|
||||
BlockType stone = BlockType.getAssetMap().getAsset("stone");
|
||||
if (stone != null) {
|
||||
world.setBlock(x, y, z, stone.getId()); // Use getId() to get the string key
|
||||
}
|
||||
|
||||
// Block ID index (for internal use)
|
||||
int stoneIndex = BlockType.getAssetMap().getIndex("stone");
|
||||
// Note: setBlock takes String, not int - use the key directly
|
||||
```
|
||||
|
||||
## Block Placement with Rotation
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**placeBlock() is on WorldChunk, not World!** You must get the chunk first to place blocks with rotation.
|
||||
{{< /callout >}}
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
|
||||
// Get the chunk containing this block position
|
||||
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
|
||||
if (chunk != null) {
|
||||
// Place block with rotation
|
||||
// Rotation values: None, Ninety, OneEighty, TwoSeventy
|
||||
chunk.placeBlock(x, y, z, "oak_log",
|
||||
Rotation.Ninety, // yaw (90 degrees)
|
||||
Rotation.None, // pitch
|
||||
Rotation.None // roll
|
||||
);
|
||||
|
||||
// Place with rotation tuple and options
|
||||
chunk.placeBlock(x, y, z, "oak_log",
|
||||
RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None),
|
||||
0, // settings
|
||||
true // validatePlacement
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Rotation Values
|
||||
|
||||
The `Rotation` enum has these values (NOT cardinal directions):
|
||||
|
||||
| Rotation | Degrees |
|
||||
|----------|---------|
|
||||
| `Rotation.None` | 0° |
|
||||
| `Rotation.Ninety` | 90° |
|
||||
| `Rotation.OneEighty` | 180° |
|
||||
| `Rotation.TwoSeventy` | 270° |
|
||||
|
||||
## Breaking Blocks
|
||||
|
||||
```java
|
||||
// Break block at position (requires settings parameter)
|
||||
world.breakBlock(x, y, z, 0); // 0 = default settings
|
||||
```
|
||||
|
||||
## BlockType Assets
|
||||
|
||||
BlockType assets define block properties:
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.protocol.BlockMaterial;
|
||||
|
||||
// Get block type by ID
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset("stone");
|
||||
|
||||
if (blockType != null) {
|
||||
// Get block ID string
|
||||
String blockId = blockType.getId();
|
||||
|
||||
// Get material
|
||||
BlockMaterial material = blockType.getMaterial();
|
||||
}
|
||||
|
||||
// Get block ID index (for internal use)
|
||||
int blockIndex = BlockType.getAssetMap().getIndex("stone");
|
||||
|
||||
// Get block from index
|
||||
BlockType fromIndex = BlockType.getAssetMap().getAsset(blockIndex);
|
||||
```
|
||||
|
||||
## Block Events
|
||||
|
||||
### BreakBlockEvent
|
||||
|
||||
Fired when a block is broken:
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import java.util.logging.Level;
|
||||
|
||||
getEventRegistry().register(BreakBlockEvent.class, event -> {
|
||||
// Get break info
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
getLogger().at(Level.INFO).log("Block broken at " + position);
|
||||
|
||||
// Cancel if protected
|
||||
event.setCancelled(true);
|
||||
});
|
||||
```
|
||||
|
||||
### PlaceBlockEvent
|
||||
|
||||
Fired when a block is placed:
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
|
||||
|
||||
getEventRegistry().register(PlaceBlockEvent.class, event -> {
|
||||
// Get place info
|
||||
Vector3i position = event.getTargetBlock();
|
||||
RotationTuple rotation = event.getRotation();
|
||||
|
||||
getLogger().at(Level.INFO).log("Block placed at " + position);
|
||||
|
||||
// Cancel if not allowed
|
||||
event.setCancelled(true);
|
||||
});
|
||||
```
|
||||
|
||||
### DamageBlockEvent
|
||||
|
||||
Fired when a block is damaged (before breaking):
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;
|
||||
|
||||
getEventRegistry().register(DamageBlockEvent.class, event -> {
|
||||
Vector3i position = event.getTargetBlock();
|
||||
BlockType blockType = event.getBlockType();
|
||||
|
||||
// Cancel to prevent damage
|
||||
event.setCancelled(true);
|
||||
});
|
||||
```
|
||||
|
||||
## Block Interaction State
|
||||
|
||||
Blocks can have interaction states (like open/closed doors):
|
||||
|
||||
```java
|
||||
BlockType currentBlock = world.getBlockType(x, y, z);
|
||||
|
||||
if (currentBlock != null) {
|
||||
// Set block to a different state
|
||||
world.setBlockInteractionState(
|
||||
new Vector3i(x, y, z),
|
||||
currentBlock,
|
||||
"open" // state name
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Working with Blocks
|
||||
|
||||
### Check Area for Block Type
|
||||
|
||||
```java
|
||||
public boolean containsBlockType(World world, Vector3i min, Vector3i max, String blockTypeKey) {
|
||||
// Note: Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
|
||||
for (int x = min.getX(); x <= max.getX(); x++) {
|
||||
for (int y = min.getY(); y <= max.getY(); y++) {
|
||||
for (int z = min.getZ(); z <= max.getZ(); z++) {
|
||||
BlockType block = world.getBlockType(x, y, z);
|
||||
if (block != null && block.getId().equals(blockTypeKey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### Fill Area
|
||||
|
||||
```java
|
||||
public void fillArea(World world, Vector3i min, Vector3i max, String blockTypeKey) {
|
||||
for (int x = min.getX(); x <= max.getX(); x++) {
|
||||
for (int y = min.getY(); y <= max.getY(); y++) {
|
||||
for (int z = min.getZ(); z <= max.getZ(); z++) {
|
||||
world.setBlock(x, y, z, blockTypeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Replace Blocks
|
||||
|
||||
```java
|
||||
public void replaceBlocks(World world, Vector3i min, Vector3i max,
|
||||
String fromBlock, String toBlock) {
|
||||
for (int x = min.getX(); x <= max.getX(); x++) {
|
||||
for (int y = min.getY(); y <= max.getY(); y++) {
|
||||
for (int z = min.getZ(); z <= max.getZ(); z++) {
|
||||
BlockType current = world.getBlockType(x, y, z);
|
||||
if (current != null && current.getId().equals(fromBlock)) {
|
||||
world.setBlock(x, y, z, toBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Block Positions
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
|
||||
// Vector3i for integer block positions
|
||||
Vector3i blockPos = new Vector3i(100, 64, 200);
|
||||
|
||||
// Get individual coordinates - use getX(), getY(), getZ()
|
||||
int x = blockPos.getX();
|
||||
int y = blockPos.getY();
|
||||
int z = blockPos.getZ();
|
||||
|
||||
// Adjacent positions
|
||||
Vector3i above = new Vector3i(x, y + 1, z);
|
||||
Vector3i below = new Vector3i(x, y - 1, z);
|
||||
Vector3i north = new Vector3i(x, y, z - 1);
|
||||
Vector3i south = new Vector3i(x, y, z + 1);
|
||||
Vector3i east = new Vector3i(x + 1, y, z);
|
||||
Vector3i west = new Vector3i(x - 1, y, z);
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**Block Manipulation Tips:**
|
||||
- Block operations must run on the world's ticking thread
|
||||
- Use `getBlockType()` for checking block types, `getBlock()` for raw IDs
|
||||
- Check `BlockType.EMPTY` for air/empty blocks
|
||||
- Events extend `CancellableEcsEvent` - use `setCancelled(true)` to prevent
|
||||
- Consider chunk boundaries when processing large areas
|
||||
- For `placeBlock()` with rotation, get the chunk first
|
||||
{{< /callout >}}
|
||||
|
||||
```java
|
||||
// Safe block modification from async context
|
||||
world.execute(() -> {
|
||||
world.setBlock(x, y, z, "stone");
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### World (IChunkAccessorSync) Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getBlock(int, int, int)` | `int` | Get block ID at position |
|
||||
| `getBlock(Vector3i)` | `int` | Get block ID at position |
|
||||
| `getBlockType(int, int, int)` | `BlockType?` | Get BlockType at position |
|
||||
| `getBlockType(Vector3i)` | `BlockType?` | Get BlockType at position |
|
||||
| `setBlock(x, y, z, String)` | `void` | Set block by type key |
|
||||
| `setBlock(x, y, z, String, int)` | `void` | Set block with settings |
|
||||
| `breakBlock(x, y, z, int)` | `boolean` | Break block with settings |
|
||||
| `setBlockInteractionState(...)` | `void` | Set block state |
|
||||
|
||||
### WorldChunk (BlockAccessor) Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `placeBlock(x, y, z, String, Rotation, Rotation, Rotation)` | `boolean` | Place with rotation |
|
||||
| `placeBlock(x, y, z, String, RotationTuple, int, boolean)` | `boolean` | Place with options |
|
||||
| `testPlaceBlock(...)` | `boolean` | Test if placement valid |
|
||||
Reference in New Issue
Block a user