Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
d4f0ee3
feat: add Blocks and Stuff Recipes module with Gradle configuration
Bloeckchengrafik Mar 7, 2026
fcdbf45
feat: add various recipe classes and JSON configurations for crafting…
CreepyX-Dev Mar 15, 2026
0a7de92
refactor: update StashController imports and clean up package structure
CreepyX-Dev Mar 15, 2026
48f47ea
feat: new block inventory api and fix furnace issues
Bloeckchengrafik Apr 9, 2026
9eb3c0b
docs: Add initial docs for the api
Bloeckchengrafik Apr 9, 2026
861bf90
Merge branch 'dev' into feat/recipes
Bloeckchengrafik Apr 9, 2026
d3fc857
refactor: clean up imports and improve package structure in recipe files
Bloeckchengrafik Apr 10, 2026
e303c46
refactor: replace BlockVec with asBlockVec() in various rules and events
Bloeckchengrafik Apr 10, 2026
ed6cf93
chore: reformat smelting handling code for better readability and con…
TheNick24 Apr 12, 2026
a1804d7
chore: update smelting logic to use raw iron, refine Furnaces' event …
TheNick24 Apr 12, 2026
b7415b1
chore: refactor SmithingTableInventory for slot constants, improve ev…
TheNick24 Apr 13, 2026
96f1f3d
chore: extract trim pattern mappings to utility, simplify TrimSmithin…
TheNick24 Apr 13, 2026
c06563f
chore: remove custom InventoryButtonClickEvent, update StonecutterInv…
TheNick24 Apr 13, 2026
9edc079
chore: improve block placement rules, enhance Farmland and GroundedPl…
TheNick24 Apr 16, 2026
f52dc8a
chore: simplify GroundedPlantBlockPlacementRule, replace manual dirt …
TheNick24 Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 86 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,16 @@
[![Supported Blocks](https://img.shields.io/badge/Supported_Blocks-100%25-green?style=for-the-badge)](TODO.md) [![Latest Version](https://img.shields.io/badge/Latest_Version-1.9.1--SNAPSHOT-green?style=for-the-badge)](https://mvn.everbuild.org/#/public/org/everbuild/blocksandstuff)
<!-- /TAG_REPLACEMENT -->

> This library is still in heavy development and API is subject to change in a breaking way at any time, even without a
> major release. See the [Versioning](#versioning) section for more details

This library provides a set of common implementations for blocks and fluids tailored for the Minestom framework. Its
purpose is to simplify and standardize the management of these entities in Minestom projects. By using this
library, developers can save time and focus on the unique aspects of their projects while relying on tested and reusable
components for core world functionalities.

### Key Features:

- **Blocks & Fluids**: Easy-to-use APIs for enabling block placement and block handlers as well as fluid simulation for
both custom and default blocks and fluids
- **Blocks, Fluids & Recipes**: Easy-to-use APIs for enabling block placement, handlers, recipes, and fluid simulation for vanilla or custom blocks.
- **Efficiency**: Optimized solutions built to integrate seamlessly with the Minestom framework.
- **Flexibility**: Highly customizable implementations suitable for a variety of use cases.
- **Flexibility**: Highly customizable implementations allowing developers to selectively include only the features they need.

See the sections below for installation instructions, versioning details, and contribution guidelines.

Expand All @@ -35,15 +31,88 @@ See the sections below for installation instructions, versioning details, and co

# Installation & Usage

> This library is not yet considered stable
Blocks-and-Stuff is split into four modules:
- `blocksandstuff-common` contains common APIs for blocksandstuff and your own code to communicate
- `blocksandstuff-blocks` contains block implementations, both placement and interaction
- `blocksandstuff-fluids` contains fluid implementations.
- `blocksandstuff-recipes` contains recipe implementations, although without the actual recipes themselves.

The latest version is always published to our maven repository at [mvn.everbuild.org](https://mvn.everbuild.org/#/public/org/everbuild/blocksandstuff). On the page of the maven repo. you can easily check the latest version and inclusion information.
The latest version is always published to our maven repository at [mvn.everbuild.org](https://mvn.everbuild.org/#/public/org/everbuild/blocksandstuff). On the page of the maven repository, you can check the latest version and inclusion information.

# Versioning
After including the module you want to use:

## Common API
> Artifact: `org.everbuild.blocksandstuff:blocksandstuff-common`

There are four parts to the common API:

**Dropped Items**: Implement the `DroppedItemFactory` and register it with
```kt
DroppedItemFactory.current = yourFactory
```
to customize item dropping behavior. The default implementation is `DefaultDroppedItemFactory`.

**Tags**: Some block placement rules require tags that are not present in vanilla. Sometimes, you may want to change these tags. For this, you may override the `blocksandstuff/block/<your-tag>.json` files in your resources. See [some default tags](blocksandstuff-blocks/src/main/resources/blocksandstuff/block) as an example.

**Instance Options**: Want to customize game-rules and similar options? Use the `InstanceOptions` class to set them:
```kt
val options = InstanceOptionsProvider.getForInstance(instance)
options.randomTickSpeed = 10
```

**Block Inventory**: The `BlockInventory` class is a convenient way to manage block inventories. Define a `BlockInventoryArchetype` and attach to a `BlockHandler` using the `BlockInventoryHolder`-interface. See furnaces as [an](blocksandstuff-recipes/src/main/kotlin/org/everbuild/blocksandstuff/recipes/smelting/FurnaceArchetype.kt) [example](blocksandstuff-recipes/src/main/kotlin/org/everbuild/blocksandstuff/recipes/smelting/AbstractSmeltingHandler.kt).

## Blocks API
> Artifact: `org.everbuild.blocksandstuff:blocksandstuff-blocks`

Use the following code-snippet to initialize all implemented vanilla behaviour:
```kt
// Register all default block placement rules
BlockPlacementRuleRegistrations.registerDefault()

// Register all default block behaviors
BlockBehaviorRuleRegistrations.registerDefault()

// Register auto-adding handlers to placed blocks
PlacedHandlerRegistration.registerDefault()

We're using Semantic Versioning for this project, although before the first `1.0.0`-release, we'll update API as we
see fit. In any other scenario, the versioning will follow these principles:
// Pick-Block behaviour
BlockPickup.enable()
```

You can exclude blocks by putting a list of them as parameters to the `registerDefault` methods. You can also only register specific handlers using the `register` methods.

## Fluids API
> Artifact: `org.everbuild.blocksandstuff:blocksandstuff-fluids`

> [!WARNING]
> The fluid API is not stable, very slow, and will be changed in the future. As a discouragement, no docs are provided as of now.

# Recipes API
> Artifact: `org.everbuild.blocksandstuff:blocksandstuff-recipes`

Recipes load from namespaces like [this](testserver/src/main/resources/data/everbuild). You can add your own namespace or copy ours to get started.

Use the `RecipeRegistrations` to register recipes and crafting.
```kt
// Kotlin:
RecipeRegistrations {
fuelNamespaces += "everbuild"
recipeNamespaces += "everbuild"
}

// Java:
RecipeRegistrations.builder()
.fuelNamespace("everbuild")
.recipeNamespace("everbuild")
.apply()
```

Use the `addRegistration` and `removeRegistration` methods to modify, which features are loaded. Using the `itemController`, custom items can be loaded from recipes as well. The `stashController` is used to register a hypixel-like stash.

# Versioning

We're using Semantic Versioning for this project:
- **MAJOR version**: Incremented for incompatible API changes.
- **MINOR version**: Incremented for new features introduced in a backward-compatible manner.
- **PATCH version**: Incremented for backward-compatible bug fixes.
Expand Down Expand Up @@ -92,4 +161,9 @@ This project is licensed under the [MIT License](LICENSE).
This means you are free to use, modify, and distribute the library in your own projects, whether they are private or
commercial, provided that you include a copy of the MIT license.

For more detailed information, please refer to the [LICENSE](LICENSE) file in the repository.
For more detailed information, please refer to the [LICENSE](LICENSE) file in the repository.

- - -

If you use and/or appreciate this library, we'd appreciate it if you could quickly star it. Also, we'd love to see what you
build with it! Please reach out to us if you have any questions or feedback.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.everbuild.blocksandstuff.blocks.behavior

import net.kyori.adventure.key.Key
import net.minestom.server.coordinate.BlockVec

Check warning on line 4 in blocksandstuff-blocks/src/main/kotlin/org/everbuild/blocksandstuff/blocks/behavior/CakeEatRule.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Unused import directive

Unused import directive
import net.minestom.server.event.EventDispatcher
import net.minestom.server.instance.block.Block
import net.minestom.server.instance.block.BlockHandler
Expand All @@ -27,7 +27,7 @@
CakeEatEvent(
interaction.player,
interaction.block,
BlockVec(interaction.blockPosition)
interaction.blockPosition.asBlockVec()
)
) {
if (currentSlices < (maxSlices - 1)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import kotlin.collections.component1
import kotlin.collections.component2
import net.kyori.adventure.key.Key
import net.minestom.server.coordinate.BlockVec

Check warning on line 6 in blocksandstuff-blocks/src/main/kotlin/org/everbuild/blocksandstuff/blocks/behavior/CandleCakeRule.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Unused import directive

Unused import directive
import net.minestom.server.event.EventDispatcher
import net.minestom.server.instance.block.Block
import net.minestom.server.instance.block.BlockHandler
Expand All @@ -21,7 +21,7 @@
CakeEatEvent(
interaction.player,
interaction.block,
BlockVec(interaction.blockPosition)
interaction.blockPosition.asBlockVec()
)
) {
interaction.instance.setBlock(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import net.minestom.server.instance.block.BlockFace
import net.minestom.server.instance.block.BlockHandler
import java.util.concurrent.ThreadLocalRandom
import net.minestom.server.coordinate.BlockVec

Check warning on line 11 in blocksandstuff-blocks/src/main/kotlin/org/everbuild/blocksandstuff/blocks/behavior/CopperOxidationRule.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Unused import directive

Unused import directive
import net.minestom.server.event.EventDispatcher
import org.everbuild.blocksandstuff.blocks.event.CopperOxidationEvent
import org.everbuild.blocksandstuff.blocks.randomticking.RandomTickHandler
Expand Down Expand Up @@ -56,7 +56,7 @@
val event = CopperOxidationEvent(
block,
nextStage,
BlockVec(blockPosition),
blockPosition.asBlockVec(),
instance
)
EventDispatcher.call(event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import kotlin.math.atan2
import net.kyori.adventure.key.Key
import net.minestom.server.coordinate.BlockVec

Check warning on line 5 in blocksandstuff-blocks/src/main/kotlin/org/everbuild/blocksandstuff/blocks/behavior/SignEditRule.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Unused import directive

Unused import directive
import net.minestom.server.coordinate.Point
import net.minestom.server.entity.Player
import net.minestom.server.event.EventDispatcher
Expand Down Expand Up @@ -67,7 +67,7 @@
}

private fun openEditor(block: Block, position: Point, player: Player, front: Boolean) {
EventDispatcher.callCancellable(PlayerOpenSignEditorEvent(player, BlockVec(position), block)) {
EventDispatcher.callCancellable(PlayerOpenSignEditorEvent(player, position.asBlockVec(), block)) {
player.sendPacket(OpenSignEditorPacket(position, front))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,9 +465,8 @@ object VanillaPlacementRules : VanillaRuleset<PlacementGroup, Function<Block, Bl
val GROUNDED_PLANTS =
group(
all(
byTag("minecraft:saplings"),
byTag("minecraft:small_flowers"),
byBlock(Block.SWEET_BERRY_BUSH),
byTag("minecraft:saplings"),
),
::GroundedPlantBlockPlacementRule,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@

import net.minestom.server.coordinate.Point
import net.minestom.server.instance.block.Block
import net.minestom.server.instance.block.BlockFace

Check warning on line 5 in blocksandstuff-blocks/src/main/kotlin/org/everbuild/blocksandstuff/blocks/placement/FarmlandPlacementRule.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Unused import directive

Unused import directive
import net.minestom.server.instance.block.rule.BlockPlacementRule
import net.minestom.server.registry.TagKey
import org.everbuild.blocksandstuff.common.utils.isWater

class FarmlandPlacementRule(block: Block) : BlockPlacementRule(block) {
class FarmlandPlacementRule(
block: Block,
) : BlockPlacementRule(block) {
val maintainsFarmLand = Block.staticRegistry().getTag(TagKey.ofHash("#minecraft:maintains_farmland"))!!
override fun blockUpdate(updateState: UpdateState): Block {
return checkBlockPlacement(updateState.blockPosition, updateState.instance) ?: updateState.currentBlock
}

override fun blockPlace(placementState: PlacementState): Block {
return checkBlockPlacement(placementState.placePosition, placementState.instance) ?: placementState.block()
}
override fun blockUpdate(updateState: UpdateState): Block =
checkBlockPlacement(updateState.blockPosition, updateState.instance) ?: updateState.currentBlock

override fun blockPlace(placementState: PlacementState): Block =
checkBlockPlacement(placementState.placePosition, placementState.instance) ?: placementState.block

fun checkBlockPlacement(placePosition: Point, instance: Block.Getter): Block? {
val abovePosition = placePosition.relative(BlockFace.TOP)
fun checkBlockPlacement(
placePosition: Point,
instance: Block.Getter,
): Block? {
val abovePosition = placePosition.add(0.0, 1.0, 0.0)
val aboveBlock = instance.getBlock(abovePosition)
if (!aboveBlock.isAir && !maintainsFarmLand.contains(aboveBlock)) {
if (!aboveBlock.isAir && !aboveBlock.isWater() && aboveBlock.isSolid) {
return Block.DIRT
}
return null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import net.minestom.server.instance.block.rule.BlockPlacementRule
import net.minestom.server.registry.TagKey
import org.everbuild.blocksandstuff.common.item.DroppedItemFactory

class GroundedPlantBlockPlacementRule(block: Block) : BlockPlacementRule(block) {
private val dirtBlocks = Block.staticRegistry().getTag(TagKey.ofHash("#minecraft:dirt"))!!
class GroundedPlantBlockPlacementRule(
block: Block,
) : BlockPlacementRule(block) {
private val dirtBlocks = Block.staticRegistry().getTag(TagKey.ofHash("#minecraft:supports_vegetation"))!!

override fun blockPlace(placementState: PlacementState): Block? {
val blockBelow = placementState.instance.getBlock(placementState.placePosition.add(0.0, -1.0, 0.0))
Expand All @@ -15,7 +17,6 @@ class GroundedPlantBlockPlacementRule(block: Block) : BlockPlacementRule(block)
return placementState.block
}
return null

}

override fun blockUpdate(updateState: UpdateState): Block {
Expand All @@ -27,6 +28,5 @@ class GroundedPlantBlockPlacementRule(block: Block) : BlockPlacementRule(block)
}

return updateState.currentBlock

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.everbuild.blocksandstuff.common.utils.getNearestHorizontalLookingDire
class ScaffoldingPlacementRule(block: Block) : BlockPlacementRule(block) {
override fun blockPlace(placementState: PlacementState): Block? {
val instance = placementState.instance as Instance
val placePos = BlockVec(placementState.placePosition)
val placePos = placementState.placePosition.asBlockVec()
val currentBlock = instance.getBlock(placementState.placePosition)
val face = placementState.blockFace ?: return placementState.block

Expand All @@ -29,7 +29,7 @@ class ScaffoldingPlacementRule(block: Block) : BlockPlacementRule(block) {
.withProperty("distance", nearest.toString())
}

val targetBlockPos = BlockVec(placementState.placePosition.relative(face.oppositeFace))
val targetBlockPos = placementState.placePosition.relative(face.oppositeFace).asBlockVec()
val targetBlock = placementState.instance.getBlock(targetBlockPos)

if (face.toDirection().horizontal()) {
Expand Down Expand Up @@ -112,7 +112,7 @@ class ScaffoldingPlacementRule(block: Block) : BlockPlacementRule(block) {

override fun blockUpdate(updateState: UpdateState): Block? {
val instance = updateState.instance as Instance
val position = BlockVec(updateState.blockPosition)
val position = updateState.blockPosition.asBlockVec()
val hasBottomSupport = hasBottomSupport(instance, position)
val distance = getDistanceToNearestBottomSupported(instance, position, hasBottomSupport)

Expand Down Expand Up @@ -143,7 +143,7 @@ class ScaffoldingPlacementRule(block: Block) : BlockPlacementRule(block) {
val point = position.add(direction.toDirection().vec().mul(n.toDouble() + 1.0))
val block = instance.getBlock(point)
if (!block.compare(Block.SCAFFOLDING)) {
return BlockVec(point)
return point.asBlockVec()
}
}
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class VerticallyRotatedPlacementRule(block: Block) : BlockPlacementRule(block) {
return placementState.block
.withProperty(
"facing",
(placementState.getHorizontalPlacementDirection() ?: return placementState.block).name.lowercase()
(placementState.getHorizontalPlacementDirection() ?: return placementState.block).opposite().name.lowercase()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.everbuild.blocksandstuff.common.blockinventory

import net.kyori.adventure.text.Component
import net.minestom.server.event.inventory.InventoryItemChangeEvent
import net.minestom.server.inventory.Inventory
import net.minestom.server.inventory.InventoryType
import net.minestom.server.item.ItemStack

open class BlockInventory(
type: InventoryType,
title: Component,
protected val backend: PhysicalInventory
) : Inventory(type, title) {
init {
eventNode().addListener(InventoryItemChangeEvent::class.java, this::updateItemInBackend)
}

private fun updateItemInBackend(event: InventoryItemChangeEvent) {
backend.transact {
it(event.slot, event.newItem)
}
}

fun updateStackRaw(slot: Int, stack: ItemStack) {
this.setItemStack(slot, stack)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.everbuild.blocksandstuff.common.blockinventory

import net.kyori.adventure.text.Component
import net.minestom.server.inventory.InventoryType

interface BlockInventoryArchetype {
val title: Component
val inventoryType: InventoryType
val size: Int

fun createInventory(backend: PhysicalInventory): BlockInventory
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.everbuild.blocksandstuff.common.blockinventory

import net.minestom.server.coordinate.Point
import net.minestom.server.instance.Instance

interface BlockInventoryHolder {
fun getInventory(instance: Instance, blockPos: Point): PhysicalInventory
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.everbuild.blocksandstuff.common.blockinventory

import net.minestom.server.item.ItemStack

interface PhysicalInventory {
val archetype: BlockInventoryArchetype

fun getItemStack(slot: Int): ItemStack
operator fun get(slot: Int): ItemStack = getItemStack(slot)

fun transact(action: (setter: (slot: Int, itemStack: ItemStack?) -> Unit) -> Unit)

fun getViewableInventory(): BlockInventory

fun readTags(): TagReader
}
Loading
Loading