Partial utility bindings

This commit is contained in:
DBotThePony 2024-12-18 13:02:13 +07:00
parent d627526088
commit 658dffc832
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 288 additions and 117 deletions

View File

@ -119,6 +119,9 @@ In addition to `add`, `multiply`, `merge` and `override` new merge methods are a
* Example: Status controller scripts now get `monster` bindings when running in context of Monster's status controller, etc * Example: Status controller scripts now get `monster` bindings when running in context of Monster's status controller, etc
* `behavior.behavior` third argument (specified commonly as `_ENV`) is ignored and can be omitted (set to nil) * `behavior.behavior` third argument (specified commonly as `_ENV`) is ignored and can be omitted (set to nil)
* It was used solely to get Lua engine (Lua execution context), and could have been deprecated long time ago even in original engine, because there is now a way in original engine to get Lua engine when binding is called * It was used solely to get Lua engine (Lua execution context), and could have been deprecated long time ago even in original engine, because there is now a way in original engine to get Lua engine when binding is called
* Added `sb.logFatal`, similar to other log functions
* `print(...)` now prints to both console (stdout) and logs
* `sb.log` functions now accept everything `string.format` accepts, and not only `%s` and `%%`
## Random ## Random
* Added `random:randn(deviation: double, mean: double): double`, returns normally distributed double, where `deviation` stands for [Standard deviation](https://en.wikipedia.org/wiki/Standard_deviation), and `mean` specifies middle point * Added `random:randn(deviation: double, mean: double): double`, returns normally distributed double, where `deviation` stands for [Standard deviation](https://en.wikipedia.org/wiki/Standard_deviation), and `mean` specifies middle point

View File

@ -374,6 +374,14 @@ open class ItemStack(val entry: ItemRegistry.Entry, val config: JsonObject, para
return createDescriptor().toJson() return createDescriptor().toJson()
} }
fun toTable(allocator: TableFactory): Table? {
if (isEmpty) {
return null
}
return createDescriptor().toTable(allocator)
}
class Adapter(gson: Gson) : TypeAdapter<ItemStack>() { class Adapter(gson: Gson) : TypeAdapter<ItemStack>() {
override fun write(out: JsonWriter, value: ItemStack?) { override fun write(out: JsonWriter, value: ItemStack?) {
val json = value?.toJson() val json = value?.toJson()

View File

@ -206,8 +206,8 @@ class LuaEnvironment : StateContext {
// TODO: NYI, maybe polyfill? // TODO: NYI, maybe polyfill?
Utf8Lib.installInto(this, globals) Utf8Lib.installInto(this, globals)
provideRootBindings(this) // provideRootBindings(this)
provideUtilityBindings(this) // provideUtilityBindings(this)
} }
private val scripts = ObjectArraySet<ChunkFactory>() private val scripts = ObjectArraySet<ChunkFactory>()

View File

@ -22,6 +22,8 @@ import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.AssetPath import ru.dbotthepony.kstarbound.defs.AssetPath
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
import ru.dbotthepony.kstarbound.lua.bindings.provideRootBindings
import ru.dbotthepony.kstarbound.lua.bindings.provideUtilityBindings
import ru.dbotthepony.kstarbound.util.random.random 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
@ -71,47 +73,8 @@ class LuaThread private constructor(
LuaJNR.INSTANCE.luaopen_utf8(this.pointer) LuaJNR.INSTANCE.luaopen_utf8(this.pointer)
this.storeGlobal("utf8") this.storeGlobal("utf8")
push { provideUtilityBindings(this)
LOGGER.info(it.nextString()) provideRootBindings(this)
0
}
storeGlobal("__print")
push {
val path = it.nextString()
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(it.nextLong(), it.nextLong()))
1
}
storeGlobal("__random_long")
push {
random = random(it.nextLong())
0
}
storeGlobal("__random_seed")
load(globalScript, "@starbound.jar!/scripts/global.lua") load(globalScript, "@starbound.jar!/scripts/global.lua")
call() call()
@ -818,6 +781,10 @@ class LuaThread private constructor(
return this@LuaThread.typeAt(position) return this@LuaThread.typeAt(position)
} }
fun hasNext(): Boolean {
return position <= top
}
fun nextString(position: Int = this.position++, limit: Long = DEFAULT_STRING_LIMIT): String { fun nextString(position: Int = this.position++, limit: Long = DEFAULT_STRING_LIMIT): String {
if (position !in 1 ..this.top) if (position !in 1 ..this.top)
throw IllegalArgumentException("bad argument #$position: string expected, got nil") throw IllegalArgumentException("bad argument #$position: string expected, got nil")
@ -851,6 +818,13 @@ class LuaThread private constructor(
return value ?: throw IllegalArgumentException("bad argument #$position: anything expected, got ${this@LuaThread.typeAt(position)}") return value ?: throw IllegalArgumentException("bad argument #$position: anything expected, got ${this@LuaThread.typeAt(position)}")
} }
fun nextOptionalJson(position: Int = this.position++, limit: Long = DEFAULT_STRING_LIMIT): JsonElement? {
if (position !in 1 ..this.top)
return null
return this@LuaThread.getJson(position, limit = limit)
}
fun nextTable(position: Int = this.position++, limit: Long = DEFAULT_STRING_LIMIT): JsonObject { fun nextTable(position: Int = this.position++, limit: Long = DEFAULT_STRING_LIMIT): JsonObject {
if (position !in 1 ..this.top) if (position !in 1 ..this.top)
throw IllegalArgumentException("bad argument #$position: table expected, got nil") throw IllegalArgumentException("bad argument #$position: table expected, got nil")

View File

@ -1,25 +1,27 @@
package ru.dbotthepony.kstarbound.lua.bindings package ru.dbotthepony.kstarbound.lua.bindings
import com.google.gson.JsonElement import com.google.gson.JsonElement
import org.apache.logging.log4j.LogManager import com.google.gson.JsonNull
import org.classdump.luna.ByteString import org.classdump.luna.ByteString
import org.classdump.luna.Table import org.classdump.luna.Table
import org.classdump.luna.runtime.ExecutionContext import org.classdump.luna.runtime.ExecutionContext
import ru.dbotthepony.kommons.util.XXHash32
import ru.dbotthepony.kommons.util.XXHash64
import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.math.vector.Vector2d
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters
import ru.dbotthepony.kstarbound.json.JsonPath import ru.dbotthepony.kstarbound.json.JsonPath
import ru.dbotthepony.kstarbound.lua.LuaEnvironment import ru.dbotthepony.kstarbound.lua.LuaEnvironment
import ru.dbotthepony.kstarbound.lua.LuaThread
import ru.dbotthepony.kstarbound.lua.LuaThread.Companion
import ru.dbotthepony.kstarbound.lua.LuaType
import ru.dbotthepony.kstarbound.lua.from import ru.dbotthepony.kstarbound.lua.from
import ru.dbotthepony.kstarbound.lua.get import ru.dbotthepony.kstarbound.lua.get
import ru.dbotthepony.kstarbound.lua.luaFunction import ru.dbotthepony.kstarbound.lua.luaFunction
import ru.dbotthepony.kstarbound.lua.luaFunctionArray import ru.dbotthepony.kstarbound.lua.luaFunctionArray
import ru.dbotthepony.kstarbound.lua.luaFunctionN import ru.dbotthepony.kstarbound.lua.luaFunctionN
import ru.dbotthepony.kstarbound.lua.nextOptionalFloat
import ru.dbotthepony.kstarbound.lua.set import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.lua.toByteString
import ru.dbotthepony.kstarbound.lua.toJson import ru.dbotthepony.kstarbound.lua.toJson
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
import ru.dbotthepony.kstarbound.lua.toVector2d import ru.dbotthepony.kstarbound.lua.toVector2d
import ru.dbotthepony.kstarbound.lua.userdata.LuaPerlinNoise import ru.dbotthepony.kstarbound.lua.userdata.LuaPerlinNoise
import ru.dbotthepony.kstarbound.lua.userdata.LuaRandomGenerator import ru.dbotthepony.kstarbound.lua.userdata.LuaRandomGenerator
@ -28,26 +30,17 @@ import ru.dbotthepony.kstarbound.util.SBPattern
import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise
import ru.dbotthepony.kstarbound.util.random.nextNormalDouble import ru.dbotthepony.kstarbound.util.random.nextNormalDouble
import ru.dbotthepony.kstarbound.util.random.random import ru.dbotthepony.kstarbound.util.random.random
import ru.dbotthepony.kstarbound.util.random.staticRandom32 import ru.dbotthepony.kstarbound.util.random.staticRandom32FromList
import ru.dbotthepony.kstarbound.util.random.staticRandom64FromList
import ru.dbotthepony.kstarbound.util.random.staticRandomDouble import ru.dbotthepony.kstarbound.util.random.staticRandomDouble
import ru.dbotthepony.kstarbound.util.random.staticRandomDoubleFromList
import ru.dbotthepony.kstarbound.util.random.staticRandomIntFromList
import ru.dbotthepony.kstarbound.util.random.staticRandomLong import ru.dbotthepony.kstarbound.util.random.staticRandomLong
import ru.dbotthepony.kstarbound.util.random.staticRandomLongFromList
import ru.dbotthepony.kstarbound.util.random.toBytes
import ru.dbotthepony.kstarbound.util.toStarboundString import ru.dbotthepony.kstarbound.util.toStarboundString
import java.util.* import java.util.*
import java.util.random.RandomGenerator import kotlin.collections.ArrayList
private val LOGGER = LogManager.getLogger()
private val logInfo = luaFunctionN("logInfo") { args ->
LOGGER.info(args.nextString().toString().format(*args.copyRemaining()))
}
private val logWarn = luaFunctionN("logWarn") { args ->
LOGGER.warn(args.nextString().toString().format(*args.copyRemaining()))
}
private val logError = luaFunctionN("logError") { args ->
LOGGER.error(args.nextString().toString().format(*args.copyRemaining()))
}
private val interpolateSinEase = luaFunctionArray { args -> private val interpolateSinEase = luaFunctionArray { args ->
if (args.size < 3) if (args.size < 3)
@ -71,75 +64,224 @@ private val interpolateSinEase = luaFunctionArray { args ->
} }
} }
private val replaceTags = luaFunction { string: ByteString, tags: Table -> // TODO: Lua-side implementation for better performance?
returnBuffer.setTo(SBPattern.of(string.toString()).resolveOrSkip({ tags[it]?.toString() }).toByteString()) private fun replaceTags(args: LuaThread.ArgStack): Int {
val string = args.nextString()
val tagsList = args.lua
.readTable(
args.position++,
{ getString(it) ?: throw IllegalStateException("Tags table contain non-string keys") },
{ getString(it) ?: throw IllegalStateException("Tags table contain non-string values") }
) ?: throw IllegalArgumentException("bad argument #2: table expected, got ${args.peek(args.position - 1)}")
val tags = tagsList.toMap()
args.lua.push(SBPattern.of(string).resolveOrSkip({ tags[it] }))
return 1
} }
private val makePerlinSource = luaFunction { settings: Table -> private val makePerlinSource = luaFunction { settings: Table ->
returnBuffer.setTo(LuaPerlinNoise(AbstractPerlinNoise.of(Starbound.gson.fromJson(settings.toJson(), PerlinNoiseParameters::class.java)))) returnBuffer.setTo(LuaPerlinNoise(AbstractPerlinNoise.of(Starbound.gson.fromJson(settings.toJson(), PerlinNoiseParameters::class.java))))
} }
private val staticRandomI32 = luaFunctionArray { private fun hash32(args: LuaThread.ArgStack): Int {
returnBuffer.setTo(staticRandom32(*it)) val digest = XXHash32(2938728349.toInt())
while (args.hasNext()) {
when (args.peek()) {
LuaType.BOOLEAN -> digest.update(if (args.nextBoolean()) 1 else 0)
LuaType.NUMBER -> toBytes(digest::update, args.nextDouble())
LuaType.STRING -> digest.update(args.nextString().toByteArray(Charsets.UTF_8))
else -> throw IllegalArgumentException("bad argument #${args.position} to staticRandomI32")
}
} }
private val staticRandomDouble = luaFunctionArray { return digest.digestAsInt()
returnBuffer.setTo(staticRandomDouble(*it))
} }
private val staticRandomDoubleRange = luaFunctionN("staticRandomDoubleRange") { private fun hash64(args: LuaThread.ArgStack): Long {
val min = it.nextFloat() val digest = XXHash64(1997293021376312589L)
val max = it.nextFloat()
returnBuffer.setTo(staticRandomDouble(*it.copyRemaining()) * (max - min) + min) while (args.hasNext()) {
when (args.peek()) {
LuaType.BOOLEAN -> digest.update(if (args.nextBoolean()) 1 else 0)
LuaType.NUMBER -> toBytes(digest::update, args.nextDouble())
LuaType.STRING -> digest.update(args.nextString().toByteArray(Charsets.UTF_8))
else -> throw IllegalArgumentException("bad argument #${args.position} to staticRandomI32")
}
} }
private val staticRandomI32Range = luaFunctionN("staticRandomI32Range") { return digest.digestAsLong()
val min = it.nextInteger()
val max = it.nextInteger()
returnBuffer.setTo(staticRandomLong(min, max, *it.copyRemaining()))
} }
private val mergeJson = luaFunction { a: Any?, b: Any? -> private fun staticRandomI32(args: LuaThread.ArgStack): Int {
returnBuffer.setTo(from(ru.dbotthepony.kstarbound.json.mergeJson(toJsonFromLua(a), toJsonFromLua(b)))) args.lua.push(hash32(args).toLong())
return 1
} }
fun provideUtilityBindings(lua: LuaEnvironment) { private fun staticRandomI64(args: LuaThread.ArgStack): Int {
val table = lua.newTable() args.lua.push(hash64(args))
lua.globals["sb"] = table return 1
table["makeUuid"] = luaFunction {
returnBuffer.setTo(UUID(lua.random.nextLong(), lua.random.nextLong()).toStarboundString().toByteString())
} }
table["logInfo"] = logInfo private fun staticRandomDouble(args: LuaThread.ArgStack): Int {
table["logWarn"] = logWarn args.lua.push(hash64(args).ushr(11) * 1.1102230246251565E-16)
table["logError"] = logError return 1
table["nrand"] = luaFunctionN("nrand") { args ->
val stdev = args.nextOptionalFloat() ?: 1.0
val mean = args.nextOptionalFloat() ?: 0.0
lua.random.nextNormalDouble(stdev, mean)
} }
table["print"] = lua.globals["tostring"] private fun staticRandomDoubleRange(args: LuaThread.ArgStack): Int {
val min = args.nextDouble()
val max = args.nextDouble()
val double = hash64(args).ushr(11) * 1.1102230246251565E-16
args.lua.push(double * (max - min) + min)
return 1
}
// FIXME: incorrect.
private fun staticRandomI64Range(args: LuaThread.ArgStack): Int {
val min = args.nextDouble()
val max = args.nextDouble()
val double = hash64(args).ushr(11) * 1.1102230246251565E-16
args.lua.push((min + (max - min + 1L) * double).toLong())
return 1
}
// FIXME: incorrect.
private fun staticRandomI32Range(args: LuaThread.ArgStack): Int {
val min = args.nextDouble()
val max = args.nextDouble()
val double = hash64(args).ushr(11) * 1.1102230246251565E-16
args.lua.push((min + (max - min + 1L) * double).toInt().toLong())
return 1
}
// TODO: Lua-side implementation for better performance?
private fun makeUuid(args: LuaThread.ArgStack): Int {
args.lua.push(UUID(args.lua.random.nextLong(), args.lua.random.nextLong()).toStarboundString())
return 1
}
private fun nrand(args: LuaThread.ArgStack): Int {
val stdev = args.nextOptionalDouble() ?: 1.0
val mean = args.nextOptionalDouble() ?: 0.0
args.lua.push(args.lua.random.nextNormalDouble(stdev, mean))
return 1
}
// TODO: Lua-side implementation for better performance?
private fun jsonMerge(args: LuaThread.ArgStack): Int {
val a = args.nextOptionalJson() ?: JsonNull.INSTANCE
val b = args.nextOptionalJson() ?: JsonNull.INSTANCE
args.lua.push(ru.dbotthepony.kstarbound.json.mergeJson(a, b))
return 1
}
// TODO: Lua-side implementation for better performance?
private fun jsonQuery(args: LuaThread.ArgStack): Int {
val json = args.nextOptionalJson() ?: JsonNull.INSTANCE
val path = args.nextString()
val default = args.nextOptionalJson() ?: JsonNull.INSTANCE
args.lua.push(JsonPath.query(path).get(json, default))
return 1
}
fun provideUtilityBindings(lua: LuaThread) {
with(lua) {
push {
LuaThread.LOGGER.info(it.nextString())
0
}
storeGlobal("__print")
push {
LuaThread.LOGGER.warn(it.nextString())
0
}
storeGlobal("__print_warn")
push {
LuaThread.LOGGER.error(it.nextString())
0
}
storeGlobal("__print_error")
push {
LuaThread.LOGGER.fatal(it.nextString())
0
}
storeGlobal("__print_fatal")
push {
val path = it.nextString()
try {
load(Starbound.readLuaScript(path).join(), "@$path")
1
} catch (err: Exception) {
LuaThread.LOGGER.error("Exception loading Lua script $path", err)
throw err
}
}
storeGlobal("__require")
push {
push(random.nextDouble())
1
}
storeGlobal("__random_double")
push {
push(random.nextLong(it.nextLong(), it.nextLong()))
1
}
storeGlobal("__random_long")
push {
random = random(it.nextLong())
0
}
storeGlobal("__random_seed")
}
lua.pushTable()
lua.dup()
lua.storeGlobal("sb")
lua.setTableValue("makeUuid", ::makeUuid)
lua.setTableValue("nrand", ::nrand)
lua.setTableValue("jsonMerge", ::jsonMerge)
lua.setTableValue("jsonQuery", ::jsonQuery)
lua.setTableValue("replaceTags", ::replaceTags)
lua.setTableValue("staticRandomI32", ::staticRandomI32)
lua.setTableValue("staticRandomI32Range", ::staticRandomI32Range)
lua.setTableValue("staticRandomI64", ::staticRandomI64)
lua.setTableValue("staticRandomI64Range", ::staticRandomI64Range)
lua.setTableValue("staticRandomDouble", ::staticRandomDouble)
lua.setTableValue("staticRandomDoubleRange", ::staticRandomDoubleRange)
/*table["print"] = lua.globals["tostring"]
table["printJson"] = lua.globals["tostring"] table["printJson"] = lua.globals["tostring"]
table["interpolateSinEase"] = interpolateSinEase table["interpolateSinEase"] = interpolateSinEase
table["replaceTags"] = replaceTags
table["makeRandomSource"] = luaFunction { seed: Long? -> table["makeRandomSource"] = luaFunction { seed: Long? ->
returnBuffer.setTo(LuaRandomGenerator(random(seed ?: lua.random.nextLong()))) returnBuffer.setTo(LuaRandomGenerator(random(seed ?: lua.random.nextLong())))
} }
table["makePerlinSource"] = makePerlinSource table["makePerlinSource"] = makePerlinSource*/
table["staticRandomI32"] = staticRandomI32 lua.pop()
table["staticRandomI64"] = staticRandomI32
table["staticRandomDouble"] = staticRandomDouble
table["staticRandomDoubleRange"] = staticRandomDoubleRange
table["staticRandomI32Range"] = staticRandomI32Range
table["staticRandomI64Range"] = staticRandomI32Range
table["jsonMerge"] = mergeJson
} }
fun provideConfigBindings(lua: LuaEnvironment, lookup: ExecutionContext.(path: JsonPath, ifMissing: Any?) -> Any?) { fun provideConfigBindings(lua: LuaEnvironment, lookup: ExecutionContext.(path: JsonPath, ifMissing: Any?) -> Any?) {

View File

@ -35,19 +35,19 @@ fun random(seed: Long = System.nanoTime()): RandomGenerator {
*/ */
val threadLocalRandom: RandomGenerator by ThreadLocal.withInitial { random() } val threadLocalRandom: RandomGenerator by ThreadLocal.withInitial { random() }
private fun toBytes(accept: ByteConsumer, value: Short) { fun toBytes(accept: ByteConsumer, value: Short) {
accept.accept(value.toByte()) accept.accept(value.toByte())
accept.accept((value.toInt() ushr 8).toByte()) accept.accept((value.toInt() ushr 8).toByte())
} }
private fun toBytes(accept: ByteConsumer, value: Int) { fun toBytes(accept: ByteConsumer, value: Int) {
accept.accept(value.toByte()) accept.accept(value.toByte())
accept.accept((value ushr 8).toByte()) accept.accept((value ushr 8).toByte())
accept.accept((value ushr 16).toByte()) accept.accept((value ushr 16).toByte())
accept.accept((value ushr 24).toByte()) accept.accept((value ushr 24).toByte())
} }
private fun toBytes(accept: ByteConsumer, value: Long) { fun toBytes(accept: ByteConsumer, value: Long) {
accept.accept(value.toByte()) accept.accept(value.toByte())
accept.accept((value ushr 8).toByte()) accept.accept((value ushr 8).toByte())
accept.accept((value ushr 16).toByte()) accept.accept((value ushr 16).toByte())
@ -58,15 +58,19 @@ private fun toBytes(accept: ByteConsumer, value: Long) {
accept.accept((value ushr 56).toByte()) accept.accept((value ushr 56).toByte())
} }
private fun toBytes(accept: ByteConsumer, value: Double) { fun toBytes(accept: ByteConsumer, value: Double) {
toBytes(accept, value.toBits()) toBytes(accept, value.toBits())
} }
private fun toBytes(accept: ByteConsumer, value: Float) { fun toBytes(accept: ByteConsumer, value: Float) {
toBytes(accept, value.toBits()) toBytes(accept, value.toBits())
} }
fun staticRandom32(vararg values: Any?): Int { fun staticRandom32(vararg values: Any?): Int {
return staticRandom32FromList(values.asIterable())
}
fun staticRandom32FromList(values: Iterable<Any?>): Int {
val digest = XXHash32(2938728349.toInt()) val digest = XXHash32(2938728349.toInt())
for (value in values) { for (value in values) {
@ -96,6 +100,10 @@ fun staticRandomDouble(vararg values: Any?): Double {
return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16 return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16
} }
fun staticRandomDoubleFromList(values: Iterable<Any?>): Double {
return staticRandom64FromList(values).ushr(11) * 1.1102230246251565E-16
}
fun staticRandomInt(min: Int, max: Int, vararg values: Any?): Int { fun staticRandomInt(min: Int, max: Int, vararg values: Any?): Int {
val hash = staticRandomDouble(*values) val hash = staticRandomDouble(*values)
return (min + (max - min + 1) * hash).toInt() return (min + (max - min + 1) * hash).toInt()
@ -106,7 +114,21 @@ fun staticRandomLong(min: Long, max: Long, vararg values: Any?): Long {
return (min + (max - min + 1L) * hash).toLong() return (min + (max - min + 1L) * hash).toLong()
} }
fun staticRandomIntFromList(min: Int, max: Int, values: Iterable<Any?>): Int {
val hash = staticRandomDoubleFromList(values)
return (min + (max - min + 1) * hash).toInt()
}
fun staticRandomLongFromList(min: Long, max: Long, values: Iterable<Any?>): Long {
val hash = staticRandomDoubleFromList(values)
return (min + (max - min + 1L) * hash).toLong()
}
fun staticRandom64(vararg values: Any?): Long { fun staticRandom64(vararg values: Any?): Long {
return staticRandom64FromList(values.asIterable())
}
fun staticRandom64FromList(values: Iterable<Any?>): Long {
val digest = XXHash64(1997293021376312589L) val digest = XXHash64(1997293021376312589L)
for (value in values) { for (value in values) {

View File

@ -147,6 +147,10 @@ end
do do
local __print = __print local __print = __print
local __print_warn = __print_warn
local __print_error = __print_error
local __print_fatal = __print_fatal
local format = string.format
function print(...) function print(...)
local values = {} local values = {}
@ -158,6 +162,22 @@ do
__print(table.concat(values, '\t')) __print(table.concat(values, '\t'))
end end
function sb.logInfo(text, ...)
__print(format(text, ...))
end
function sb.logWarn(text, ...)
__print_warn(format(text, ...))
end
function sb.logError(text, ...)
__print_error(format(text, ...))
end
function sb.logFatal(text, ...)
__print_fatal(format(text, ...))
end
end end
do do
@ -258,3 +278,5 @@ do
__random_seed(floor(seed)) __random_seed(floor(seed))
end end
end end