Skip to content

Commit

Permalink
PlacedBlueprint
Browse files Browse the repository at this point in the history
  • Loading branch information
andantet committed Nov 14, 2023
1 parent c1d3be9 commit b094240
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/net/mcbrawls/blueprint/BlueprintMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object BlueprintMod : ModInitializer {
const val MOD_ID = "blueprint"
const val MOD_NAME = "Blueprint"

private val logger = LoggerFactory.getLogger(MOD_NAME)
val logger = LoggerFactory.getLogger(MOD_NAME)

override fun onInitialize() {
logger.info("Initializing $MOD_NAME")
Expand Down
22 changes: 10 additions & 12 deletions src/main/kotlin/net/mcbrawls/blueprint/region/CompoundRegion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import dev.andante.codex.SetCodec.Companion.setOf
import dev.andante.codex.nullableFieldOf
import net.mcbrawls.blueprint.region.serialization.SerializableRegion
import net.mcbrawls.blueprint.structure.Blueprint
import net.minecraft.entity.Entity
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
Expand All @@ -25,14 +24,6 @@ data class CompoundRegion(
*/
val globalOffset: Vec3d? = null
) : Region {
/**
* A secondary vararg constructor.
*/
constructor(
vararg regions: SerializableRegion,
globalOffset: Vec3d? = null
) : this(regions.toSet(), globalOffset)

private fun getAbsoluteOffset(offset: Vec3d): Vec3d {
return if (globalOffset != null) {
offset.add(globalOffset)
Expand Down Expand Up @@ -68,12 +59,19 @@ data class CompoundRegion(
}

/**
* Creates a compound region from the given regions of a blueprint.
* Creates a compound region from the given regions.
* @return a compound region
*/
fun of(blueprint: Blueprint, vararg keys: String): CompoundRegion {
val regions = keys.mapNotNull(blueprint.regions::get)
fun ofRegions(vararg regions: SerializableRegion): CompoundRegion {
return CompoundRegion(regions.toSet())
}

/**
* Creates a compound region from the given regions with an offset.
* @return a compound region
*/
fun ofRegionsOffset(offset: Vec3d, vararg regions: SerializableRegion): CompoundRegion {
return CompoundRegion(regions.toSet(), offset)
}
}
}
18 changes: 18 additions & 0 deletions src/main/kotlin/net/mcbrawls/blueprint/region/EmptyRegion.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package net.mcbrawls.blueprint.region

import net.minecraft.entity.Entity
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d

/**
* A region with no size or position.
*/
object EmptyRegion : Region {
override fun getBlockPositions(offset: Vec3d): Set<BlockPos> {
return emptySet()
}

override fun contains(entity: Entity, offset: Vec3d): Boolean {
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ abstract class SerializableRegion(
* @return a compound region
*/
fun withOffset(offset: Vec3d): CompoundRegion {
return CompoundRegion(this, globalOffset = offset)
return CompoundRegion.ofRegionsOffset(offset, this)
}

/**
Expand Down
21 changes: 16 additions & 5 deletions src/main/kotlin/net/mcbrawls/blueprint/structure/Blueprint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.mcbrawls.blueprint.region.serialization.SerializableRegion
import net.minecraft.block.BlockState
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d

/**
* Represents a structure blueprint.
Expand All @@ -17,10 +18,15 @@ data class Blueprint(
val palette: List<BlockState>,

/**
* The block states stored as their palette ids.
* A list of paletted states, mapping palette indexes to their positions.
*/
val palettedBlockStates: List<PalettedState>,

/**
* The size of the blueprint.
*/
val size: Vec3d,

/**
* The regions stored within this blueprint.
*/
Expand All @@ -29,17 +35,19 @@ data class Blueprint(
/**
* A list of the positions to their actual block states.
*/
private val blockStates: Map<BlockPos, BlockState> = palettedBlockStates.associate { it.blockPos to palette[it.paletteIndex] }
private val blockStates: Map<BlockPos, BlockState> = palettedBlockStates
.associate { palettedState -> palettedState.blockPos to palette[palettedState.paletteIndex] }

/**
* Places this blueprint in the world at the given position.
* @return a placed blueprint
*/
fun place(world: ServerWorld, pos: BlockPos): Int {
fun place(world: ServerWorld, position: BlockPos): PlacedBlueprint {
blockStates.forEach { (offset, state) ->
world.setBlockState(pos.add(offset), state)
world.setBlockState(position.add(offset), state)
}

return blockStates.size
return PlacedBlueprint(this, position)
}

companion object {
Expand All @@ -54,6 +62,9 @@ data class Blueprint(
PalettedState.CODEC.listOf()
.fieldOf("block_states")
.forGetter(Blueprint::palettedBlockStates),
Vec3d.CODEC
.fieldOf("size")
.forGetter(Blueprint::size),
Codec.unboundedMap(
Codec.STRING,
SerializableRegion.CODEC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.math.BlockPos

/**
* A paletted block state.
* The placement of a palette block state within a blueprint.
*/
data class PalettedState(
/**
* The offset position.
* The offset position from the root of the blueprint placement.
*/
val blockPos: BlockPos,

/**
* The index of the block state in the palette.
* The index of the block state in the blueprint's palette.
*/
val paletteIndex: Int
) {
override fun toString(): String {
return "PalettedState[#$paletteIndex, $blockPos]"
}

companion object {
/**
* The codec of this class.
* The codec of a paletted state.
*/
val CODEC: Codec<PalettedState> = RecordCodecBuilder.create { instance ->
instance.group(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package net.mcbrawls.blueprint.structure

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.mcbrawls.blueprint.BlueprintMod.logger
import net.mcbrawls.blueprint.region.CompoundRegion
import net.mcbrawls.blueprint.region.EmptyRegion
import net.mcbrawls.blueprint.region.Region
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d

/**
* A blueprint after it has been placed in the world.
*/
data class PlacedBlueprint(
/**
* The blueprint used in placement.
*/
val sourceBlueprint: Blueprint,

/**
* Where the blueprint was placed in the world.
*/
val placedPosition: BlockPos
) {
val offset: Vec3d = Vec3d.of(placedPosition)

/**
* Places the source blueprint again.
* @return this placed blueprint
*/
fun place(world: ServerWorld): PlacedBlueprint {
sourceBlueprint.place(world, placedPosition)
return this
}

/**
* Retrieves a single region from the source blueprint.
* @return an offset region
*/
fun getRegion(key: String): Region {
val region = sourceBlueprint.regions[key]

// verify region
if (region == null) {
logger.warn("Tried to access blueprint region but was not present: $key")
return EmptyRegion
}

// return offset compound region
return CompoundRegion.ofRegionsOffset(offset, region)
}

/**
* Retrieves all given regions from the source blueprint as a compound region.
* @return an offset region
*/
fun getRegionsCombined(vararg keys: String): Region {
val nullableRegions = keys.associateWith { key -> sourceBlueprint.regions[key] }
val regions = nullableRegions.values.filterNotNull()

// verify regions
if (regions.size != nullableRegions.size) {
val invalidKeys = nullableRegions.filter { it.value == null }.keys
logger.warn("Tried to access blueprint regions but were not present: $invalidKeys")
}

if (regions.isEmpty()) {
return EmptyRegion
}

// return offset compound region
return CompoundRegion.ofRegionsOffset(offset, *regions.toTypedArray())
}

companion object {
/**
* The codec for a placed blueprint.
*/
val CODEC: Codec<PlacedBlueprint> = RecordCodecBuilder.create { instance ->
instance.group(
Blueprint.CODEC.fieldOf("source_blueprint").forGetter(PlacedBlueprint::sourceBlueprint),
BlockPos.CODEC.fieldOf("placed_position").forGetter(PlacedBlueprint::placedPosition)
).apply(instance, ::PlacedBlueprint)
}
}
}

0 comments on commit b094240

Please sign in to comment.