Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 570a948

Browse files
committed
refactor!: convert Site-63 to Structure (Part 1)
1 parent c92e26c commit 570a948

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+515
-1165
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package scpsharp.content.facility
2+
3+
import net.minecraft.registry.Registries
4+
import net.minecraft.registry.tag.TagKey
5+
import net.minecraft.structure.StructurePiece
6+
import net.minecraft.structure.StructurePieceType
7+
import net.minecraft.structure.StructurePiecesCollector
8+
import net.minecraft.structure.StructurePiecesHolder
9+
import net.minecraft.util.math.BlockBox
10+
import net.minecraft.util.math.BlockPos
11+
import net.minecraft.util.math.Direction
12+
import net.minecraft.world.gen.structure.Structure
13+
import kotlin.jvm.optionals.getOrNull
14+
15+
class FacilityGenerator(val ctx: Structure.Context) : StructurePiecesHolder {
16+
17+
val pieces = StackAllocator<StructurePiece> { a, b -> a.boundingBox.intersects(b.boundingBox) }
18+
val depth by pieces::depth
19+
20+
inline fun piece(crossinline content: FacilityGenerator.() -> Boolean): Boolean {
21+
pieces.push()
22+
return try {
23+
if (content()) {
24+
pieces.squash()
25+
true
26+
} else {
27+
pieces.drop()
28+
false
29+
}
30+
} catch (e: Throwable) {
31+
pieces.drop()
32+
false
33+
}
34+
}
35+
36+
fun add(piece: StructurePiece) = pieces.tryAllocate(piece)
37+
38+
override fun addPiece(piece: StructurePiece) {
39+
pieces.allocate(piece)
40+
}
41+
42+
override fun getIntersecting(box: BlockBox) = pieces.find { it.boundingBox.intersects(box) }
43+
44+
fun collect(collector: StructurePiecesCollector) {
45+
pieces.freeze()
46+
check(depth == 1) { "Trying to collect structure pieces when there are more than one layers" }
47+
val piecesIter = pieces.iterator()
48+
check(piecesIter.hasNext()) { "Nothing to collect" }
49+
piecesIter.forEach { collector.addPiece(it) }
50+
}
51+
52+
fun random(tag: TagKey<StructurePieceType>, pos: BlockPos, direction: Direction) =
53+
Registries.STRUCTURE_PIECE.getOrCreateEntryList(tag)
54+
.toList()
55+
.run {
56+
if (isEmpty()) error("Nothing registered in $tag")
57+
val randomEntry = elementAt(ctx.random.nextInt(size))!!
58+
val random = randomEntry.keyOrValue
59+
val value = random.left().getOrNull()?.value?.let { Registries.STRUCTURE_PIECE.get(it) }
60+
?: random.right().get()
61+
if (value is FacilityStructurePiece.Type) {
62+
value.invoke(this@FacilityGenerator, pos, direction)
63+
} else {
64+
error("$tag registered $randomEntry but is SCP# incompatible structure pieces")
65+
}
66+
}
67+
68+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package scpsharp.content.facility
2+
3+
import com.mojang.datafixers.util.Either
4+
import net.minecraft.structure.StructurePiecesCollector
5+
import net.minecraft.util.math.BlockPos
6+
import net.minecraft.util.math.Direction
7+
import net.minecraft.world.Heightmap
8+
import net.minecraft.world.gen.structure.Structure
9+
import java.util.*
10+
11+
abstract class FacilityStructure(config: Config) : Structure(config) {
12+
13+
companion object {
14+
15+
@JvmStatic
16+
protected fun getStructurePosition(
17+
ctx: Context,
18+
heightmap: Heightmap.Type = Heightmap.Type.WORLD_SURFACE_WG,
19+
generator: FacilityGenerator.(pos: BlockPos, direction: Direction) -> Boolean
20+
): Optional<StructurePosition> {
21+
val chunkPos = ctx.chunkPos()
22+
val y = ctx.chunkGenerator()
23+
.getHeightInGround(
24+
chunkPos.centerX, chunkPos.centerZ, heightmap,
25+
ctx.world(), ctx.noiseConfig()
26+
)
27+
val pos = BlockPos(chunkPos.centerX, y, chunkPos.centerZ)
28+
val collector = StructurePiecesCollector()
29+
val generatorContext = FacilityGenerator(ctx)
30+
return if (generatorContext.generator(pos, Direction.random(ctx.random))) {
31+
generatorContext.collect(collector)
32+
Optional.of(StructurePosition(pos, Either.right(collector)))
33+
} else {
34+
Optional.empty()
35+
}
36+
}
37+
38+
}
39+
40+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package scpsharp.content.facility
2+
3+
import net.minecraft.nbt.NbtCompound
4+
import net.minecraft.structure.StructureContext
5+
import net.minecraft.structure.StructurePiece
6+
import net.minecraft.structure.StructurePieceType
7+
import net.minecraft.util.math.BlockBox
8+
import net.minecraft.util.math.BlockPos
9+
import net.minecraft.util.math.ChunkPos
10+
import net.minecraft.util.math.Direction
11+
import net.minecraft.util.math.random.Random
12+
import net.minecraft.world.StructureWorldAccess
13+
import net.minecraft.world.gen.StructureAccessor
14+
import net.minecraft.world.gen.chunk.ChunkGenerator
15+
16+
abstract class FacilityStructurePiece : StructurePiece {
17+
18+
constructor(type: StructurePieceType, length: Int, boundingBox: BlockBox) : super(type, length, boundingBox)
19+
constructor(type: StructurePieceType, nbt: NbtCompound) : super(type, nbt)
20+
21+
abstract override fun writeNbt(context: StructureContext, nbt: NbtCompound)
22+
23+
abstract override fun generate(
24+
world: StructureWorldAccess,
25+
structureAccessor: StructureAccessor,
26+
chunkGenerator: ChunkGenerator,
27+
random: Random,
28+
chunkBox: BlockBox,
29+
chunkPos: ChunkPos,
30+
pivot: BlockPos
31+
)
32+
33+
interface Type : StructurePieceType {
34+
35+
override fun load(context: StructureContext, nbt: NbtCompound): StructurePiece
36+
37+
operator fun invoke(generator: FacilityGenerator, pos: BlockPos, direction: Direction): Boolean
38+
39+
}
40+
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package scpsharp.content.facility
2+
3+
import net.minecraft.block.Block
4+
import net.minecraft.registry.Registries
5+
import net.minecraft.registry.RegistryKeys
6+
import net.minecraft.registry.tag.TagKey
7+
import scpsharp.util.id
8+
9+
object FacilityTags {
10+
11+
val FACILITY_KEEP: TagKey<Block> = TagKey.of(Registries.BLOCK.key, id("facility_keep"))
12+
13+
val START = TagKey.of(RegistryKeys.STRUCTURE_PIECE, id("facility/start"))!!
14+
val PART = TagKey.of(RegistryKeys.STRUCTURE_PIECE, id("facility/part"))!!
15+
val CONTAINMENT_ROOM = TagKey.of(RegistryKeys.STRUCTURE_PIECE, id("facility/containment_room"))!!
16+
val CORRIDOR = TagKey.of(RegistryKeys.STRUCTURE_PIECE, id("facility/corridor"))!!
17+
val CROSSING = TagKey.of(RegistryKeys.STRUCTURE_PIECE, id("facility/crossing"))!!
18+
19+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (C) 2023 SCPSharp Team
3+
*
4+
* This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. See LICENSE file for more.
5+
*/
6+
package scpsharp.content.facility
7+
8+
import java.util.*
9+
10+
class StackAllocator<T>(val conflictMatcher: (T, T) -> Boolean) : Iterable<T> {
11+
12+
private val layers = Stack<MutableSet<T>>()
13+
14+
@Transient
15+
var frozen = false
16+
private set
17+
val active get() = !frozen
18+
19+
init {
20+
push()
21+
}
22+
23+
val topLayer get() = layers.peek()!!
24+
val depth get() = layers.size
25+
26+
private fun checkActive() {
27+
check(!frozen) { "Trying to operate a frozen SpaceAllocator" }
28+
}
29+
30+
fun freeze() {
31+
checkActive()
32+
frozen = true
33+
}
34+
35+
fun push() {
36+
checkActive()
37+
layers.push(mutableSetOf())
38+
}
39+
40+
fun drop() {
41+
checkActive()
42+
check(layers.size > 1) { "Could not drop the last layer" }
43+
layers.pop()
44+
}
45+
46+
fun squash() {
47+
checkActive()
48+
check(layers.size > 1) { "Could not squash the last layer" }
49+
val elements = layers.pop()!!
50+
topLayer += elements
51+
}
52+
53+
fun allocate(value: T) {
54+
check(tryAllocate(value)) { "Failed to allocate $value" }
55+
}
56+
57+
fun tryAllocate(value: T): Boolean {
58+
checkActive()
59+
if (layers.any { layer -> layer.any { conflictMatcher(value, it) } }) {
60+
return false
61+
}
62+
return topLayer.add(value)
63+
}
64+
65+
fun release(value: T) {
66+
checkActive()
67+
check(topLayer.remove(value)) { error("Element is not allocated on the top layer: $value") }
68+
}
69+
70+
override fun iterator() = object : Iterator<T> {
71+
val layersIter = layers.iterator()
72+
var elementIter: Iterator<T>? = null
73+
74+
private fun seekToNext() {
75+
while (elementIter?.hasNext() != true) {
76+
if (layersIter.hasNext()) {
77+
elementIter = layersIter.next()!!.iterator()
78+
} else {
79+
break
80+
}
81+
}
82+
}
83+
84+
override fun hasNext(): Boolean {
85+
seekToNext()
86+
return elementIter?.hasNext() ?: false
87+
}
88+
89+
override fun next(): T {
90+
seekToNext()
91+
return elementIter!!.next()
92+
}
93+
}
94+
95+
}

0 commit comments

Comments
 (0)