Compare commits
2 Commits
7a5a979169
...
a03c2d5eb4
Author | SHA1 | Date | |
---|---|---|---|
a03c2d5eb4 | |||
7ca0ba1ce5 |
@ -1,207 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.collect
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import net.minecraft.core.SectionPos
|
|
||||||
import net.minecraft.world.level.ChunkPos
|
|
||||||
import ru.dbotthepony.kommons.collect.iterateSetBits
|
|
||||||
import java.util.BitSet
|
|
||||||
import java.util.Collections
|
|
||||||
|
|
||||||
class BlockPosSet : MutableSet<BlockPos> {
|
|
||||||
private class Segment {
|
|
||||||
val bits = BitSet(16 * 16 * 16)
|
|
||||||
|
|
||||||
private fun pos2index(pos: BlockPos): Int {
|
|
||||||
val x = pos.x.and(15)
|
|
||||||
val z = pos.z.and(15) shl 4
|
|
||||||
val y = pos.y.and(15) shl 8
|
|
||||||
return x or y or z
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun index2pos(index: Int, gX: Int, gY: Int, gZ: Int): BlockPos {
|
|
||||||
val x = index.and(15) + gX
|
|
||||||
val z = index.ushr(4).and(15) + gZ
|
|
||||||
val y = index.ushr(8).and(15) + gY
|
|
||||||
return BlockPos(x, y, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun contains(pos: BlockPos): Boolean {
|
|
||||||
return bits[pos2index(pos)]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(pos: BlockPos): Boolean {
|
|
||||||
val index = pos2index(pos)
|
|
||||||
|
|
||||||
if (bits[index])
|
|
||||||
return false
|
|
||||||
|
|
||||||
bits[index] = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(pos: BlockPos): Boolean {
|
|
||||||
val index = pos2index(pos)
|
|
||||||
|
|
||||||
if (!bits[index])
|
|
||||||
return false
|
|
||||||
|
|
||||||
bits[index] = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun iterator(gX: Int, gY: Int, gZ: Int): MutableIterator<BlockPos> {
|
|
||||||
return object : MutableIterator<BlockPos> {
|
|
||||||
private val iterator = bits.iterateSetBits(4096)
|
|
||||||
private var last = -1
|
|
||||||
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
return iterator.hasNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun next(): BlockPos {
|
|
||||||
last = iterator.nextInt()
|
|
||||||
return index2pos(last, gX, gY, gZ)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
if (last == -1)
|
|
||||||
throw NoSuchElementException()
|
|
||||||
|
|
||||||
bits[last] = false
|
|
||||||
last = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isEmpty: Boolean
|
|
||||||
get() = bits.isEmpty
|
|
||||||
|
|
||||||
val size: Int
|
|
||||||
get() = bits.cardinality()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val segments = Object2ObjectRBTreeMap<SectionPos, Segment>(Vec3iHashStrategy)
|
|
||||||
|
|
||||||
fun subset(pos: ChunkPos): Set<BlockPos> {
|
|
||||||
val result = BlockPosSet()
|
|
||||||
|
|
||||||
for (key in segments.keys.iterator(SectionPos.of(pos.x, Int.MIN_VALUE, pos.z))) {
|
|
||||||
if (key.x != pos.x || key.z != pos.z) break
|
|
||||||
result.segments[key] = segments[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableSet(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun add(element: BlockPos): Boolean {
|
|
||||||
return segments.computeIfAbsent(SectionPos.of(element), Object2ObjectFunction { Segment() }).add(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val size: Int
|
|
||||||
get() = segments.values.sumOf { it.size }
|
|
||||||
|
|
||||||
override fun addAll(elements: Collection<BlockPos>): Boolean {
|
|
||||||
var any = false
|
|
||||||
elements.forEach { any = add(it) || any }
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
segments.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun contains(element: BlockPos): Boolean {
|
|
||||||
return segments[SectionPos.of(element)]?.contains(element) == true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun containsAll(elements: Collection<BlockPos>): Boolean {
|
|
||||||
return elements.all { contains(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEmpty(): Boolean {
|
|
||||||
var any = true
|
|
||||||
|
|
||||||
segments.entries.removeIf { (_, s) ->
|
|
||||||
any = any && s.isEmpty
|
|
||||||
s.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator(): MutableIterator<BlockPos> {
|
|
||||||
return object : MutableIterator<BlockPos> {
|
|
||||||
private val iterator = segments.entries.iterator()
|
|
||||||
private var current: MutableIterator<BlockPos> = ObjectIterators.emptyIterator()
|
|
||||||
private var last = current
|
|
||||||
private var foundNext = false
|
|
||||||
|
|
||||||
private fun findNext() {
|
|
||||||
if (!foundNext) {
|
|
||||||
foundNext = true
|
|
||||||
|
|
||||||
while (!current.hasNext() && iterator.hasNext()) {
|
|
||||||
val (pos, itr) = iterator.next()
|
|
||||||
current = itr.iterator(pos.x shl 4, pos.y shl 4, pos.z shl 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
findNext()
|
|
||||||
return current.hasNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun next(): BlockPos {
|
|
||||||
findNext()
|
|
||||||
last = current
|
|
||||||
foundNext = false
|
|
||||||
val blockPos = current.next()
|
|
||||||
return blockPos
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove() {
|
|
||||||
last.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove(element: BlockPos): Boolean {
|
|
||||||
val index = SectionPos.of(element)
|
|
||||||
val segment = segments[index] ?: return false
|
|
||||||
|
|
||||||
if (segment.remove(element)) {
|
|
||||||
if (segment.isEmpty)
|
|
||||||
segments.remove(index)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAll(elements: Collection<BlockPos>): Boolean {
|
|
||||||
var any = false
|
|
||||||
elements.forEach { any = remove(it) || any }
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun retainAll(elements: Collection<BlockPos>): Boolean {
|
|
||||||
return removeIf { it !in elements }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
return this === other || other is Set<*> && other.size == size && other.containsAll(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return segments.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "BlockPosSet[${joinToString(", ")}]"
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import com.github.benmanes.caffeine.cache.Caffeine
|
|||||||
import com.github.benmanes.caffeine.cache.Scheduler
|
import com.github.benmanes.caffeine.cache.Scheduler
|
||||||
import com.mojang.serialization.Codec
|
import com.mojang.serialization.Codec
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
|
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
|
||||||
@ -31,6 +30,7 @@ import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
|
|||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
||||||
@ -58,12 +58,12 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
|||||||
val root: EnhancedPlacement,
|
val root: EnhancedPlacement,
|
||||||
) : FeatureConfiguration {
|
) : FeatureConfiguration {
|
||||||
private class GeneratedChunk : EnhancedPlacementContext.Placer {
|
private class GeneratedChunk : EnhancedPlacementContext.Placer {
|
||||||
private data class Placement(val context: EnhancedPlacementContext, val positions: ObjectRBTreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
|
private data class Placement(val context: EnhancedPlacementContext, val positions: TreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
|
||||||
private val placed = LinkedList<Placement>()
|
private val placed = LinkedList<Placement>()
|
||||||
|
|
||||||
override fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
|
override fun place(context: EnhancedPlacementContext, positions: List<PlacementPos>, feature: EnhancedFeature.Configured<*, *>) {
|
||||||
if (positions.isNotEmpty()) {
|
if (positions.isNotEmpty()) {
|
||||||
placed.add(Placement(context, ObjectRBTreeSet<PlacementPos>().also { it.addAll(positions) }, feature))
|
placed.add(Placement(context, TreeSet<PlacementPos>().also { it.addAll(positions) }, feature))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,12 +72,12 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
|||||||
val pos = ChunkPos(context.origin())
|
val pos = ChunkPos(context.origin())
|
||||||
|
|
||||||
for ((eContext, positions, feature) in placed) {
|
for ((eContext, positions, feature) in placed) {
|
||||||
val itr = positions.iterator(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap))
|
val itr = positions.tailSet(PlacementPos(BlockPos(pos.minBlockX, Int.MIN_VALUE, pos.minBlockZ), emptyVariableMap)).iterator()
|
||||||
|
|
||||||
if (!itr.hasNext())
|
if (!itr.hasNext())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
val result = ObjectRBTreeSet<PlacementPos>()
|
val result = TreeSet<PlacementPos>()
|
||||||
|
|
||||||
for (placementPos in itr) {
|
for (placementPos in itr) {
|
||||||
if (SectionPos.blockToSectionCoord(placementPos.pos.x) != pos.x || SectionPos.blockToSectionCoord(placementPos.pos.z) != pos.z)
|
if (SectionPos.blockToSectionCoord(placementPos.pos.x) != pos.x || SectionPos.blockToSectionCoord(placementPos.pos.z) != pos.z)
|
||||||
@ -155,14 +155,21 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
|
|||||||
// between threads which lookup cache while it is being populated on neighbouring chunks
|
// between threads which lookup cache while it is being populated on neighbouring chunks
|
||||||
withIndex.shuffle(THREAD_LOCAL_RANDOM)
|
withIndex.shuffle(THREAD_LOCAL_RANDOM)
|
||||||
|
|
||||||
val instances = Int2ObjectRBTreeMap<GeneratedChunk>()
|
data class P(val i: Int, val v: GeneratedChunk) : Comparable<P> {
|
||||||
|
override fun compareTo(other: P): Int {
|
||||||
|
return i.compareTo(other.i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val instances = ArrayList<P>()
|
||||||
|
instances.sort()
|
||||||
|
|
||||||
withIndex.forEach { (i, it) ->
|
withIndex.forEach { (i, it) ->
|
||||||
instances.put(i, cache.get(it) { evaluate(context, it) })
|
instances.add(P(i, cache.get(it) { evaluate(context, it) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
var any = false
|
var any = false
|
||||||
instances.values.forEach { any = it.place(context) }
|
instances.forEach { any = it.v.place(context) }
|
||||||
return any
|
return any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.worldgen.placement
|
|||||||
|
|
||||||
import com.mojang.serialization.MapCodec
|
import com.mojang.serialization.MapCodec
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.util.RandomSource
|
import net.minecraft.util.RandomSource
|
||||||
import net.minecraft.util.valueproviders.FloatProvider
|
import net.minecraft.util.valueproviders.FloatProvider
|
||||||
@ -14,6 +13,7 @@ import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy
|
|||||||
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
|
||||||
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
|
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
|
||||||
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
|
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
|
||||||
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,9 +61,9 @@ data class EllipsoidPlacement(
|
|||||||
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
|
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluate(random: RandomSource, position: BlockPos): ObjectRBTreeSet<BlockPos> {
|
private fun evaluate(random: RandomSource, position: BlockPos): Set<BlockPos> {
|
||||||
val count = count.sample(random)
|
val count = count.sample(random)
|
||||||
val results = ObjectRBTreeSet<BlockPos>(Vec3iHashStrategy)
|
val results = TreeSet<BlockPos>(Vec3iHashStrategy)
|
||||||
|
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
return results
|
return results
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.tests
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import net.minecraft.core.SectionPos
|
|
||||||
import net.minecraft.world.level.ChunkPos
|
|
||||||
import org.junit.jupiter.api.Assertions
|
|
||||||
import org.junit.jupiter.api.DisplayName
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.BlockPosSet
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
|
|
||||||
object BlockPosSetTests {
|
|
||||||
@Test
|
|
||||||
@DisplayName("BlockPosSet additions")
|
|
||||||
fun add() {
|
|
||||||
val rand = GJRAND64RandomSource(43752304058048234L, -29394036784594328L)
|
|
||||||
|
|
||||||
val positions = ObjectArraySet<BlockPos>()
|
|
||||||
|
|
||||||
for (i in 0 until 1000) {
|
|
||||||
val x = rand.nextInt(-200, 200)
|
|
||||||
val y = rand.nextInt(-200, 200)
|
|
||||||
val z = rand.nextInt(-200, 200)
|
|
||||||
positions.add(BlockPos(x, y, z))
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = BlockPosSet()
|
|
||||||
result.addAll(positions)
|
|
||||||
|
|
||||||
Assertions.assertEquals(positions, result)
|
|
||||||
Assertions.assertEquals(positions, ObjectArraySet(result))
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos.ZERO)
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(0, 1))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 0 && SectionPos.blockToSectionCoord(it.z) == -1 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(0, -1))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 1 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(1, 0))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == -1 && SectionPos.blockToSectionCoord(it.z) == 0 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(-1, 0))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == 1 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(1, 1))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
val expected = positions.stream().filter { SectionPos.blockToSectionCoord(it.x) == -1 && SectionPos.blockToSectionCoord(it.z) == 1 }.collect(Collectors.toCollection(::ObjectArraySet))
|
|
||||||
val actual = result.subset(ChunkPos(-1, 1))
|
|
||||||
|
|
||||||
Assertions.assertEquals(expected, actual)
|
|
||||||
Assertions.assertEquals(expected, ObjectArraySet(actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user