Skip to content

Commit 5424da0

Browse files
committed
check both current block and neighboring blocks to ensure potential replacement places are scanned. Slabs for example are only replaceable under a more specific context than a simple isReplaceable check
1 parent 5cf47e0 commit 5424da0

File tree

3 files changed

+84
-74
lines changed

3 files changed

+84
-74
lines changed

src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PlaceChecker.kt

Lines changed: 81 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ import com.lambda.interaction.request.rotating.visibilty.lookInDirection
4646
import com.lambda.threading.runSafeAutomated
4747
import com.lambda.util.BlockUtils
4848
import com.lambda.util.BlockUtils.blockState
49-
import com.lambda.util.BlockUtils.isEmpty
5049
import com.lambda.util.item.ItemStackUtils.inventoryIndex
5150
import com.lambda.util.item.ItemUtils.blockItem
5251
import com.lambda.util.math.distSq
5352
import com.lambda.util.math.vec3d
5453
import com.lambda.util.player.copyPlayer
5554
import com.lambda.util.world.raycast.RayCastUtils.blockResult
55+
import kotlinx.coroutines.CoroutineScope
5656
import kotlinx.coroutines.Dispatchers
5757
import kotlinx.coroutines.cancel
5858
import kotlinx.coroutines.joinAll
@@ -69,6 +69,7 @@ import net.minecraft.item.Items
6969
import net.minecraft.util.Hand
7070
import net.minecraft.util.hit.BlockHitResult
7171
import net.minecraft.util.math.BlockPos
72+
import net.minecraft.util.math.Direction
7273
import net.minecraft.util.math.Vec3d
7374
import net.minecraft.util.shape.VoxelShapes
7475
import kotlin.math.pow
@@ -123,91 +124,98 @@ class PlaceChecker @SimCheckerDsl private constructor(simInfo: SimInfo)
123124

124125
supervisorScope {
125126
withContext(Dispatchers.Default) {
126-
preProcessing.sides.map { neighbor ->
127+
preProcessing.sides.map { side ->
127128
launch {
128-
val hitBlockPos = if (!placeConfig.airPlace.isEnabled && state.isEmpty)
129-
pos.offset(neighbor) else pos
130-
val hitSide = neighbor.opposite
131-
if (!world.worldBorder.contains(hitBlockPos)) return@launch
132-
133-
val voxelShape = blockState(hitBlockPos).getOutlineShape(world, hitBlockPos).let { outlineShape ->
134-
if (!outlineShape.isEmpty || !placeConfig.airPlace.isEnabled) outlineShape
135-
else VoxelShapes.fullCube()
136-
}
137-
if (voxelShape.isEmpty) return@launch
129+
val neighborPos = pos.offset(side)
130+
val neighborSide = side.opposite
131+
if (!placeConfig.airPlace.isEnabled)
132+
testBlock(neighborPos, neighborSide, this@supervisorScope)
133+
testBlock(pos, side, this@supervisorScope)
134+
}
135+
}.joinAll()
136+
}
137+
}
138+
139+
return true
140+
}
141+
142+
private suspend fun AutomatedSafeContext.testBlock(pos: BlockPos, side: Direction, supervisorScope: CoroutineScope) {
143+
if (!world.worldBorder.contains(pos)) return
144+
145+
val voxelShape = blockState(pos).getOutlineShape(world, pos).let { outlineShape ->
146+
if (!outlineShape.isEmpty || !placeConfig.airPlace.isEnabled) outlineShape
147+
else VoxelShapes.fullCube()
148+
}
149+
if (voxelShape.isEmpty) return
150+
151+
val boxes = voxelShape.boundingBoxes.map { it.offset(pos) }
152+
val verify: CheckedHit.() -> Boolean = {
153+
hit.blockResult?.blockPos == pos && hit.blockResult?.side == side
154+
}
138155

139-
val boxes = voxelShape.boundingBoxes.map { it.offset(hitBlockPos) }
140-
val verify: CheckedHit.() -> Boolean = {
141-
hit.blockResult?.blockPos == hitBlockPos && hit.blockResult?.side == hitSide
156+
val validHits = mutableListOf<CheckedHit>()
157+
val misses = mutableSetOf<Vec3d>()
158+
val reachSq = buildConfig.interactReach.pow(2)
159+
160+
withContext(Dispatchers.Default) {
161+
boxes.map { box ->
162+
launch {
163+
val sides = if (buildConfig.checkSideVisibility) {
164+
box.getVisibleSurfaces(eye).intersect(setOf(side))
165+
} else setOf(side)
166+
167+
scanSurfaces(box, sides, buildConfig.resolution, preProcessing.surfaceScan) { _, vec ->
168+
val distSquared = eye distSq vec
169+
if (distSquared > reachSq) {
170+
misses.add(vec)
171+
return@scanSurfaces
142172
}
143173

144-
val validHits = mutableListOf<CheckedHit>()
145-
val misses = mutableSetOf<Vec3d>()
146-
val reachSq = buildConfig.interactReach.pow(2)
147-
148-
boxes.map { box ->
149-
launch {
150-
val sides = if (buildConfig.checkSideVisibility) {
151-
box.getVisibleSurfaces(eye).intersect(setOf(hitSide))
152-
} else setOf(hitSide)
153-
154-
scanSurfaces(box, sides, buildConfig.resolution, preProcessing.surfaceScan) { _, vec ->
155-
val distSquared = eye distSq vec
156-
if (distSquared > reachSq) {
157-
misses.add(vec)
158-
return@scanSurfaces
159-
}
160-
161-
val newRotation = eye.rotationTo(vec)
162-
163-
val hit = if (buildConfig.strictRayCast) {
164-
val rayCast = newRotation.rayCast(buildConfig.interactReach, eye)
165-
when {
166-
rayCast != null && (!placeConfig.airPlace.isEnabled || eye distSq rayCast.pos <= distSquared) ->
167-
rayCast.blockResult
168-
169-
placeConfig.airPlace.isEnabled -> {
170-
val hitVec = newRotation.castBox(box, buildConfig.interactReach, eye)
171-
BlockHitResult(hitVec, hitSide, hitBlockPos, false)
172-
}
173-
174-
else -> null
175-
}
176-
} else {
177-
val hitVec = newRotation.castBox(box, buildConfig.interactReach, eye)
178-
BlockHitResult(hitVec, hitSide, hitBlockPos, false)
179-
} ?: return@scanSurfaces
180-
181-
val checked = CheckedHit(hit, newRotation, buildConfig.interactReach)
182-
if (!checked.verify()) return@scanSurfaces
183-
184-
validHits.add(checked)
174+
val newRotation = eye.rotationTo(vec)
175+
176+
val hit = if (buildConfig.strictRayCast) {
177+
val rayCast = newRotation.rayCast(buildConfig.interactReach, eye)
178+
when {
179+
rayCast != null && (!placeConfig.airPlace.isEnabled || eye distSq rayCast.pos <= distSquared) ->
180+
rayCast.blockResult
181+
182+
placeConfig.airPlace.isEnabled -> {
183+
val hitVec = newRotation.castBox(box, buildConfig.interactReach, eye)
184+
BlockHitResult(hitVec, side, pos, false)
185185
}
186-
}
187-
}.joinAll()
188186

189-
if (validHits.isEmpty()) {
190-
if (misses.isNotEmpty()) {
191-
result(GenericResult.OutOfReach(pos, eye, misses))
192-
return@launch
187+
else -> null
193188
}
189+
} else {
190+
val hitVec = newRotation.castBox(box, buildConfig.interactReach, eye)
191+
BlockHitResult(hitVec, side, pos, false)
192+
} ?: return@scanSurfaces
194193

195-
result(GenericResult.NotVisible(pos, hitBlockPos, hitSide, eye.distanceTo(hitBlockPos.offset(hitSide).vec3d)))
196-
return@launch
197-
}
194+
val checked = CheckedHit(hit, newRotation, buildConfig.interactReach)
195+
if (!checked.verify()) return@scanSurfaces
198196

199-
if (swapStack.item == Items.AIR)
200-
this@supervisorScope.cancel()
201-
else if (!swapStack.item.isEnabled(world.enabledFeatures)) {
202-
result(PlaceResult.BlockFeatureDisabled(pos, swapStack))
203-
this@supervisorScope.cancel()
204-
} else selectHitPos(validHits)
197+
validHits.add(checked)
205198
}
206-
}.joinAll()
199+
}
200+
}.joinAll()
201+
}
202+
203+
if (validHits.isEmpty()) {
204+
if (misses.isNotEmpty()) {
205+
result(GenericResult.OutOfReach(pos, eye, misses))
206+
return
207207
}
208+
209+
result(GenericResult.NotVisible(pos, pos, side, eye.distanceTo(pos.offset(side).vec3d)))
210+
return
208211
}
209212

210-
return true
213+
if (swapStack.item == Items.AIR)
214+
supervisorScope.cancel()
215+
else if (!swapStack.item.isEnabled(world.enabledFeatures)) {
216+
result(PlaceResult.BlockFeatureDisabled(pos, swapStack))
217+
supervisorScope.cancel()
218+
} else selectHitPos(validHits)
211219
}
212220

213221
private fun AutomatedSafeContext.selectHitPos(validHits: List<CheckedHit>) {

src/main/kotlin/com/lambda/interaction/construction/simulation/checks/PostProcessingChecker.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class PostProcessingChecker @SimCheckerDsl private constructor(simInfo: SimInfo)
104104
val expectedState = state.with(Properties.OPEN, !state.get(Properties.OPEN))
105105
simInteraction(expectedState)
106106
}
107+
108+
Properties.SLAB_TYPE -> return false
107109
}
108110
}
109111

src/main/kotlin/com/lambda/interaction/request/rotating/visibilty/PointSelection.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ enum class PointSelection(
4141
"Choose the point closest to the average of all candidates (balanced and stable aim).",
4242
select = { hits ->
4343
val optimum = hits
44-
.mapNotNull { it.hit.pos }
44+
.mapNotNull { it?.hit?.pos }
4545
.reduceOrNull { acc, pos -> acc.add(pos) }
4646
?.times(1 / hits.size.toDouble())
4747

0 commit comments

Comments
 (0)