package ru.dbotthepony.kstarbound.lua import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import ru.dbotthepony.kstarbound.lua.userdata.LuaFuture import ru.dbotthepony.kstarbound.lua.userdata.LuaPathFinder import ru.dbotthepony.kstarbound.util.random.random import java.io.Closeable import java.lang.ref.Cleaner.Cleanable import java.util.concurrent.ConcurrentLinkedQueue import java.util.random.RandomGenerator import kotlin.properties.Delegates class LuaSharedState(val handlesThread: LuaThread, private val cleanable: Cleanable) : Closeable { private val pendingFree = ConcurrentLinkedQueue<Int>() private val freeHandles = IntAVLTreeSet() private var nextHandle = 0 // faster code path private var handlesInUse = 0 private val namedHandles = HashMap<Any, LuaHandle>() var random: RandomGenerator = random() var commonHandles by Delegates.notNull<CommonHandleRegistry>() private set var isValid = true private set fun ensureValid() { check(isValid) { "Tried to use NULL LuaState!" } } var errorToStringFunction by Delegates.notNull<LuaHandle>() private set var errorTrapFunction by Delegates.notNull<LuaHandle>() private set override fun toString(): String { return "LuaSharedState[$handlesThread]" } override fun close() { if (!isValid) return isValid = false namedHandles.clear() cleanable.clean() errorToStringFunction = LuaHandle.Nil errorTrapFunction = LuaHandle.Nil commonHandles = CommonHandleRegistry.EMPTY } fun initializeHandles(mainThread: LuaThread) { val future = LuaFuture.initializeHandle(mainThread) val pathFinder = LuaPathFinder.initializeHandle(mainThread) commonHandles = CommonHandleRegistry( future = future, pathFinder = pathFinder, ) handlesThread.push { //it.lua.push(it.nextObject<Throwable?>(-1)?.stackTraceToString() ?: it.nextObject<Any?>(-1).toString()) it.lua.push(it.nextObject<Any?>().toString()) 1 } errorToStringFunction = allocateHandle(null) handlesThread.push { val peek = it.peek() if (peek == LuaType.STRING) { val err = LuaRuntimeException(it.lua.traceback(it.lua.getString(), 1)) it.lua.push(err) } else if (peek == LuaType.USERDATA) { val obj = it.nextObject<Any>() if (obj is Throwable && obj !is LuaRuntimeException) { val err = LuaRuntimeException(it.lua.traceback(obj.toString(), 1), cause = obj, writeStackTrace = false) it.lua.push(err) } } 1 } errorTrapFunction = allocateHandle(null) } fun freeHandle(handle: Int, key: Any?) { if (!isValid) return pendingFree.add(handle) if (key != null) { namedHandles.remove(key) } } fun cleanup() { ensureValid() if (handlesInUse == 0) return var handle = pendingFree.poll() while (handle != null) { handlesInUse-- freeHandles.add(handle) handlesThread.push() handlesThread.copy(-1, handle) handlesThread.pop() handle = pendingFree.poll() } } fun allocateHandle(name: Any?): LuaHandle { ensureValid() require(name == null || name !in namedHandles) { "Named handle '$name' already exists" } handlesInUse++ if (freeHandles.isEmpty()) { if (nextHandle % 10 == 0) { handlesThread.ensureExtraCapacity(20) } return LuaHandle.Regular(this, ++nextHandle, name).also { if (name != null) namedHandles[name] = it } } else { val handle = freeHandles.firstInt() freeHandles.remove(handle) handlesThread.copy(-1, handle) handlesThread.pop() return LuaHandle.Regular(this, handle, name).also { if (name != null) namedHandles[name] = it } } } fun getNamedHandle(key: Any): LuaHandle { ensureValid() return namedHandles[key] ?: throw NoSuchElementException("No such handle: $key") } fun findNamedHandle(key: Any): LuaHandle? { ensureValid() return namedHandles[key] } }