Skip to content

Commit

Permalink
fix(creator_markers): serialization and editor world handling
Browse files Browse the repository at this point in the history
  • Loading branch information
andantet committed Jan 12, 2025
1 parent 3e23610 commit c068565
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 47 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
java {
withSourcesJar()

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

jar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class RegionIdBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Bluepr

override fun writeNbt(nbt: NbtCompound, lookup: RegistryWrapper.WrapperLookup) {
super.writeNbt(nbt, lookup)
id?.also { id -> nbt.putString(REGION_ID_KEY, id) }
id?.also { nbt.putString(REGION_ID_KEY, it) }
}

override fun readNbt(nbt: NbtCompound, lookup: RegistryWrapper.WrapperLookup) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import xyz.nucleoid.fantasy.RuntimeWorld
import xyz.nucleoid.fantasy.RuntimeWorldConfig
import java.util.function.BooleanSupplier
import kotlin.math.max
import kotlin.math.min

Expand Down Expand Up @@ -46,6 +47,7 @@ class BlueprintEditorWorld(
maxZ += size.z

blueprint.place(this, BLUEPRINT_PLACEMENT_POS)
blueprint.placeCreatorMarkers(this, BLUEPRINT_PLACEMENT_POS)
false
} else {
setBlockState(minPos, Blocks.STONE.defaultState)
Expand All @@ -56,20 +58,7 @@ class BlueprintEditorWorld(
override fun setBlockState(pos: BlockPos, state: BlockState, flags: Int, maxUpdateDepth: Int): Boolean {
return if (super.setBlockState(pos, state, flags, maxUpdateDepth)) {
if (!state.isAir) {
pos.x.also {
minX = min(minX, it)
maxX = max(maxX, it)
}

pos.y.also {
minY = min(minY, it)
maxY = max(maxY, it)
}

pos.z.also {
minZ = min(minZ, it)
maxZ = max(maxZ, it)
}
updateExpectedSize(pos)
}

true
Expand All @@ -78,6 +67,31 @@ class BlueprintEditorWorld(
}
}

override fun tick(shouldKeepTicking: BooleanSupplier) {
super.tick(shouldKeepTicking)

players.forEach { player ->
updateExpectedSize(player.blockPos)
}
}

private fun updateExpectedSize(pos: BlockPos) {
pos.x.also {
minX = min(minX, it)
maxX = max(maxX, it)
}

pos.y.also {
minY = min(minY, it)
maxY = max(maxY, it)
}

pos.z.also {
minZ = min(minZ, it)
maxZ = max(maxZ, it)
}
}

/**
* Calculates the total bounding box of the Blueprint within the world.
* @return a pair of min/max positions
Expand Down Expand Up @@ -112,23 +126,8 @@ class BlueprintEditorWorld(
*/
fun saveBlueprint(): String {
val (min, max) = getRoughBlueprintBoundingBox()

val generatedBlueprint = Blueprint.save(this, min, max).let { generatedBlueprint ->
val blueprint = sourceBlueprint
if (blueprint != null) {
Blueprint(
generatedBlueprint.palette,
generatedBlueprint.palettedBlockStates,
generatedBlueprint.blockEntities,
blueprint.regions + generatedBlueprint.regions,
blueprint.anchors + generatedBlueprint.anchors,
)
} else {
generatedBlueprint
}
}

return BlueprintManager.saveGenerated(server, blueprintId, generatedBlueprint)
val blueprint = Blueprint.save(this, min, max)
return BlueprintManager.saveGenerated(server, blueprintId, blueprint)
}

companion object {
Expand Down
47 changes: 37 additions & 10 deletions src/main/kotlin/net/mcbrawls/blueprint/entity/AnchorEntity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ import eu.pb4.polymer.core.api.entity.PolymerEntity
import net.mcbrawls.blueprint.anchor.Anchor
import net.mcbrawls.blueprint.structure.Blueprint
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityPose
import net.minecraft.entity.EntityType
import net.minecraft.entity.decoration.InteractionEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.particle.DustParticleEffect
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.Formatting
import net.minecraft.util.Hand
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec2f
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import xyz.nucleoid.packettweaker.PacketContext

Expand All @@ -23,8 +28,9 @@ class AnchorEntity(type: EntityType<*>, world: World) : InteractionEntity(type,
var data: String? = null

init {
interactionWidth = 0.25f
interactionHeight = 0.25f
val dimensions = getDimensions(EntityPose.STANDING)
interactionWidth = dimensions.width / 2
interactionHeight = dimensions.height / 2

setResponse(true)
}
Expand Down Expand Up @@ -53,14 +59,14 @@ class AnchorEntity(type: EntityType<*>, world: World) : InteractionEntity(type,
}

fun openAnchorIdEditor(player: ServerPlayerEntity) {
openInputGui(player, Text.literal("Anchor ID"), this, getOrCreateId(), "anchor ID") { input, dataName ->
openInputGui(player, Text.literal("Anchor ID"), getOrCreateId(), "anchor ID") { input, dataName ->
id = input.trim()
player.sendMessage(Text.literal("Set $dataName: \"$id\"").formatted(Formatting.GREEN))
}
}

fun openAnchorDataEditor(player: ServerPlayerEntity) {
openInputGui(player, Text.literal("Anchor Data"), this, "", "data") { input, dataName ->
openInputGui(player, Text.literal("Anchor Data"), data ?: "", "data", canBeBlank = true) { input, dataName ->
data = input.trim()
player.sendMessage(Text.literal("Set $dataName: \"$data\"").formatted(Formatting.GREEN))
}
Expand All @@ -76,8 +82,8 @@ class AnchorEntity(type: EntityType<*>, world: World) : InteractionEntity(type,
return false
}

fun createAnchor(): Anchor {
return Anchor(pos, Vec2f(pitch, yaw), data)
fun createAnchor(root: BlockPos): Anchor {
return Anchor(pos.subtract(Vec3d.of(root)), Vec2f(pitch, yaw), data)
}

/**
Expand All @@ -90,20 +96,41 @@ class AnchorEntity(type: EntityType<*>, world: World) : InteractionEntity(type,
return Blueprint.Companion.createUniqueId(world ?: throw IllegalStateException("World not set"), pos)
}

override fun writeCustomDataToNbt(nbt: NbtCompound) {
super.writeCustomDataToNbt(nbt)

id?.also { nbt.putString(ANCHOR_ID_KEY, it) }
data?.also { nbt.putString(DATA_KEY, it) }
}

override fun readCustomDataFromNbt(nbt: NbtCompound) {
super.readCustomDataFromNbt(nbt)

if (nbt.contains(ANCHOR_ID_KEY, NbtElement.STRING_TYPE.toInt())) {
id = nbt.getString(ANCHOR_ID_KEY)
}

if (nbt.contains(DATA_KEY, NbtElement.STRING_TYPE.toInt())) {
data = nbt.getString(DATA_KEY)
}
}

override fun getPolymerEntityType(context: PacketContext): EntityType<*> {
return EntityType.INTERACTION
}

companion object {
const val ANCHOR_ID_KEY = "anchor_id"
const val DATA_KEY = "data"

/**
* Opens the region id editor gui for a given region id block entity.
*/
fun openInputGui(player: ServerPlayerEntity, slateTitle: Text, anchorEntity: AnchorEntity, initialInput: String, dataName: String, setter: (input: String, dataName: String) -> Unit) {
fun openInputGui(player: ServerPlayerEntity, slateTitle: Text, initialInput: String, dataName: String, canBeBlank: Boolean = false, setter: (input: String, dataName: String) -> Unit) {
Blueprint.Companion.openInputGui(player, slateTitle, initialInput) { input ->
if (input != initialInput) {
if (input.isBlank()) {
val id = anchorEntity.id
player.sendMessage(Text.literal("No $dataName set. Still: \"$id\"").formatted(Formatting.RED))
if (canBeBlank && input.isBlank()) {
player.sendMessage(Text.literal("No $dataName set. Still: \"$initialInput\"").formatted(Formatting.RED))
} else {
setter.invoke(input, dataName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object BlueprintEntityTypes {
val ANCHOR = register(
"anchor",
EntityType.Builder.create(::AnchorEntity, SpawnGroup.MISC)
.dimensions(0.25f, 0.25f)
.dimensions(0.3f, 0.3f)
.maxTrackingRange(2)
.trackingTickInterval(10)
.disableSaving()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ data class PointRegion(
/**
* The position of the point.
*/
val pointPosition: Vec3d
val pointPosition: Vec3d,
) : SerializableRegion(SerializableRegionTypes.POINT) {
override fun getBlockPositions(offset: Vec3d): Set<BlockPos> {
return setOf(BlockPos.ofFloored(pointPosition.add(offset)))
Expand Down
47 changes: 46 additions & 1 deletion src/main/kotlin/net/mcbrawls/blueprint/structure/Blueprint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package net.mcbrawls.blueprint.structure
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.mcbrawls.blueprint.anchor.Anchor
import net.mcbrawls.blueprint.block.BlueprintBlocks
import net.mcbrawls.blueprint.block.entity.RegionIdBlockEntity
import net.mcbrawls.blueprint.block.region.RegionBlock
import net.mcbrawls.blueprint.entity.AnchorEntity
import net.mcbrawls.blueprint.entity.BlueprintEntityTypes
import net.mcbrawls.blueprint.region.PointRegion
import net.mcbrawls.blueprint.region.serialization.SerializableRegion
import net.mcbrawls.slate.Slate.Companion.slate
import net.mcbrawls.slate.tile.Tile.Companion.tile
import net.mcbrawls.slate.tile.TileGrid
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.SpawnReason
import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound
import net.minecraft.screen.ScreenHandlerType
Expand Down Expand Up @@ -102,6 +107,46 @@ data class Blueprint(
return ProgressiveFuture(future, ProgressProvider(progress::get))
}

/**
* Places creator markers in the world from this blueprint.
*/
fun placeCreatorMarkers(world: ServerWorld, pos: BlockPos) {
placeRegions(world, pos)
placeAnchors(world, pos)
}

fun placeRegions(world: ServerWorld, pos: BlockPos) {
regions.forEach { id, region ->
if (region !is PointRegion) {
return@forEach
}

val offset = BlockPos.ofFloored(region.pointPosition)
placePointRegion(world, pos.add(offset), id)
}
}

fun placePointRegion(world: ServerWorld, pos: BlockPos, id: String) {
val state = BlueprintBlocks.POINT_REGION.defaultState
world.setBlockState(pos, state)

val blockEntity = RegionIdBlockEntity(pos, state)
blockEntity.id = id
world.addBlockEntity(blockEntity)
}

fun placeAnchors(world: ServerWorld, pos: BlockPos) {
anchors.forEach { id, anchor ->
BlueprintEntityTypes.ANCHOR.create(world, SpawnReason.COMMAND)?.also { anchorEntity ->
anchorEntity.id = id
anchorEntity.data = anchor.data
anchorEntity.setPosition(anchor.position.add(Vec3d.of(pos)))
anchorEntity.rotate(anchor.rotation.x, anchor.rotation.y)
world.spawnEntity(anchorEntity)
}
}
}

/**
* Places a position's block data to the world.
*/
Expand Down Expand Up @@ -225,7 +270,7 @@ data class Blueprint(
val anchors = mutableMapOf<String, Anchor>()
world.iterateEntities().filterIsInstance<AnchorEntity>().forEach { anchorEntity ->
val id = anchorEntity.getOrCreateId()
val anchor = anchorEntity.createAnchor()
val anchor = anchorEntity.createAnchor(min)
anchors[id] = anchor
}

Expand Down

0 comments on commit c068565

Please sign in to comment.