Skip to content

Commit

Permalink
Block entity support
Browse files Browse the repository at this point in the history
  • Loading branch information
andantet committed Jul 8, 2024
1 parent c8a238d commit a6e8c3f
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 20 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ yarn_build=7
loader_version=0.15.11

# Mod Properties
mod_version=1.5.5
mod_version=1.6.0
maven_group=net.mcbrawls
mod_id=blueprint

Expand Down
26 changes: 18 additions & 8 deletions src/main/kotlin/net/mcbrawls/blueprint/command/BlueprintCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import net.mcbrawls.blueprint.editor.BlueprintEditorGui
import net.mcbrawls.blueprint.editor.BlueprintEditors
import net.mcbrawls.blueprint.resource.BlueprintManager
import net.mcbrawls.blueprint.structure.Blueprint
import net.mcbrawls.blueprint.structure.BlueprintBlockEntity
import net.mcbrawls.blueprint.structure.PalettedState
import net.mcbrawls.sgui.openGui
import net.minecraft.block.BlockState
Expand Down Expand Up @@ -121,21 +122,30 @@ object BlueprintCommand {

// create paletted positions
val palette = mutableListOf<BlockState>()
val palettedBlockStates = positions.mapNotNull { pos ->
val blockEntities = mutableListOf<BlueprintBlockEntity>()
val palettedBlockStates = mutableListOf<PalettedState>()

positions.forEach { pos ->
val relativePos = pos.subtract(min)

// state
val state = world.getBlockState(pos)
if (state.isAir) {
// disregard air
null
} else {
if (!state.isAir) {
// build palette
if (state !in palette) {
palette.add(state)
}

// create paletted state
val relativePos = pos.subtract(min)
val paletteId = palette.indexOf(state)
PalettedState(relativePos, paletteId)
palettedBlockStates.add(PalettedState(relativePos, paletteId))
}

// block entity
val blockEntity = world.getBlockEntity(pos)
if (blockEntity != null) {
val nbt = blockEntity.createNbt(world.registryManager)
blockEntities.add(BlueprintBlockEntity(relativePos, nbt))
}
}

Expand All @@ -144,7 +154,7 @@ object BlueprintCommand {
val size = Vec3i(blockBox.blockCountX, blockBox.blockCountZ, blockBox.blockCountZ)

// create blueprint
val blueprint = Blueprint(palette, palettedBlockStates, size, mapOf())
val blueprint = Blueprint(palette, palettedBlockStates, blockEntities.associateBy(BlueprintBlockEntity::blockPos), size, mapOf())
val nbt = Blueprint.CODEC.encodeQuick(NbtOps.INSTANCE, blueprint)

// save blueprint
Expand Down
47 changes: 36 additions & 11 deletions src/main/kotlin/net/mcbrawls/blueprint/structure/Blueprint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.mcbrawls.blueprint.region.serialization.SerializableRegion
import net.minecraft.block.BlockState
import net.minecraft.nbt.NbtCompound
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3i
Expand All @@ -25,6 +26,11 @@ data class Blueprint(
*/
val palettedBlockStates: List<PalettedState>,

/**
* A list of block entities stored within the blueprint.
*/
val blockEntities: Map<BlockPos, BlueprintBlockEntity>,

/**
* The size of the blueprint.
*/
Expand All @@ -50,11 +56,7 @@ data class Blueprint(
* @return a placed blueprint
*/
fun place(world: ServerWorld, position: BlockPos, processor: BlockStateProcessor? = null): PlacedBlueprint {
forEach { offset, state ->
val trueState = processor?.process(state) ?: state
world.setBlockState(position.add(offset), trueState)
}

forEach { offset, (state, blockEntityNbt) -> placePosition(world, position, offset, state, blockEntityNbt, processor) }
return PlacedBlueprint(this, position)
}

Expand All @@ -68,9 +70,8 @@ data class Blueprint(
val future: CompletableFuture<PlacedBlueprint> = CompletableFuture.supplyAsync {
synchronized(world) {
var i = 0
forEach { offset, state ->
val trueState = processor?.process(state) ?: state
world.setBlockState(position.add(offset), trueState)
forEach { offset, (state, blockEntityNbt) ->
placePosition(world, position, offset, state, blockEntityNbt, processor)
progress.set(++i / totalBlocks.toFloat())
}
}
Expand All @@ -81,13 +82,31 @@ data class Blueprint(
return future to ProgressProvider(progress::get)
}

/**
* Places a position's block data to the world.
*/
private fun placePosition(world: ServerWorld, position: BlockPos, offset: BlockPos, state: BlockState, blockEntityNbt: NbtCompound?, processor: BlockStateProcessor?) {
val trueState = processor?.process(state) ?: state
val truePos = position.add(offset)

// state
world.setBlockState(truePos, trueState)

// block entity
if (blockEntityNbt != null) {
val blockEntity = world.getBlockEntity(truePos)
blockEntity?.read(blockEntityNbt, world.registryManager)
}
}

/**
* Performs the given action for every position in the blueprint.
*/
fun forEach(action: BiConsumer<BlockPos, BlockState>) {
fun forEach(action: BiConsumer<BlockPos, Pair<BlockState, NbtCompound?>>) {
palettedBlockStates.forEach { (offset, index) ->
val state = palette[index]
action.accept(offset, state)
val blockEntity = blockEntities[offset]?.nbt
action.accept(offset, state to blockEntity)
}
}

Expand All @@ -103,18 +122,24 @@ data class Blueprint(
PalettedState.CODEC.listOf()
.fieldOf("block_states")
.forGetter(Blueprint::palettedBlockStates),
BlueprintBlockEntity.CODEC.listOf()
.fieldOf("block_entities")
.xmap({ entry -> entry.associateBy(BlueprintBlockEntity::blockPos) }, { map -> map.values.toList() })
.orElseGet(::emptyMap)
.forGetter(Blueprint::blockEntities),
Vec3i.CODEC
.fieldOf("size")
.forGetter(Blueprint::size),
Codec.unboundedMap(Codec.STRING, SerializableRegion.CODEC)
.fieldOf("regions")
.orElseGet(::emptyMap)
.forGetter(Blueprint::regions)
).apply(instance, ::Blueprint)
}

/**
* An entirely empty blueprint.
*/
val EMPTY = Blueprint(emptyList(), emptyList(), Vec3i.ZERO, emptyMap())
val EMPTY = Blueprint(emptyList(), emptyList(), emptyMap(), Vec3i.ZERO, emptyMap())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.mcbrawls.blueprint.structure

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.NbtCompound
import net.minecraft.util.math.BlockPos

data class BlueprintBlockEntity(
/**
* The offset position from the root of the blueprint placement.
*/
val blockPos: BlockPos,

/**
* The block entity NBT.
*/
val nbt: NbtCompound
) {
override fun toString(): String {
return "BlueprintBlockEntity[$blockPos, $nbt]"
}

companion object {
/**
* The codec of a blueprint block entity.
*/
val CODEC: Codec<BlueprintBlockEntity> = RecordCodecBuilder.create { instance ->
instance.group(
BlockPos.CODEC.fieldOf("offset").forGetter(BlueprintBlockEntity::blockPos),
NbtCompound.CODEC.fieldOf("nbt").forGetter(BlueprintBlockEntity::nbt)
).apply(instance, ::BlueprintBlockEntity)
}
}
}
Binary file not shown.

0 comments on commit a6e8c3f

Please sign in to comment.