Skip to content

Commit

Permalink
Slapped in M_Marvin's assembly code (ported by Fern), might need adju…
Browse files Browse the repository at this point in the history
…sting
  • Loading branch information
ThePlasticPotato committed May 27, 2024
1 parent b2211ee commit 98a14ab
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.valkyrienskies.mod.common.assembly


import com.mojang.math.Vector3d
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.util.Mth
import net.minecraft.world.level.Level
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.chunk.LevelChunk
import net.minecraft.world.ticks.ScheduledTick
import org.joml.Vector3i

private val AIR = Blocks.AIR.defaultBlockState()
object AssemblyUtil {

fun setBlock(level: Level, pos: BlockPos, state: BlockState?) {
val chunk = level.getChunk(pos) as LevelChunk
val section = chunk.getSection(chunk.getSectionIndex(pos.y))
val oldState = level.getBlockState(pos)
section.setBlockState(pos.x and 15, pos.y and 15, pos.z and 15, state)
ShipAssembler.triggerBlockChange(level, pos, oldState, state)
}

fun removeBlock(level: Level, pos: BlockPos) {
level.removeBlockEntity(pos)
setBlock(level, pos, Blocks.AIR.defaultBlockState())
}

fun copyBlock(level: Level, from: BlockPos?, to: BlockPos) {
val state = level.getBlockState(from)
val blockentity = level.getBlockEntity(from)
setBlock(level, to, state)

// Transfer pending schedule-ticks
if (level.blockTicks.hasScheduledTick(from, state.block)) {
level.blockTicks.schedule(ScheduledTick<Block?>(state.block, to, 0, 0))
}

// Transfer block-entity data
if (state.hasBlockEntity() && blockentity != null) {
val data: CompoundTag = blockentity.saveWithId()
level.setBlockEntity(blockentity)
val newBlockentity = level.getBlockEntity(to)
newBlockentity?.load(data)
}
}

fun updateBlock(level: Level, fromPos: BlockPos, toPos: BlockPos, toState: BlockState) {

// 75 = flag 1 (block update) & flag 2 (send to clients) + flag 8 (force rerenders)
val flags = 11

//updateNeighbourShapes recurses through nearby blocks, recursionLeft is the limit
val recursionLeft = 511

level.setBlocksDirty(fromPos, toState, AIR)
level.sendBlockUpdated(fromPos, toState, AIR, flags)
level.blockUpdated(fromPos, AIR.block)
// This handles the update for neighboring blocks in worldspace
AIR.updateIndirectNeighbourShapes(level, fromPos, flags, recursionLeft - 1)
AIR.updateNeighbourShapes(level, fromPos, flags, recursionLeft)
AIR.updateIndirectNeighbourShapes(level, fromPos, flags, recursionLeft)
//This updates lighting for blocks in worldspace
level.chunkSource.lightEngine.checkBlock(fromPos)

level.setBlocksDirty(toPos, AIR, toState)
level.sendBlockUpdated(toPos, AIR, toState, flags)
level.blockUpdated(toPos, toState.block)
if (!level.isClientSide && toState.hasAnalogOutputSignal()) {
level.updateNeighbourForOutputSignal(toPos, toState.block)
}
//This updates lighting for blocks in shipspace
level.chunkSource.lightEngine.checkBlock(toPos)
}

fun toBlockPos(x: Double, y: Double, z: Double): BlockPos {
return BlockPos(Mth.floor(x), Math.floor(y).toInt(), Math.floor(z).toInt())
}

fun toBlockPos(vec: Vector3d): BlockPos {
return toBlockPos(vec.x, vec.y, vec.z)
}

fun getVecDirection(v: Vector3i): Direction {
var axis = Direction.Axis.X
if (v.y() != 0) axis = Direction.Axis.Y
if (v.z() != 0) axis = Direction.Axis.Z
val direction = if (v.x + v.y + v.z > 0) Direction.AxisDirection.POSITIVE else Direction.AxisDirection.NEGATIVE
return Direction.fromAxisAndDirection(axis, direction)
}

fun getMinCorner(pos1: BlockPos, pos2: BlockPos): BlockPos {
return BlockPos(
Math.min(pos1.x, pos2.x),
Math.min(pos1.y, pos2.y),
Math.min(pos1.z, pos2.z)
)
}

fun getMaxCorner(pos1: BlockPos, pos2: BlockPos): BlockPos {
return BlockPos(
Math.max(pos1.x, pos2.x),
Math.max(pos1.y, pos2.y),
Math.max(pos1.z, pos2.z)
)
}

fun getMiddle(pos1: BlockPos, pos2: BlockPos): Vector3i {
val middleX = Math.min(pos1.x, pos2.x).toDouble() + (Math.max(pos1.x, pos2.x) - Math.min(
pos1.x,
pos2.x
) + 1) / 2
val middleY = Math.min(pos1.y, pos2.y).toDouble() + (Math.max(pos1.y, pos2.y) - Math.min(
pos1.y,
pos2.y
) + 1) / 2
val middleZ = Math.min(pos1.z, pos2.z).toDouble() + (Math.max(pos1.z, pos2.z) - Math.min(
pos1.z,
pos2.z
) + 1) / 2
return Vector3i(middleX.toInt(), middleY.toInt(), middleZ.toInt())
}

fun getMiddle(pos1: Vector3d, pos2: Vector3d): Vector3d {
val middleX = Math.min(pos1.x, pos2.x) + (Math.max(pos1.x, pos2.x) - Math.min(
pos1.x,
pos2.x
)) / 2.0
val middleY = Math.min(pos1.y, pos2.y) + (Math.max(pos1.y, pos2.y) - Math.min(
pos1.y,
pos2.y
)) / 2.0
val middleZ = Math.min(pos1.z, pos2.z) + (Math.max(pos1.z, pos2.z) - Math.min(
pos1.z,
pos2.z
)) / 2.0
return Vector3d(middleX, middleY, middleZ)
}





}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.valkyrienskies.mod.common.assembly



import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.state.BlockState
import org.joml.Vector3d
import org.joml.Vector3i
import org.valkyrienskies.core.api.ships.ServerShip
import org.valkyrienskies.core.api.ships.Ship
import org.valkyrienskies.core.impl.game.ShipTeleportDataImpl
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

object ShipAssembler {

fun triggerBlockChange(level: Level?, pos: BlockPos?, prevState: BlockState?, newState: BlockState?) {
onSetBlock(level!!, pos!!, prevState!!, newState!!)
}

fun isValidShipBlock(state: BlockState?): Boolean {
if (state != null) {
//return !state.tags.anyMatch { it== VsShipAssemblerTags.FORBIDDEN_ASSEMBLE }
}
return true
}


fun assembleToShip(level: Level, blocks: List<BlockPos>, removeOriginal: Boolean, scale: Double): Boolean {
assert(level is ServerLevel) { "Can't manage contraptions on client side!" }
val sLevel: ServerLevel = level as ServerLevel
if (blocks.isEmpty()) {
return false
}

var structureCornerMin: BlockPos = blocks[0]
var structureCornerMax: BlockPos = blocks[0]
var hasSolids = false

// Calculate bounds of the area containing all blocks adn check for solids and invalid blocks
for (itPos in blocks) {
if (isValidShipBlock(level.getBlockState(itPos))) {
structureCornerMin = AssemblyUtil.getMinCorner(structureCornerMin, itPos)
structureCornerMax = AssemblyUtil.getMaxCorner(structureCornerMax, itPos)
hasSolids = true
}
}
if (!hasSolids) return false

// Create new contraption at center of bounds
val contraptionWorldPos: Vector3i = AssemblyUtil.getMiddle(structureCornerMin, structureCornerMax)
//val contraptionPosition = ContraptionPosition(Quaterniond(Vec3d(0.0, 1.0, 1.0), 0.0), contraptionWorldPos, null)

val newShip: Ship = (level as ServerLevel).server.shipObjectWorld
.createNewShipAtBlock(contraptionWorldPos, false, scale, level.dimensionId)

// Stone for safety reasons

val contraptionShipPos = newShip.worldToShip.transformPosition(Vector3d(contraptionWorldPos.x.toDouble(),contraptionWorldPos.y.toDouble(),contraptionWorldPos.z.toDouble()))
val contraptionBlockPos = BlockPos(contraptionShipPos.x.toInt(),contraptionShipPos.y.toInt(),contraptionShipPos.z.toInt())


// 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))) {
val relative: BlockPos = itPos.subtract( BlockPos(contraptionWorldPos.x,contraptionWorldPos.y,contraptionWorldPos.z))
val shipPos: BlockPos = contraptionBlockPos.offset(relative)
AssemblyUtil.copyBlock(level, itPos, shipPos)
if (relative == BlockPos.ZERO) centerBlockReplaced = true
}
}

// If center block got not replaced, remove the stone block
if (!centerBlockReplaced) {
level.setBlock(contraptionBlockPos, Blocks.AIR.defaultBlockState(), 3)
}

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

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


sLevel.server.shipObjectWorld
.teleportShip(newShip as ServerShip, ShipTeleportDataImpl(Vector3d(contraptionWorldPos.x.toDouble(),contraptionWorldPos.y.toDouble(),contraptionWorldPos.z.toDouble())))

return true
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.valkyrienskies.mod.common.util.toJOML
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.valkyrienskies.mod.common.util.toJOMLD
import org.valkyrienskies.mod.util.relocateBlock
import org.valkyrienskies.mod.util.updateBlock

@Deprecated("Use ShipAssembler.assembleToShip instead")
fun splitShip(centerBlock: BlockPos, blocks: DenseBlockPosSet, level: ServerLevel, originalShip: ServerShip): ServerShip {
if (blocks.isEmpty()) throw IllegalArgumentException()

Expand Down

0 comments on commit 98a14ab

Please sign in to comment.