diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 6760bfbc..7ca2c686 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -4,7 +4,6 @@ import org.apache.logging.log4j.LogManager import org.lwjgl.Version import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.server.IntegratedStarboundServer -import ru.dbotthepony.kstarbound.util.random.random import java.io.File import java.net.InetSocketAddress diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index 17d71550..55443caa 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -151,6 +151,12 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca @JvmField val COROUTINE_EXECUTOR = ExecutorWithScheduler(EXECUTOR, this).asCoroutineDispatcher() + // this is required for Caffeine since it ignores scheduler + // (and suffers noticeable throughput penalty) in rescheduleCleanUpIfIncomplete() + // if executor is specified as ForkJoinPool.commonPool() + @JvmField + val SCREENED_EXECUTOR: ExecutorService = object : ExecutorService by EXECUTOR {} + @JvmField val CLEANER: Cleaner = Cleaner.create { val t = Thread(it, "Starbound Global Cleaner") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt index c6a97f32..922e81d2 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt @@ -345,7 +345,7 @@ class Image private constructor( .weigher { key, value -> value.capacity() } .maximumWeight((Runtime.getRuntime().maxMemory() / 4L).coerceIn(1_024L * 1_024L * 32L /* 32 МиБ */, 1_024L * 1_024L * 256L /* 256 МиБ */)) .scheduler(Starbound) - .executor(Starbound.EXECUTOR) + .executor(Starbound.EXECUTOR) // SCREENED_EXECUTOR shouldn't be used here .buildAsync(CacheLoader { readImageDirect(it).data }) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt index 2d30d667..78e2d953 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt @@ -228,7 +228,7 @@ data class BiomePlaceables( return null // whut if (staticRandomDouble(x, y, blockSeed) <= blockProbability) { - return Placement(randomItems[staticRandomInt(0, randomItems.size, x, y, blockSeed)], Vector2i(x, y), priority) + return Placement(randomItems[staticRandomInt(0, randomItems.size - 1, x, y, blockSeed)], Vector2i(x, y), priority) } } else { if (weightedItems.isEmpty()) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt index d8a59a06..84409b12 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt @@ -259,7 +259,7 @@ class WorldTemplate(val geometry: WorldGeometry) { for (layer in worldParameters.layers) { if (layer.dungeons.isNotEmpty()) { val dungeonSpacing = geometry.size.x / layer.dungeons.size - var dungeonOffset = staticRandomInt(0, geometry.size.x, seed, layer.layerBaseHeight, "dungeon") + var dungeonOffset = staticRandomInt(0, geometry.size.x - 1, seed, layer.layerBaseHeight, "dungeon") for (dungeon in layer.dungeons) { if (dungeon.isPresent) { @@ -303,7 +303,7 @@ class WorldTemplate(val geometry: WorldGeometry) { .maximumSize(1_500_000L) // plentiful of space, and allows for high hit ratio (around 79%) in most situations // downside is memory consumption, but why should it matter when we save 80% of cpu time? .expireAfterAccess(Duration.ofSeconds(20)) - .executor(Starbound.EXECUTOR) + .executor(Starbound.SCREENED_EXECUTOR) .scheduler(Starbound) // .recordStats() .build { (x, y) -> cellInfo0(x, y) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/AssetJsonFunction.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/AssetJsonFunction.kt deleted file mode 100644 index f30bc682..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/AssetJsonFunction.kt +++ /dev/null @@ -1,27 +0,0 @@ -package ru.dbotthepony.kstarbound.lua.bindings - -import org.classdump.luna.ByteString -import org.classdump.luna.LuaRuntimeException -import org.classdump.luna.TableFactory -import org.classdump.luna.impl.NonsuspendableFunctionException -import org.classdump.luna.runtime.AbstractFunction1 -import org.classdump.luna.runtime.ExecutionContext -import ru.dbotthepony.kstarbound.Starbound -import ru.dbotthepony.kstarbound.lua.from - -class AssetJsonFunction(private val tables: TableFactory) : AbstractFunction1() { - override fun resume(context: ExecutionContext?, suspendedState: Any?) { - throw NonsuspendableFunctionException(this::class.java) - } - - override fun invoke(context: ExecutionContext, arg1: ByteString?) { - arg1 ?: throw LuaRuntimeException("bad argument #1 to root.assetJson (string expected, got nil)") - val load = Starbound.loadJsonAsset(arg1.decode()) - - if (load == null) { - context.returnBuffer.setTo() - } else { - context.returnBuffer.setTo(tables.from(load)) - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt index 702a74ca..635bc922 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/RootBindings.kt @@ -350,7 +350,10 @@ fun provideRootBindings(lua: LuaEnvironment) { val table = lua.newTable() lua.globals["root"] = table - table["assetJson"] = AssetJsonFunction(lua) + table["assetJson"] = luaFunction { path: ByteString -> + returnBuffer.setTo(from(Starbound.loadJsonAsset(path.decode()))) + } + table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson") table["loadVersionedJson"] = luaStub("loadVersionedJson") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/UtilityBindings.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/UtilityBindings.kt index d5062be8..cba1a133 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/UtilityBindings.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/UtilityBindings.kt @@ -29,6 +29,7 @@ import ru.dbotthepony.kstarbound.util.random.staticRandomLong import ru.dbotthepony.kstarbound.util.toStarboundString import java.util.* import java.util.random.RandomGenerator +import kotlin.math.sign private val LOGGER = LogManager.getLogger() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/WorldBindings.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/WorldBindings.kt index 52f3f930..2b8f9c6e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/WorldBindings.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/lua/bindings/WorldBindings.kt @@ -1,10 +1,12 @@ package ru.dbotthepony.kstarbound.lua.bindings import org.classdump.luna.Table +import org.classdump.luna.runtime.ExecutionContext import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.get import ru.dbotthepony.kstarbound.lua.luaFunction import ru.dbotthepony.kstarbound.lua.set +import ru.dbotthepony.kstarbound.lua.toPoly import ru.dbotthepony.kstarbound.lua.toVector2d import ru.dbotthepony.kstarbound.world.World @@ -23,4 +25,8 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) { returnBuffer.setTo(self.geometry.diff(toVector2d(arg1), toVector2d(arg2)).length) } } + + callbacks["polyContains"] = luaFunction { poly: Table, position: Table -> + returnBuffer.setTo(self.geometry.polyContains(toPoly(poly), toVector2d(position))) + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerUniverse.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerUniverse.kt index f1586207..ee3d9d49 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerUniverse.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerUniverse.kt @@ -221,7 +221,7 @@ class ServerUniverse private constructor(marker: Nothing?) : Universe(), Closeab .maximumSize(1024L) .softValues() .scheduler(Starbound) - .executor(Starbound.EXECUTOR) + .executor(Starbound.SCREENED_EXECUTOR) .build() fun getChunkFuture(pos: Vector2i): CompletableFuture> { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorldTracker.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorldTracker.kt index b3fd7f31..066b7213 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorldTracker.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorldTracker.kt @@ -18,6 +18,7 @@ import ru.dbotthepony.kstarbound.client.network.packets.ChunkCellsPacket import ru.dbotthepony.kstarbound.defs.SpawnTarget import ru.dbotthepony.kstarbound.defs.WarpAction import ru.dbotthepony.kstarbound.defs.WorldID +import ru.dbotthepony.kstarbound.defs.world.FlyingType import ru.dbotthepony.kstarbound.network.IPacket import ru.dbotthepony.kstarbound.network.packets.EntityCreatePacket import ru.dbotthepony.kstarbound.network.packets.EntityDestroyPacket @@ -127,6 +128,8 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p } } + private var lastSeenFlyingType = FlyingType.NONE + fun tick() { if (!client.worldStartAcknowledged) return @@ -144,11 +147,12 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p } run { - if (skyUpdateWaitTicks++ >= 20) { + if (skyUpdateWaitTicks++ >= 20 || world.sky.flyingType != lastSeenFlyingType) { val (data, version) = world.sky.networkedGroup.write(skyVersion, isLegacy = client.isLegacy) skyVersion = version send(EnvironmentUpdatePacket(data, ByteArrayList())) skyUpdateWaitTicks = 0 + lastSeenFlyingType = world.sky.flyingType } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt index bb2d8e96..7a0f967b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/BlockableEventLoop.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.util +import it.unimi.dsi.fastutil.objects.ObjectArrayList import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -129,7 +130,7 @@ open class BlockableEventLoop(name: String) : Thread(name), ScheduledExecutorSer } if (scheduledQueue.isNotEmpty()) { - val executed = ArrayList>() + val executed = ObjectArrayList>(4) var lastSize: Int do { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/RandomUtils.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/RandomUtils.kt index a9d36006..5b041bd4 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/RandomUtils.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/RandomUtils.kt @@ -86,14 +86,14 @@ fun staticRandomDouble(vararg values: Any?): Double { return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16 } -fun staticRandomInt(origin: Int, bound: Int, vararg values: Any?): Int { - val rand = staticRandomDouble(*values) - return origin + ((bound - origin) * rand).toInt() +fun staticRandomInt(min: Int, max: Int, vararg values: Any?): Int { + val hash = staticRandomDouble(*values) + return (min + (max - min + 1) * hash).toInt() } -fun staticRandomLong(origin: Long, bound: Long, vararg values: Any?): Long { - val rand = staticRandomDouble(*values) - return origin + ((bound - origin) * rand).toLong() +fun staticRandomLong(min: Long, max: Long, vararg values: Any?): Long { + val hash = staticRandomDouble(*values) + return (min + (max - min + 1L) * hash).toLong() } fun staticRandom64(vararg values: Any?): Long { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index c5cfd299..1e8e1084 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -90,7 +90,7 @@ abstract class Chunk, This : Chunk() + val signalPositions = ObjectArrayList(24) for (x in 1 .. 2) { for (y in 1 .. 2) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Sky.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Sky.kt index fd72eab2..d138bfd8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Sky.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Sky.kt @@ -98,12 +98,18 @@ class Sky() { } fun startFlying(enterHyperspace: Boolean, startInWarp: Boolean = false) { + if (flyingType == FlyingType.NONE) + flyingTimer = 0.0 + else if (flyingType == FlyingType.WARP && warpPhase == WarpPhase.SLOWING_DOWN) { + warpPhase = WarpPhase.SPEEDING_UP + flyingTimer = speedupTime + } + if (startInWarp) flyingType = FlyingType.WARP else if (flyingType == FlyingType.NONE) flyingType = FlyingType.DISEMBARKING - // flyingTimer = 0.0 this.enterHyperspace = enterHyperspace this.startInWarp = startInWarp } @@ -113,6 +119,11 @@ class Sky() { if (skyType != SkyType.WARP) skyType = SkyType.ORBITAL + + if (flyingType == FlyingType.NONE || flyingType == FlyingType.ARRIVING) { + this.skyParameters = destination + this.destination = null + } } private var lastFlyingType = FlyingType.NONE diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/WorldGeometry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/WorldGeometry.kt index a34d51a2..d7112798 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/WorldGeometry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/WorldGeometry.kt @@ -240,4 +240,13 @@ data class WorldGeometry(val size: Vector2i, val loopX: Boolean = true, val loop fun polyDistance(poly: Poly, point: IStruct2d): Double { return poly.distance(nearestTo(poly.centre, point)) } + + fun split(poly: Poly): List { + TODO() + } + + fun polyContains(poly: Poly, point: IStruct2d): Boolean { + val wrap = wrap(point) + return split(poly).any { it.contains(wrap) } + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/KarstCaveTerrainSelector.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/KarstCaveTerrainSelector.kt index a8737cc5..c1e0ee01 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/KarstCaveTerrainSelector.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/KarstCaveTerrainSelector.kt @@ -62,7 +62,7 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters .softValues() .expireAfterAccess(Duration.ofMinutes(1)) .scheduler(Starbound) - .executor(Starbound.EXECUTOR) + .executor(Starbound.SCREENED_EXECUTOR) .build(::Layer) private inner class Sector(val sector: Vector2i) { @@ -131,7 +131,7 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters .softValues() .expireAfterAccess(Duration.ofMinutes(1)) .scheduler(Starbound) - .executor(Starbound.EXECUTOR) + .executor(Starbound.SCREENED_EXECUTOR) .build(::Sector) override fun get(x: Int, y: Int): Double { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/WormCaveTerrainSelector.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/WormCaveTerrainSelector.kt index 149c359d..6a3f60c5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/WormCaveTerrainSelector.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/terrain/WormCaveTerrainSelector.kt @@ -185,7 +185,7 @@ class WormCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters) .softValues() .expireAfterAccess(Duration.ofMinutes(1)) .scheduler(Starbound) - .executor(Starbound.EXECUTOR) + .executor(Starbound.SCREENED_EXECUTOR) .build(::Sector) override fun get(x: Int, y: Int): Double {