require, math.random and math.randomseed implementations
This commit is contained in:
parent
c9be37e37b
commit
f82b48672e
@ -17,13 +17,18 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.lwjgl.system.MemoryStack
|
import org.lwjgl.system.MemoryStack
|
||||||
import org.lwjgl.system.MemoryUtil
|
import org.lwjgl.system.MemoryUtil
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||||
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
|
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.lang.ref.Cleaner
|
import java.lang.ref.Cleaner
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
import kotlin.properties.Delegates
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -50,6 +55,8 @@ class LuaThread private constructor(
|
|||||||
panic.setAutoRelease(false)
|
panic.setAutoRelease(false)
|
||||||
LuaJNR.INSTANCE.lua_atpanic(pointer, panic.address)
|
LuaJNR.INSTANCE.lua_atpanic(pointer, panic.address)
|
||||||
|
|
||||||
|
randomHolder = Delegate.Box(random())
|
||||||
|
|
||||||
LuaJNR.INSTANCE.luaopen_base(this.pointer)
|
LuaJNR.INSTANCE.luaopen_base(this.pointer)
|
||||||
this.storeGlobal("_G")
|
this.storeGlobal("_G")
|
||||||
LuaJNR.INSTANCE.luaopen_table(this.pointer)
|
LuaJNR.INSTANCE.luaopen_table(this.pointer)
|
||||||
@ -70,13 +77,60 @@ class LuaThread private constructor(
|
|||||||
|
|
||||||
storeGlobal("__print")
|
storeGlobal("__print")
|
||||||
|
|
||||||
|
push {
|
||||||
|
val path = getString()
|
||||||
|
|
||||||
|
try {
|
||||||
|
load(Starbound.readLuaScript(path).join(), "@$path")
|
||||||
|
1
|
||||||
|
} catch (err: Exception) {
|
||||||
|
LOGGER.error("Exception loading Lua script $path", err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeGlobal("__require")
|
||||||
|
|
||||||
|
push {
|
||||||
|
push(random.nextDouble())
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
storeGlobal("__random_double")
|
||||||
|
|
||||||
|
push {
|
||||||
|
push(random.nextLong(getLong(), getLong()))
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
storeGlobal("__random_long")
|
||||||
|
|
||||||
|
push {
|
||||||
|
random = random(getLong())
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
storeGlobal("__random_seed")
|
||||||
|
|
||||||
load(globalScript, "@starbound.jar!/scripts/global.lua")
|
load(globalScript, "@starbound.jar!/scripts/global.lua")
|
||||||
call()
|
call()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var cleanable: Cleaner.Cleanable? = null
|
private var cleanable: Cleaner.Cleanable? = null
|
||||||
|
private var randomHolder: Delegate<RandomGenerator> by Delegates.notNull()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for generating random numbers using math.random
|
||||||
|
*
|
||||||
|
* Can be safely set to any other random number generator;
|
||||||
|
* math.randomseed sets this property to brand new generator with required seed
|
||||||
|
*/
|
||||||
|
var random: RandomGenerator
|
||||||
|
get() = randomHolder.get()
|
||||||
|
set(value) = randomHolder.accept(value)
|
||||||
|
|
||||||
private fun initializeFrom(other: LuaThread) {
|
private fun initializeFrom(other: LuaThread) {
|
||||||
|
randomHolder = other.randomHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newThread(): LuaThread {
|
fun newThread(): LuaThread {
|
||||||
@ -145,7 +199,7 @@ class LuaThread private constructor(
|
|||||||
CallContext.getCallContext(Type.POINTER, arrayOf(Type.POINTER, Type.ULONG_LONG, Type.POINTER), CallingConvention.DEFAULT, false)
|
CallContext.getCallContext(Type.POINTER, arrayOf(Type.POINTER, Type.ULONG_LONG, Type.POINTER), CallingConvention.DEFAULT, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
this.throwLoadError(LuaJNR.INSTANCE.lua_load(this.pointer, closure.address, 0L, chunkName, "t"))
|
throwLoadError(LuaJNR.INSTANCE.lua_load(pointer, closure.address, 0L, chunkName, "t"))
|
||||||
closure.dispose()
|
closure.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +213,66 @@ class LuaThread private constructor(
|
|||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val attachedScripts = ArrayList<AssetPath>()
|
||||||
|
private var initCalled = false
|
||||||
|
|
||||||
|
fun initScripts(callInit: Boolean = true): Boolean {
|
||||||
|
check(!initCalled) { "Already initialized scripts!" }
|
||||||
|
initCalled = true
|
||||||
|
|
||||||
|
if (attachedScripts.isEmpty()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val loadScripts = attachedScripts.map { Starbound.readLuaScript(it.fullPath) to it.fullPath }
|
||||||
|
attachedScripts.clear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// minor hiccups during unpopulated script cache should be tolerable
|
||||||
|
for ((chunk, path) in loadScripts) {
|
||||||
|
load(chunk.join(), "@$path")
|
||||||
|
call()
|
||||||
|
}
|
||||||
|
} catch (err: Exception) {
|
||||||
|
LOGGER.error("Failed to attach scripts to Lua environment", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (callInit) {
|
||||||
|
val type = loadGlobal("init")
|
||||||
|
|
||||||
|
if (type == LuaType.FUNCTION) {
|
||||||
|
call()
|
||||||
|
} else if (type == LuaType.NIL || type == LuaType.NONE) {
|
||||||
|
pop()
|
||||||
|
} else {
|
||||||
|
pop()
|
||||||
|
throw LuaRuntimeException("init is not a function: $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err: Exception) {
|
||||||
|
LOGGER.error("Failed to call init() in Lua environment", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attach(script: AssetPath) {
|
||||||
|
if (initCalled) {
|
||||||
|
// minor hiccups during unpopulated script cache should be tolerable
|
||||||
|
load(Starbound.readLuaScript(script.fullPath).join(), "@" + script.fullPath)
|
||||||
|
call()
|
||||||
|
} else {
|
||||||
|
attachedScripts.add(script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attach(script: Collection<AssetPath>) {
|
||||||
|
script.forEach { attach(it) }
|
||||||
|
}
|
||||||
|
|
||||||
fun getString(stackIndex: Int = -1, limit: Long = DEFAULT_STRING_LIMIT): String? {
|
fun getString(stackIndex: Int = -1, limit: Long = DEFAULT_STRING_LIMIT): String? {
|
||||||
if (!this.isString(stackIndex))
|
if (!this.isString(stackIndex))
|
||||||
return null
|
return null
|
||||||
@ -263,20 +377,7 @@ class LuaThread private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun typeAt(stackIndex: Int = -1): LuaType {
|
fun typeAt(stackIndex: Int = -1): LuaType {
|
||||||
return when (val value = LuaJNR.INSTANCE.lua_type(this.pointer, stackIndex)) {
|
return LuaType.valueOf(LuaJNR.INSTANCE.lua_type(this.pointer, stackIndex))
|
||||||
LUA_TNONE -> LuaType.NONE
|
|
||||||
LUA_TNIL -> LuaType.NIL
|
|
||||||
LUA_TBOOLEAN -> LuaType.BOOLEAN
|
|
||||||
LUA_TLIGHTUSERDATA -> LuaType.LIGHTUSERDATA
|
|
||||||
LUA_TNUMBER -> LuaType.NUMBER
|
|
||||||
LUA_TSTRING -> LuaType.STRING
|
|
||||||
LUA_TTABLE -> LuaType.TABLE
|
|
||||||
LUA_TFUNCTION -> LuaType.FUNCTION
|
|
||||||
LUA_TUSERDATA -> LuaType.USERDATA
|
|
||||||
LUA_TTHREAD -> LuaType.THREAD
|
|
||||||
LUA_NUMTYPES -> LuaType.UMTYPES
|
|
||||||
else -> throw RuntimeException("Invalid Lua type: $value")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -621,8 +722,8 @@ class LuaThread private constructor(
|
|||||||
LuaJNR.INSTANCE.lua_setglobal(this.pointer, name)
|
LuaJNR.INSTANCE.lua_setglobal(this.pointer, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadGlobal(name: String) {
|
fun loadGlobal(name: String): LuaType {
|
||||||
LuaJNR.INSTANCE.lua_getglobal(this.pointer, name)
|
return LuaType.valueOf(LuaJNR.INSTANCE.lua_getglobal(this.pointer, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ArgStack(val top: Int) {
|
inner class ArgStack(val top: Int) {
|
||||||
|
@ -6,6 +6,15 @@ LUA_HINT_NONE = 0
|
|||||||
LUA_HINT_ARRAY = 1
|
LUA_HINT_ARRAY = 1
|
||||||
LUA_HINT_OBJECT = 2
|
LUA_HINT_OBJECT = 2
|
||||||
|
|
||||||
|
local rawset = rawset
|
||||||
|
local type = type
|
||||||
|
local pairs = pairs
|
||||||
|
local ipairs = ipairs
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local select = select
|
||||||
|
local math = math
|
||||||
|
local string = string
|
||||||
|
|
||||||
-- this replicates original engine code, but it shouldn't work in first place
|
-- this replicates original engine code, but it shouldn't work in first place
|
||||||
local function __newindex(self, key, value)
|
local function __newindex(self, key, value)
|
||||||
local nils = getmetatable(self).__nils
|
local nils = getmetatable(self).__nils
|
||||||
@ -107,10 +116,12 @@ function jresize(self, target)
|
|||||||
|
|
||||||
if meta and meta.__nils then
|
if meta and meta.__nils then
|
||||||
local indices = {}
|
local indices = {}
|
||||||
|
local i = 1
|
||||||
|
|
||||||
for k, v in pairs(meta.__nils) do
|
for k, v in pairs(meta.__nils) do
|
||||||
if type(k) == 'number' and k % 1.0 == 0.0 and k > target then
|
if type(k) == 'number' and k % 1.0 == 0.0 and k > target then
|
||||||
table.insert(indices, k)
|
indices[i] = k
|
||||||
|
i = i + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -120,10 +131,12 @@ function jresize(self, target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local indices = {}
|
local indices = {}
|
||||||
|
local i = 1
|
||||||
|
|
||||||
for k, v in pairs(self) do
|
for k, v in pairs(self) do
|
||||||
if type(k) == 'number' and k % 1.0 == 0.0 and k > target then
|
if type(k) == 'number' and k % 1.0 == 0.0 and k > target then
|
||||||
table.insert(indices, k)
|
indices[i] = k
|
||||||
|
i = i + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -132,14 +145,116 @@ function jresize(self, target)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local __print = __print
|
do
|
||||||
|
local __print = __print
|
||||||
|
|
||||||
function print(...)
|
function print(...)
|
||||||
local values = {...}
|
local values = {}
|
||||||
|
|
||||||
for i, v in ipairs(values) do
|
-- this is required for properly printing nils
|
||||||
values[i] = tostring(v)
|
for i = 1, select('#', ...) do
|
||||||
|
values[i] = tostring(select(i, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
__print(table.concat(values, '\t'))
|
__print(table.concat(values, '\t'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local __require = __require
|
||||||
|
local loadedScripts = {}
|
||||||
|
|
||||||
|
function require(path, ...)
|
||||||
|
if type(path) ~= 'string' then
|
||||||
|
error('bad argument #1 to require: string expected, got ' .. type(path), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.sub(path, 1, 1) ~= '/' then
|
||||||
|
error('require: script path must be absolute: ' .. path)
|
||||||
|
end
|
||||||
|
|
||||||
|
if loadedScripts[path] then return unpack(loadedScripts[path]) end
|
||||||
|
local fn = __require(path)
|
||||||
|
|
||||||
|
if fn then
|
||||||
|
local result = {fn(...)}
|
||||||
|
loadedScripts[path] = result
|
||||||
|
return unpack(result)
|
||||||
|
else
|
||||||
|
print('Failed to require Lua script ' .. path)
|
||||||
|
loadedScripts[path] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local __random_double = __random_double
|
||||||
|
local __random_long = __random_long
|
||||||
|
local __random_seed = __random_seed
|
||||||
|
local floor = math.floor
|
||||||
|
|
||||||
|
function math.random(a, b)
|
||||||
|
local tA = type(a)
|
||||||
|
local tB = type(b)
|
||||||
|
|
||||||
|
if tA == 'nil' and tB == 'nil' then
|
||||||
|
return __random_double()
|
||||||
|
else
|
||||||
|
local low, high
|
||||||
|
|
||||||
|
if tB == 'nil' then
|
||||||
|
if tA ~= 'number' then
|
||||||
|
error('bad argument #1 to math.random: integer expected, got ' .. tA, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
low = 1
|
||||||
|
high = tA
|
||||||
|
|
||||||
|
if high % 1.0 ~= 0.0 then
|
||||||
|
error('bad argument #1 to math.random: integer expected, got double', 2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if tA ~= 'number' then
|
||||||
|
error('bad argument #1 to math.random: number expected, got ' .. tA, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if tA ~= 'number' then
|
||||||
|
error('bad argument #2 to math.random: number expected, got ' .. tB, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
low = tA
|
||||||
|
high = tB
|
||||||
|
|
||||||
|
if low % 1.0 ~= 0.0 then
|
||||||
|
error('bad argument #1 to math.random: integer expected, got double', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if high % 1.0 ~= 0.0 then
|
||||||
|
error('bad argument #2 to math.random: integer expected, got double', 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if low > high then
|
||||||
|
error('interval is empty (low: ' .. low .. ', high: ' .. high .. ')', 2)
|
||||||
|
elseif low == high then
|
||||||
|
return floor(low)
|
||||||
|
elseif high >= 9223372036854775807 || low <= -9223372036854775808 then
|
||||||
|
error('interval too large', 2)
|
||||||
|
else
|
||||||
|
return __random_long(floor(low), floor(high))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function math.randomseed(seed)
|
||||||
|
if type(seed) ~= 'number' then
|
||||||
|
error('bad argument #1 to math.randomseed: integer expected, got ' .. type(seed), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if seed % 1.0 ~= 0.0 then
|
||||||
|
error('bad argument #1 to math.randomseed: integer expected, got double', 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
__random_seed(floor(seed))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user