Smarter multithreading
This commit is contained in:
parent
6ae8066ebc
commit
425494e104
@ -15,7 +15,6 @@ import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.ForkJoinTask
|
||||
import java.util.stream.Stream
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.full.createType
|
||||
|
||||
inline fun <reified T> GsonBuilder.registerTypeAdapter(adapter: TypeAdapter<T>): GsonBuilder {
|
||||
return registerTypeAdapter(T::class.java, adapter)
|
||||
@ -37,17 +36,17 @@ inline fun <reified T> GsonBuilder.registerTypeAdapter(noinline factory: (Gson)
|
||||
|
||||
fun <T> Array<T>.stream(): Stream<T> = Arrays.stream(this)
|
||||
|
||||
operator fun <T> ThreadLocal<T>.getValue(thisRef: Any, property: KProperty<*>): T? {
|
||||
operator fun <T> ThreadLocal<T>.getValue(thisRef: Any, property: KProperty<*>): T {
|
||||
return get()
|
||||
}
|
||||
|
||||
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T?) {
|
||||
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||
set(value)
|
||||
}
|
||||
|
||||
operator fun <K, V> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> = put(key, value)
|
||||
|
||||
fun String.sintern(): String = Starbound.strings.intern(this)
|
||||
fun String.sintern(): String = Starbound.STRINGS.intern(this)
|
||||
|
||||
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
||||
|
||||
|
@ -75,7 +75,7 @@ fun main() {
|
||||
for (chunkX in 0 .. 100) {
|
||||
//for (chunkX in 0 .. 17) {
|
||||
// for (chunkY in 21 .. 21) {
|
||||
for (chunkY in 18 .. 24) {
|
||||
for (chunkY in 0 .. 24) {
|
||||
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
|
||||
val data2 = db.read(byteArrayOf(2, 0, chunkX.toByte(), 0, chunkY.toByte()))
|
||||
|
||||
|
@ -2,8 +2,6 @@ package ru.dbotthepony.kstarbound
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Interner
|
||||
import com.google.gson.*
|
||||
import com.google.gson.internal.bind.JsonTreeReader
|
||||
import com.google.gson.stream.JsonReader
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
@ -15,37 +13,15 @@ import ru.dbotthepony.kstarbound.api.PhysicalFile
|
||||
import ru.dbotthepony.kstarbound.defs.*
|
||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.BackArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.ChestArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.CurrencyItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.FlashlightDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.HarvestingToolPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.HeadArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.api.IArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.InventoryIcon
|
||||
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.ItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.LegsArmorItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.LiquidItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.impl.MaterialItemDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterSkillDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.npc.NpcTypeDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.npc.TenantDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation
|
||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.particle.ParticleDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList
|
||||
import ru.dbotthepony.kstarbound.defs.player.PlayerDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.player.TechDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate
|
||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.util.JsonArrayCollector
|
||||
import ru.dbotthepony.kstarbound.io.*
|
||||
import ru.dbotthepony.kstarbound.io.json.AABBTypeAdapter
|
||||
@ -85,12 +61,10 @@ import ru.dbotthepony.kstarbound.util.filterNotNull
|
||||
import ru.dbotthepony.kstarbound.util.set
|
||||
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
||||
import java.io.*
|
||||
import java.lang.ref.Cleaner
|
||||
import java.text.DateFormat
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.ForkJoinTask
|
||||
import java.util.concurrent.Future
|
||||
import java.util.function.BiConsumer
|
||||
import java.util.function.BinaryOperator
|
||||
import java.util.function.Function
|
||||
@ -104,15 +78,22 @@ object Starbound : ISBFileLocator {
|
||||
const val TICK_TIME_ADVANCE = 0.01666666666666664
|
||||
const val TICK_TIME_ADVANCE_NANOS = 16_666_666L
|
||||
|
||||
val CLEANER: Cleaner = Cleaner.create {
|
||||
val t = Thread(it, "Starbound Global Cleaner Thread")
|
||||
t.isDaemon = true
|
||||
t.priority = 2
|
||||
t
|
||||
}
|
||||
|
||||
// currently Caffeine one saves only 4 megabytes of RAM on pretty big modpack
|
||||
// Hrm.
|
||||
// val strings: Interner<String> = Interner.newWeakInterner()
|
||||
// val strings: Interner<String> = Interner { it }
|
||||
val strings: Interner<String> = HashTableInterner(5)
|
||||
val STRINGS: Interner<String> = HashTableInterner(5)
|
||||
|
||||
private val polyfill by lazy { loadInternalScript("polyfill") }
|
||||
|
||||
private val logger = LogManager.getLogger()
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
val gson: Gson = with(GsonBuilder()) {
|
||||
serializeNulls()
|
||||
@ -120,9 +101,9 @@ object Starbound : ISBFileLocator {
|
||||
setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
|
||||
setPrettyPrinting()
|
||||
|
||||
registerTypeAdapter(InternedStringAdapter(strings))
|
||||
registerTypeAdapter(InternedStringAdapter(STRINGS))
|
||||
|
||||
InternedJsonElementAdapter(strings).also {
|
||||
InternedJsonElementAdapter(STRINGS).also {
|
||||
registerTypeAdapter(it)
|
||||
registerTypeAdapter(it.arrays)
|
||||
registerTypeAdapter(it.objects)
|
||||
@ -134,10 +115,10 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapterFactory(JsonImplementationTypeFactory)
|
||||
|
||||
// ImmutableList, ImmutableSet, ImmutableMap
|
||||
registerTypeAdapterFactory(ImmutableCollectionAdapterFactory(strings))
|
||||
registerTypeAdapterFactory(ImmutableCollectionAdapterFactory(STRINGS))
|
||||
|
||||
// fastutil collections
|
||||
registerTypeAdapterFactory(FastutilTypeAdapterFactory(strings))
|
||||
registerTypeAdapterFactory(FastutilTypeAdapterFactory(STRINGS))
|
||||
|
||||
// ArrayList
|
||||
registerTypeAdapterFactory(ArrayListAdapterFactory)
|
||||
@ -146,10 +127,10 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapterFactory(EnumAdapter.Companion)
|
||||
|
||||
// @JsonBuilder
|
||||
registerTypeAdapterFactory(BuilderAdapter.Factory(strings))
|
||||
registerTypeAdapterFactory(BuilderAdapter.Factory(STRINGS))
|
||||
|
||||
// @JsonFactory
|
||||
registerTypeAdapterFactory(FactoryAdapter.Factory(strings))
|
||||
registerTypeAdapterFactory(FactoryAdapter.Factory(STRINGS))
|
||||
|
||||
// Either<>
|
||||
registerTypeAdapterFactory(EitherTypeAdapter)
|
||||
@ -192,7 +173,7 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapterFactory(Json2Function.Companion)
|
||||
|
||||
// Общее
|
||||
registerTypeAdapterFactory(ThingDescription.Factory(strings))
|
||||
registerTypeAdapterFactory(ThingDescription.Factory(STRINGS))
|
||||
|
||||
registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.NORMAL))
|
||||
|
||||
@ -206,7 +187,7 @@ object Starbound : ISBFileLocator {
|
||||
|
||||
registerTypeAdapter(ItemStack.Adapter(this@Starbound))
|
||||
|
||||
registerTypeAdapterFactory(ItemReference.Factory(strings))
|
||||
registerTypeAdapterFactory(ItemReference.Factory(STRINGS))
|
||||
registerTypeAdapterFactory(TreasurePoolDefinition.Companion)
|
||||
|
||||
registerTypeAdapterFactory(with(RegistryReferenceFactory()) {
|
||||
@ -846,10 +827,10 @@ object Starbound : ISBFileLocator {
|
||||
var time = System.currentTimeMillis()
|
||||
|
||||
if (archivePaths.isNotEmpty()) {
|
||||
log.line("Searching for pak archives...".also(logger::info))
|
||||
log.line("Searching for pak archives...".also(LOGGER::info))
|
||||
|
||||
for (path in archivePaths) {
|
||||
val line = log.line("Reading index of ${path}...".also(logger::info))
|
||||
val line = log.line("Reading index of ${path}...".also(LOGGER::info))
|
||||
|
||||
addPak(StarboundPak(path) { _, status ->
|
||||
line.text = ("${path.parent}/${path.name}: $status")
|
||||
@ -857,9 +838,9 @@ object Starbound : ISBFileLocator {
|
||||
}
|
||||
}
|
||||
|
||||
log.line("Finished reading pak archives in ${System.currentTimeMillis() - time}ms".also(logger::info))
|
||||
log.line("Finished reading pak archives in ${System.currentTimeMillis() - time}ms".also(LOGGER::info))
|
||||
time = System.currentTimeMillis()
|
||||
log.line("Building file index...".also(logger::info))
|
||||
log.line("Building file index...".also(LOGGER::info))
|
||||
|
||||
val ext2files = fileSystems.parallelStream()
|
||||
.flatMap { it.explore() }
|
||||
@ -895,7 +876,7 @@ object Starbound : ISBFileLocator {
|
||||
}
|
||||
})
|
||||
|
||||
log.line("Finished building file index in ${System.currentTimeMillis() - time}ms".also(logger::info))
|
||||
log.line("Finished building file index in ${System.currentTimeMillis() - time}ms".also(LOGGER::info))
|
||||
|
||||
val tasks = ArrayList<ForkJoinTask<*>>()
|
||||
val pool = if (parallel) ForkJoinPool.commonPool() else ForkJoinPool(1)
|
||||
|
@ -13,6 +13,7 @@ import org.lwjgl.opengl.GLCapabilities
|
||||
import org.lwjgl.system.MemoryStack
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import ru.dbotthepony.kstarbound.LoadingLog
|
||||
import ru.dbotthepony.kstarbound.util.ManualExecutorService
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
@ -49,7 +50,6 @@ import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||
import ru.dbotthepony.kstarbound.util.Either
|
||||
import ru.dbotthepony.kstarbound.util.forEachValid
|
||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||
@ -73,29 +73,24 @@ import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.FutureTask
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import java.util.function.IntConsumer
|
||||
import kotlin.NoSuchElementException
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class StarboundClient : Closeable, ExecutorService {
|
||||
class StarboundClient : Closeable {
|
||||
val window: Long
|
||||
val camera = Camera(this)
|
||||
val input = UserInput()
|
||||
val thread: Thread = Thread.currentThread()
|
||||
// client specific executor which will accept tasks which involve probable
|
||||
// callback to foreground executor to initialize thread-unsafe data
|
||||
// In above case multiple threads will introduce big congestion for resources
|
||||
val backgroundExecutor = ForkJoinPool(Runtime.getRuntime().availableProcessors().coerceAtMost(4))
|
||||
val foregroundExecutor = ManualExecutorService(thread)
|
||||
val capabilities: GLCapabilities
|
||||
|
||||
var viewportX: Int = 0
|
||||
@ -150,8 +145,6 @@ class StarboundClient : Closeable, ExecutorService {
|
||||
|
||||
val loadingLog = LoadingLog()
|
||||
|
||||
private val executeQueue = ConcurrentLinkedQueue<Runnable>()
|
||||
private val futureQueue = ConcurrentLinkedQueue<FutureTask<*>>()
|
||||
private val openglCleanQueue = ReferenceQueue<Any>()
|
||||
private var openglCleanQueueHead: CleanRef? = null
|
||||
|
||||
@ -160,118 +153,6 @@ class StarboundClient : Closeable, ExecutorService {
|
||||
var prev: CleanRef? = null
|
||||
}
|
||||
|
||||
private data class InPlaceFuture<V>(private val value: V) : Future<V> {
|
||||
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCancelled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDone(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): V {
|
||||
return value
|
||||
}
|
||||
|
||||
override fun get(timeout: Long, unit: TimeUnit): V {
|
||||
return value
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EMPTY = InPlaceFuture(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(command: Runnable) {
|
||||
if (isSameThread()) {
|
||||
command.run()
|
||||
} else {
|
||||
executeQueue.add(command)
|
||||
LockSupport.unpark(thread)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shutdown() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun shutdownNow(): MutableList<Runnable> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun isShutdown(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isTerminated(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any?> submit(task: Callable<T>): Future<T> {
|
||||
if (isSameThread()) return InPlaceFuture(task.call())
|
||||
return FutureTask(task).also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun <T : Any?> submit(task: Runnable, result: T): Future<T> {
|
||||
if (isSameThread()) { task.run(); return InPlaceFuture(result) }
|
||||
return FutureTask { task.run(); result }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun submit(task: Runnable): Future<*> {
|
||||
if (isSameThread()) { task.run(); return InPlaceFuture.EMPTY }
|
||||
return FutureTask { task.run() }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAll(tasks: Collection<Callable<T>>): List<Future<T>> {
|
||||
if (isSameThread()) {
|
||||
return tasks.map { InPlaceFuture(it.call()) }
|
||||
} else {
|
||||
return tasks.map { submit(it) }.onEach { it.get() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAll(
|
||||
tasks: Collection<Callable<T>>,
|
||||
timeout: Long,
|
||||
unit: TimeUnit
|
||||
): List<Future<T>> {
|
||||
if (isSameThread()) {
|
||||
return tasks.map { InPlaceFuture(it.call()) }
|
||||
} else {
|
||||
return tasks.map { submit(it) }.onEach { it.get(timeout, unit) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>): T {
|
||||
if (tasks.isEmpty())
|
||||
throw NoSuchElementException("Provided task list is empty")
|
||||
|
||||
if (isSameThread()) {
|
||||
return tasks.first().call()
|
||||
} else {
|
||||
return submit(tasks.first()).get()
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>, timeout: Long, unit: TimeUnit): T {
|
||||
if (tasks.isEmpty())
|
||||
throw NoSuchElementException("Provided task list is empty")
|
||||
|
||||
if (isSameThread()) {
|
||||
return tasks.first().call()
|
||||
} else {
|
||||
return submit(tasks.first()).get(timeout, unit)
|
||||
}
|
||||
}
|
||||
|
||||
var openglObjectsCreated = 0L
|
||||
private set
|
||||
|
||||
@ -421,20 +302,7 @@ class StarboundClient : Closeable, ExecutorService {
|
||||
}
|
||||
|
||||
private fun executeQueuedTasks() {
|
||||
var next = executeQueue.poll()
|
||||
|
||||
while (next != null) {
|
||||
next.run()
|
||||
next = executeQueue.poll()
|
||||
}
|
||||
|
||||
var next2 = futureQueue.poll()
|
||||
|
||||
while (next2 != null) {
|
||||
next2.run()
|
||||
Thread.interrupted()
|
||||
next2 = futureQueue.poll()
|
||||
}
|
||||
foregroundExecutor.executeQueuedTasks()
|
||||
|
||||
var next3 = openglCleanQueue.poll() as CleanRef?
|
||||
|
||||
|
@ -18,9 +18,9 @@ class StreamVertexBuilder(
|
||||
) {
|
||||
val builder = VertexBuilder(attributes, type, initialCapacity)
|
||||
|
||||
private val vao = client.submit(Callable { VertexArrayObject() })
|
||||
private val vbo = client.submit(Callable { BufferObject.VBO() })
|
||||
private val ebo = client.submit(Callable { BufferObject.EBO() })
|
||||
private val vao = client.foregroundExecutor.submit(Callable { VertexArrayObject() })
|
||||
private val vbo = client.foregroundExecutor.submit(Callable { BufferObject.VBO() })
|
||||
private val ebo = client.foregroundExecutor.submit(Callable { BufferObject.EBO() })
|
||||
|
||||
private var initialized = false
|
||||
|
||||
|
@ -52,14 +52,14 @@ class TileRenderers(val client: StarboundClient) {
|
||||
fun getMaterialRenderer(defName: String): TileRenderer {
|
||||
return matCache.get(defName) {
|
||||
val def = Registries.tiles[defName] // TODO: Пустой рендерер
|
||||
client.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||
client.foregroundExecutor.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||
}
|
||||
}
|
||||
|
||||
fun getModifierRenderer(defName: String): TileRenderer {
|
||||
return modCache.get(defName) {
|
||||
val def = Registries.tileModifiers[defName] // TODO: Пустой рендерер
|
||||
client.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||
client.foregroundExecutor.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ class ClientWorld(
|
||||
|
||||
isDirty = false
|
||||
|
||||
currentBakeTask = ForkJoinPool.commonPool().submit(Callable {
|
||||
currentBakeTask = client.backgroundExecutor.submit(Callable {
|
||||
val meshes = LayeredRenderer(client)
|
||||
|
||||
for (x in 0 until renderRegionWidth) {
|
||||
|
@ -94,7 +94,7 @@ class Image private constructor(
|
||||
) ?: throw IllegalArgumentException("File $source is not an image or it is corrupted")
|
||||
|
||||
val address = MemoryUtil.memAddress(data)
|
||||
cleaner.register(data) { STBImage.nstbi_image_free(address) }
|
||||
Starbound.CLEANER.register(data) { STBImage.nstbi_image_free(address) }
|
||||
|
||||
check(getWidth[0] == width && getHeight[0] == height && components[0] == amountOfChannels) {
|
||||
"Actual loaded image differs from constructed (this $width x $height with $amountOfChannels channels; loaded ${getWidth[0]} x ${getHeight[0]} with ${components[0]} channels)"
|
||||
@ -272,7 +272,6 @@ class Image private constructor(
|
||||
private val cache = ConcurrentHashMap<String, Optional<List<DataSprite>>>()
|
||||
private val imageCache = ConcurrentHashMap<String, Optional<Image>>()
|
||||
private val logger = LogManager.getLogger()
|
||||
private val cleaner = Cleaner.create { Thread(it, "STB Image Cleaner") }
|
||||
|
||||
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
|
@ -2,7 +2,9 @@ package ru.dbotthepony.kstarbound.io
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
||||
import ru.dbotthepony.kstarbound.getValue
|
||||
import ru.dbotthepony.kstarbound.io.json.BinaryJsonReader
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.Closeable
|
||||
@ -12,6 +14,7 @@ import java.io.IOError
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.RandomAccessFile
|
||||
import java.lang.ref.Cleaner
|
||||
import java.nio.channels.Channels
|
||||
import java.util.*
|
||||
|
||||
@ -97,11 +100,9 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
||||
return -1
|
||||
}
|
||||
|
||||
synchronized(reader) {
|
||||
reader.seek(innerOffset + offset)
|
||||
innerOffset++
|
||||
return reader.read()
|
||||
}
|
||||
reader.seek(innerOffset + offset)
|
||||
innerOffset++
|
||||
return reader.read()
|
||||
}
|
||||
|
||||
override fun readNBytes(len: Int): ByteArray {
|
||||
@ -112,13 +113,11 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
||||
if (readMax == 0)
|
||||
return ByteArray(0)
|
||||
|
||||
synchronized(reader) {
|
||||
val b = ByteArray(readMax)
|
||||
reader.seek(innerOffset + offset)
|
||||
reader.readFully(b)
|
||||
innerOffset += readMax
|
||||
return b
|
||||
}
|
||||
val b = ByteArray(readMax)
|
||||
reader.seek(innerOffset + offset)
|
||||
reader.readFully(b)
|
||||
innerOffset += readMax
|
||||
return b
|
||||
}
|
||||
|
||||
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||
@ -152,7 +151,11 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
||||
}
|
||||
}
|
||||
|
||||
private val reader = RandomAccessFile(path, "r")
|
||||
private val reader by object : ThreadLocal<RandomAccessFile>() {
|
||||
override fun initialValue(): RandomAccessFile {
|
||||
return RandomAccessFile(path, "r")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
readHeader(reader, 0x53) // S
|
||||
|
@ -14,7 +14,6 @@ import ru.dbotthepony.kstarbound.io.readString
|
||||
import ru.dbotthepony.kstarbound.io.readVarInt
|
||||
import ru.dbotthepony.kstarbound.io.readVarLong
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.EOFException
|
||||
import java.io.InputStream
|
||||
import java.io.Reader
|
||||
@ -357,7 +356,7 @@ class BinaryJsonReader(private val stream: DataInputStream) : JsonReader(unreada
|
||||
}
|
||||
|
||||
fun readString(reader: DataInputStream): String {
|
||||
return Starbound.strings.intern(reader.readString(reader.readVarInt()))
|
||||
return Starbound.STRINGS.intern(reader.readString(reader.readVarInt()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,15 +13,12 @@ import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.internal.bind.JsonTreeReader
|
||||
import com.google.gson.internal.bind.TypeAdapters
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
@ -512,7 +509,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
fun <T : Any> createFor(kclass: KClass<T>, config: JsonFactory, gson: Gson, stringInterner: Interner<String> = Starbound.strings): TypeAdapter<T> {
|
||||
fun <T : Any> createFor(kclass: KClass<T>, config: JsonFactory, gson: Gson, stringInterner: Interner<String> = Starbound.STRINGS): TypeAdapter<T> {
|
||||
val builder = Builder(kclass)
|
||||
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
||||
|
||||
|
@ -32,8 +32,8 @@ import java.nio.ByteOrder
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Suppress("unused")
|
||||
class LuaState private constructor(private val pointer: Pointer, val stringInterner: Interner<String> = Starbound.strings) : Closeable {
|
||||
constructor(stringInterner: Interner<String> = Starbound.strings) : this(LuaJNR.INSTANCE.luaL_newstate() ?: throw OutOfMemoryError("Unable to allocate new LuaState"), stringInterner) {
|
||||
class LuaState private constructor(private val pointer: Pointer, val stringInterner: Interner<String> = Starbound.STRINGS) : Closeable {
|
||||
constructor(stringInterner: Interner<String> = Starbound.STRINGS) : this(LuaJNR.INSTANCE.luaL_newstate() ?: throw OutOfMemoryError("Unable to allocate new LuaState"), stringInterner) {
|
||||
val pointer = this.pointer
|
||||
val panic = ClosureManager.getInstance().newClosure(
|
||||
{
|
||||
@ -44,7 +44,7 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
|
||||
CallContext.getCallContext(Type.SINT, arrayOf(Type.POINTER), CallingConvention.DEFAULT, false)
|
||||
)
|
||||
|
||||
this.cleanable = CLEANER.register(this) {
|
||||
this.cleanable = Starbound.CLEANER.register(this) {
|
||||
LuaJNR.INSTANCE.lua_close(pointer)
|
||||
panic.dispose()
|
||||
}
|
||||
@ -1078,12 +1078,6 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
|
||||
private val CLEANER: Cleaner = Cleaner.create {
|
||||
val thread = Thread(it, "Lua State Cleaner")
|
||||
thread.priority = 1
|
||||
thread
|
||||
}
|
||||
|
||||
private val sharedBuffers = ThreadLocal<Long>()
|
||||
|
||||
private val sharedStringBufferPtr: Long get() {
|
||||
@ -1099,7 +1093,7 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
|
||||
sharedBuffers.set(p)
|
||||
val p2 = p
|
||||
|
||||
CLEANER.register(Thread.currentThread()) {
|
||||
Starbound.CLEANER.register(Thread.currentThread()) {
|
||||
MemoryIO.getInstance().freeMemory(p2)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ object AssetPathStack {
|
||||
if (b[0] == '/')
|
||||
return b
|
||||
|
||||
return Starbound.strings.intern("$a/$b")
|
||||
return Starbound.STRINGS.intern("$a/$b")
|
||||
}
|
||||
|
||||
fun remap(path: String): String {
|
||||
|
@ -0,0 +1,151 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.FutureTask
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
|
||||
class ManualExecutorService(val thread: Thread = Thread.currentThread()) : ExecutorService {
|
||||
private val executeQueue = ConcurrentLinkedQueue<Runnable>()
|
||||
private val futureQueue = ConcurrentLinkedQueue<FutureTask<*>>()
|
||||
|
||||
private data class InPlaceFuture<V>(private val value: V) : Future<V> {
|
||||
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCancelled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDone(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun get(): V {
|
||||
return value
|
||||
}
|
||||
|
||||
override fun get(timeout: Long, unit: TimeUnit): V {
|
||||
return value
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EMPTY = InPlaceFuture(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun isSameThread(): Boolean {
|
||||
return Thread.currentThread() === thread
|
||||
}
|
||||
|
||||
fun executeQueuedTasks() {
|
||||
check(isSameThread()) { "Trying to execute queued tasks in thread ${Thread.currentThread()}, while correct thread is $thread" }
|
||||
|
||||
var next = executeQueue.poll()
|
||||
|
||||
while (next != null) {
|
||||
next.run()
|
||||
next = executeQueue.poll()
|
||||
}
|
||||
|
||||
var next2 = futureQueue.poll()
|
||||
|
||||
while (next2 != null) {
|
||||
next2.run()
|
||||
Thread.interrupted()
|
||||
next2 = futureQueue.poll()
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(command: Runnable) {
|
||||
if (isSameThread()) {
|
||||
command.run()
|
||||
} else {
|
||||
executeQueue.add(command)
|
||||
LockSupport.unpark(thread)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shutdown() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun shutdownNow(): MutableList<Runnable> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun isShutdown(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isTerminated(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun <T : Any?> submit(task: Callable<T>): Future<T> {
|
||||
if (isSameThread()) return InPlaceFuture(task.call())
|
||||
return FutureTask(task).also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun <T : Any?> submit(task: Runnable, result: T): Future<T> {
|
||||
if (isSameThread()) { task.run(); return InPlaceFuture(result) }
|
||||
return FutureTask { task.run(); result }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun submit(task: Runnable): Future<*> {
|
||||
if (isSameThread()) { task.run(); return InPlaceFuture.EMPTY
|
||||
}
|
||||
return FutureTask { task.run() }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAll(tasks: Collection<Callable<T>>): List<Future<T>> {
|
||||
if (isSameThread()) {
|
||||
return tasks.map { InPlaceFuture(it.call()) }
|
||||
} else {
|
||||
return tasks.map { submit(it) }.onEach { it.get() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAll(
|
||||
tasks: Collection<Callable<T>>,
|
||||
timeout: Long,
|
||||
unit: TimeUnit
|
||||
): List<Future<T>> {
|
||||
if (isSameThread()) {
|
||||
return tasks.map { InPlaceFuture(it.call()) }
|
||||
} else {
|
||||
return tasks.map { submit(it) }.onEach { it.get(timeout, unit) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>): T {
|
||||
if (tasks.isEmpty())
|
||||
throw NoSuchElementException("Provided task list is empty")
|
||||
|
||||
if (isSameThread()) {
|
||||
return tasks.first().call()
|
||||
} else {
|
||||
return submit(tasks.first()).get()
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>, timeout: Long, unit: TimeUnit): T {
|
||||
if (tasks.isEmpty())
|
||||
throw NoSuchElementException("Provided task list is empty")
|
||||
|
||||
if (isSameThread()) {
|
||||
return tasks.first().call()
|
||||
} else {
|
||||
return submit(tasks.first()).get(timeout, unit)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -204,7 +204,7 @@ class SBPattern private constructor(
|
||||
throw IllegalArgumentException("Malformed pattern string: $raw")
|
||||
}
|
||||
|
||||
pieces.add(Piece(name = Starbound.strings.intern(raw.substring(open + 1, closing))))
|
||||
pieces.add(Piece(name = Starbound.STRINGS.intern(raw.substring(open + 1, closing))))
|
||||
i = closing + 1
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ import java.util.function.Consumer
|
||||
import java.util.stream.Stream
|
||||
|
||||
fun String.sbIntern(): String {
|
||||
return Starbound.strings.intern(this)
|
||||
return Starbound.STRINGS.intern(this)
|
||||
}
|
||||
|
||||
fun String.sbIntern2(): String {
|
||||
return Starbound.strings.intern(this.intern())
|
||||
return Starbound.STRINGS.intern(this.intern())
|
||||
}
|
||||
|
||||
fun traverseJsonPath(path: String?, element: JsonElement?): JsonElement? {
|
||||
|
Loading…
Reference in New Issue
Block a user