Skip to content

Commit

Permalink
assembly via structure block
Browse files Browse the repository at this point in the history
  • Loading branch information
ThePlasticPotato committed Dec 22, 2024
1 parent 181b1fb commit 93cc755
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ import org.valkyrienskies.core.api.ships.ServerShip
import org.valkyrienskies.core.api.ships.Ship
import org.valkyrienskies.core.api.ships.getAttachment
import org.valkyrienskies.core.impl.game.ShipTeleportDataImpl
import org.valkyrienskies.core.util.datastructures.DenseBlockPosSet
import org.valkyrienskies.mod.common.BlockStateInfo.onSetBlock
import org.valkyrienskies.mod.common.dimensionId
import org.valkyrienskies.mod.common.getShipObjectManagingPos
import org.valkyrienskies.mod.common.shipObjectWorld
import org.valkyrienskies.mod.common.util.SplittingDisablerAttachment
import org.valkyrienskies.mod.common.util.toBlockPos

object ShipAssembler {

Expand All @@ -34,14 +32,14 @@ object ShipAssembler {
}


fun assembleToShip(level: Level, blocks: DenseBlockPosSet, removeOriginal: Boolean, scale: Double = 1.0, shouldDisableSplitting: Boolean = false): ServerShip {
fun assembleToShip(level: Level, blocks: List<BlockPos>, removeOriginal: Boolean, scale: Double = 1.0, shouldDisableSplitting: Boolean = false): ServerShip {
assert(level is ServerLevel) { "Can't create ships clientside!" }
val sLevel: ServerLevel = level as ServerLevel
if (blocks.isEmpty()) {
throw IllegalArgumentException("No blocks to assemble.")
}

val existingShip = sLevel.getShipObjectManagingPos(blocks.find { !sLevel.getBlockState(it.toBlockPos()).isAir }?.toBlockPos() ?: throw IllegalArgumentException())
val existingShip = sLevel.getShipObjectManagingPos(blocks.find { !sLevel.getBlockState(it).isAir } ?: throw IllegalArgumentException())

var existingShipCouldSplit = true
var structureCornerMin: BlockPos? = null
Expand All @@ -50,13 +48,13 @@ object ShipAssembler {

// Calculate bounds of the area containing all blocks adn check for solids and invalid blocks
for (itPos in blocks) {
if (isValidShipBlock(level.getBlockState(itPos.toBlockPos()))) {
if (isValidShipBlock(level.getBlockState(itPos))) {
if (structureCornerMin == null) {
structureCornerMin = itPos.toBlockPos()
structureCornerMax = itPos.toBlockPos()
structureCornerMin = itPos
structureCornerMax = itPos
} else {
structureCornerMin = AssemblyUtil.getMinCorner(structureCornerMin!!, itPos.toBlockPos())
structureCornerMax = AssemblyUtil.getMaxCorner(structureCornerMax!!, itPos.toBlockPos())
structureCornerMin = AssemblyUtil.getMinCorner(structureCornerMin!!, itPos)
structureCornerMax = AssemblyUtil.getMaxCorner(structureCornerMax!!, itPos)
}
hasSolids = true
}
Expand Down Expand Up @@ -89,28 +87,28 @@ object ShipAssembler {
// Copy blocks and check if the center block got replaced (is default a stone block)
var centerBlockReplaced = false
for (itPos in blocks) {
if (isValidShipBlock(level.getBlockState(itPos.toBlockPos()))) {
val relative: BlockPos = itPos.toBlockPos().subtract( BlockPos(shipOGPos.x(),shipOGPos.y(),shipOGPos.z()))
if (isValidShipBlock(level.getBlockState(itPos))) {
val relative: BlockPos = itPos.subtract( BlockPos(shipOGPos.x(),shipOGPos.y(),shipOGPos.z()))
val shipPos: BlockPos = shipBlockPos.offset(relative)
AssemblyUtil.copyBlock(level, itPos.toBlockPos(), shipPos)
AssemblyUtil.copyBlock(level, itPos, shipPos)
if (relative.equals(BlockPos.ZERO)) centerBlockReplaced = true
}
}

// Remove original blocks
if (removeOriginal) {
for (itPos in blocks) {
if (isValidShipBlock(level.getBlockState(itPos.toBlockPos()))) {
AssemblyUtil.removeBlock(level, itPos.toBlockPos())
if (isValidShipBlock(level.getBlockState(itPos))) {
AssemblyUtil.removeBlock(level, itPos)
}
}
}

// Trigger updates on both ships
for (itPos in blocks) {
val relative: BlockPos = itPos.toBlockPos().subtract(BlockPos(shipOGPos.x(),shipOGPos.y(),shipOGPos.z()))
val relative: BlockPos = itPos.subtract(BlockPos(shipOGPos.x(),shipOGPos.y(),shipOGPos.z()))
val shipPos: BlockPos = shipBlockPos.offset(relative)
AssemblyUtil.updateBlock(level,itPos.toBlockPos(),shipPos,level.getBlockState(shipPos))
AssemblyUtil.updateBlock(level,itPos,shipPos,level.getBlockState(shipPos))
}

val shipCenterPos = ((newShip as ServerShip).inertiaData.centerOfMassInShip).add(0.5, 0.5, 0.5, Vector3d())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,154 @@ package org.valkyrienskies.mod.common.assembly

import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Clearable
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate
import org.joml.Vector3d
import org.valkyrienskies.core.api.ships.ServerShip
import org.valkyrienskies.core.apigame.ShipTeleportData
import org.valkyrienskies.core.impl.game.ShipTeleportDataImpl
import org.valkyrienskies.core.impl.game.ships.ShipData
import org.valkyrienskies.core.impl.game.ships.ShipTransformImpl
import org.valkyrienskies.core.util.datastructures.DenseBlockPosSet
import org.valkyrienskies.mod.common.util.toBlockPos
import org.valkyrienskies.mod.common.dimensionId
import org.valkyrienskies.mod.common.executeIf
import org.valkyrienskies.mod.common.isTickingChunk
import org.valkyrienskies.mod.common.shipObjectWorld
import org.valkyrienskies.mod.common.util.toJOML
import org.valkyrienskies.mod.common.util.toJOMLD
import org.valkyrienskies.mod.common.util.toMinecraft
import org.valkyrienskies.mod.util.relocateBlock
import org.valkyrienskies.mod.util.updateBlock

@Deprecated("Use [ShipAssembler.assembleToShip] instead")
fun createNewShipWithBlocks(
centerBlock: BlockPos, blocks: DenseBlockPosSet, level: ServerLevel
): ServerShip {
if (blocks.isEmpty()) throw IllegalArgumentException()
return ShipAssembler.assembleToShip(level, blocks, true, 1.0)
//return ShipAssembler.assembleToShip(level, blocks, true, 1.0)


val ship = level.shipObjectWorld.createNewShipAtBlock(centerBlock.toJOML(), false, 1.0, level.dimensionId)
val shipChunkX = ship.chunkClaim.xMiddle
val shipChunkZ = ship.chunkClaim.zMiddle
val worldChunkX = centerBlock.x shr 4
val worldChunkZ = centerBlock.z shr 4
val deltaX = worldChunkX - shipChunkX
val deltaZ = worldChunkZ - shipChunkZ
val chunksToBeUpdated = mutableMapOf<ChunkPos, Pair<ChunkPos, ChunkPos>>()
blocks.forEachChunk { x, _, z, _ ->
val sourcePos = ChunkPos(x, z)
val destPos = ChunkPos(x - deltaX, z - deltaZ)
chunksToBeUpdated[sourcePos] = Pair(sourcePos, destPos)
}
val chunkPairs = chunksToBeUpdated.values.toList()
val chunkPoses = chunkPairs.flatMap { it.toList() }
val chunkPosesJOML = chunkPoses.map { it.toJOML() }
// Send a list of all the chunks that we plan on updating to players, so that they
// defer all updates until assembly is finished
// with(vsCore.simplePacketNetworking) {
// PacketStopChunkUpdates(chunkPosesJOML).sendToAllClients()
// }
// Use relocateBlock to copy all the blocks into the ship
blocks.forEachChunk { chunkX, chunkY, chunkZ, chunk ->
val sourceChunk = level.getChunk(chunkX, chunkZ)
val destChunk = level.getChunk(chunkX - deltaX, chunkZ - deltaZ)
chunk.forEach { x, y, z ->
val fromPos = BlockPos((sourceChunk.pos.x shl 4) + x, (chunkY shl 4) + y, (sourceChunk.pos.z shl 4) + z)
val toPos = BlockPos((destChunk.pos.x shl 4) + x, (chunkY shl 4) + y, (destChunk.pos.z shl 4) + z)
relocateBlock(sourceChunk, fromPos, destChunk, toPos, false, ship)
}
}
// Use updateBlock to update blocks after copying
blocks.forEachChunk { chunkX, chunkY, chunkZ, chunk ->
val sourceChunk = level.getChunk(chunkX, chunkZ)
val destChunk = level.getChunk(chunkX - deltaX, chunkZ - deltaZ)
chunk.forEach { x, y, z ->
val fromPos = BlockPos((sourceChunk.pos.x shl 4) + x, (chunkY shl 4) + y, (sourceChunk.pos.z shl 4) + z)
val toPos = BlockPos((destChunk.pos.x shl 4) + x, (chunkY shl 4) + y, (destChunk.pos.z shl 4) + z)
updateBlock(destChunk.level, fromPos, toPos, destChunk.getBlockState(toPos))
}
}
// Calculate the position of the block that the player clicked after it has been assembled
val centerInShip = Vector3d(
((shipChunkX shl 4) + (centerBlock.x and 15)).toDouble(),
centerBlock.y.toDouble(),
((shipChunkZ shl 4) + (centerBlock.z and 15)).toDouble()
)
// The ship's position has shifted from the center block since we assembled the ship, compensate for that
val centerBlockPosInWorld = ship.inertiaData.centerOfMassInShip.sub(centerInShip, Vector3d())
.add(ship.transform.positionInWorld)
// Put the ship into the compensated position, so that all the assembled blocks stay in the same place
// TODO: AAAAAAAAA THIS IS HORRIBLE how can the API support this?
(ship as ShipData).transform = (ship.transform as ShipTransformImpl).copy(positionInWorld = centerBlockPosInWorld)
level.server.executeIf(
// This condition will return true if all modified chunks have been both loaded AND
// chunk update packets were sent to players
{ chunkPoses.all(level::isTickingChunk) }
) {
// Once all the chunk updates are sent to players, we can tell them to restart chunk updates
// with(vsCore.simplePacketNetworking) {
// PacketRestartChunkUpdates(chunkPosesJOML).sendToAllClients()
// }
}

return ship
}

fun createNewShipWithStructure(
lowerCorner: BlockPos, higherCorner: BlockPos, blocks: StructureTemplate, level: ServerLevel
): ServerShip {
//if (blocks.size.toJOML().length() < 0.0001) throw IllegalArgumentException()

val ship = level.shipObjectWorld.createNewShipAtBlock(lowerCorner.toJOML(), false, 1.0, level.dimensionId)
val shipChunkX = ship.chunkClaim.xMiddle
val shipChunkZ = ship.chunkClaim.zMiddle

// Calculate the position of the block that the player clicked after it has been assembled
val lowerCornerInShip = Vector3d(
((shipChunkX shl 4) + (lowerCorner.x and 15)).toDouble(),
lowerCorner.y.toDouble(),
((shipChunkZ shl 4) + (lowerCorner.z and 15)).toDouble()
)
val higherCornerInShip = Vector3d(
((shipChunkX shl 4) + (higherCorner.x and 15)).toDouble(),
higherCorner.y.toDouble(),
((shipChunkZ shl 4) + (higherCorner.z and 15)).toDouble()
)

blocks.placeInWorld(level, BlockPos(lowerCornerInShip.toMinecraft()), BlockPos(lowerCornerInShip.toMinecraft()), StructurePlaceSettings(), level.random, Block.UPDATE_ALL)

val diff = higherCorner.subtract(lowerCorner)
val centerPos = lowerCorner.offset(diff.x / 2, diff.y / 2, diff.z / 2)

// The ship's position has shifted from the center block since we assembled the ship, compensate for that
val centerBlockPosInWorld = ship.inertiaData.centerOfMassInShip.sub(lowerCornerInShip, Vector3d())
.add(ship.transform.positionInWorld)
// Put the ship into the compensated position, so that all the assembled blocks stay in the same place
// TODO: AAAAAAAAA THIS IS HORRIBLE how can the API support this?
//(ship as ShipData).transform = (ship.transform as ShipTransformImpl).copy(positionInWorld = centerBlockPosInWorld)
level.shipObjectWorld
.teleportShip(ship, ShipTeleportDataImpl(newPos = centerBlockPosInWorld.add(0.0, (128.0 - lowerCorner.y.toDouble() + 0.5), 0.0, Vector3d()), newPosInShip = ship.inertiaData.centerOfMassInShip))


for (x in lowerCorner.x..higherCorner.x) {
for (y in lowerCorner.y..higherCorner.y) {
for (z in lowerCorner.z..higherCorner.z) {
if (!level.getBlockState(BlockPos(x, y, z)).isAir) {
val blockEntity: BlockEntity? = level.getBlockEntity(BlockPos(x, y, z))
Clearable.tryClear(blockEntity)
level.removeBlockEntity(BlockPos(x, y, z))
level.setBlock(BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS)

//level.getChunk(BlockPos(x, y, z)).setBlockState(BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false)
}
}
}
}
return ship
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ package org.valkyrienskies.mod.common.item
import net.minecraft.Util
import net.minecraft.core.BlockPos
import net.minecraft.core.Vec3i
import net.minecraft.gametest.framework.StructureUtils
import net.minecraft.network.chat.TextComponent
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.InteractionResult
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate
import org.joml.primitives.AABBi
import org.valkyrienskies.core.util.datastructures.DenseBlockPosSet
import org.valkyrienskies.mod.common.assembly.ShipAssembler
import org.valkyrienskies.mod.common.assembly.createNewShipWithBlocks
import org.valkyrienskies.mod.common.assembly.createNewShipWithStructure
import org.valkyrienskies.mod.common.dimensionId
import org.valkyrienskies.mod.common.getShipManagingPos
import org.valkyrienskies.mod.common.getShipObjectManagingPos
import org.valkyrienskies.mod.common.shipObjectWorld
import org.valkyrienskies.mod.common.util.toJOML
Expand Down Expand Up @@ -53,20 +59,15 @@ class AreaAssemblerItem(
} else {
val blockAABB = AABBi(blockPos.toJOML(), Vec3i(firstPosX, firstPosY, firstPosZ).toJOML())
blockAABB.correctBounds()
val blocks = ArrayList<BlockPos>()
val lowerCorner = BlockPos(blockAABB.minX, blockAABB.minY, blockAABB.minZ)
val upperCorner = BlockPos(blockAABB.maxX, blockAABB.maxY, blockAABB.maxZ)

val structure = StructureTemplate()
structure.fillFromWorld(level, lowerCorner, upperCorner.offset(1, 1, 1).subtract(lowerCorner), true, Blocks.STRUCTURE_VOID)

for (x in blockAABB.minX..blockAABB.maxX) {
for (y in blockAABB.minY..blockAABB.maxY) {
for (z in blockAABB.minZ..blockAABB.maxZ) {
if (level.getBlockState(BlockPos(x, y, z)).isAir) {
continue
}
blocks.add(BlockPos(x, y, z))
}
}
}
ctx.player?.sendMessage(TextComponent("Assembling (${blockPos.x}, ${blockPos.y}, ${blockPos.z}) to ($firstPosX, $firstPosY, $firstPosZ)!"), Util.NIL_UUID)
ShipAssembler.assembleToShip(level, blocks, true, scale.asDouble, true)
//ShipAssembler.assembleToShip(level, blocks, true, scale.asDouble, true)
createNewShipWithStructure(lowerCorner, upperCorner, structure, level)
}
item.tag!!.remove("firstPosX")
item.tag!!.remove("firstPosY")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.valkyrienskies.core.api.world.connectivity.ConnectionStatus.CONNECTED
import org.valkyrienskies.core.api.world.connectivity.ConnectionStatus.DISCONNECTED
import org.valkyrienskies.core.util.datastructures.DenseBlockPosSet
import org.valkyrienskies.core.util.expand
import org.valkyrienskies.mod.common.assembly.ShipAssembler
import org.valkyrienskies.mod.common.assembly.createNewShipWithBlocks
import org.valkyrienskies.mod.common.dimensionId
import org.valkyrienskies.mod.common.getShipObjectManagingPos
import org.valkyrienskies.mod.common.shipObjectWorld
Expand Down Expand Up @@ -131,7 +131,8 @@ class SplitHandler(private val doEdges: Boolean, private val doCorners: Boolean)
}

for (component in toAssemble) {
ShipAssembler.assembleToShip(level, component, true, 1.0, true)
//ShipAssembler.assembleToShip(level, component, true, 1.0, true)
createNewShipWithBlocks(component.first().toBlockPos(), component, level)
}

loadedShip.getAttachment(SplittingDisablerAttachment::class.java)?.enableSplitting()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=1G
org.gradle.jvmargs=-Xmx20G -XX:MaxMetaspaceSize=1G
minecraft_version=1.18.2
enabled_platforms=quilt,fabric,forge
archives_base_name=valkyrienskies-118
Expand Down

0 comments on commit 93cc755

Please sign in to comment.