More stuff

Scrolling callbacks
Improved btree reader
noclip controls
This commit is contained in:
DBotThePony 2022-08-04 17:28:37 +07:00
parent dcc06319c6
commit 51a43d70be
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 214 additions and 84 deletions

View File

@ -40,6 +40,7 @@ fun main() {
}
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
//val db = BTreeDB(File("world.world"))
/*if (true) {
val a = System.currentTimeMillis()
@ -89,6 +90,7 @@ fun main() {
//Starbound.addFilePath(File("./unpacked_assets/"))
Starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak"))
//Starbound.addPakPath(File("packed.pak"))
Starbound.initializeGame { finished, replaceStatus, status ->
client.putDebugLog(status, replaceStatus)
@ -163,14 +165,19 @@ fun main() {
}
println("$find $set $parse")
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
}
//ent.position += Vector2d(y = 14.0, x = -10.0)
ent.position = Vector2d(128.0 + 16.0, 672.0 + 48.0)
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
client.camera.pos.x = 616f
client.camera.pos.y = 721f
client.onDrawGUI {
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
client.gl.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
client.gl.font.render("${client.camera.pos}", y = 140f, scale = 0.25f)
}
client.onPreDrawWorld {
@ -178,8 +185,6 @@ fun main() {
//client.camera.pos.y = ent.pos.y.toFloat()
}
client.camera.pos.y = 10f
client.gl.box2dRenderer.drawShapes = false
client.gl.box2dRenderer.drawPairs = false
client.gl.box2dRenderer.drawAABB = false
@ -187,19 +192,33 @@ fun main() {
ent.spawn()
client.input.addScrollCallback { _, x, y ->
if (y > 0.0) {
client.settings.scale *= y.toFloat() * 2f
} else if (y < 0.0) {
client.settings.scale /= -y.toFloat() * 2f
}
}
while (client.renderFrame()) {
Starbound.pollCallbacks()
//ent.think(client.frameRenderTime)
client.camera.pos.x = ent.position.x.toFloat()
client.camera.pos.y = ent.position.y.toFloat()
//client.camera.pos.x = ent.position.x.toFloat()
//client.camera.pos.y = ent.position.y.toFloat()
client.camera.pos.x += if (client.input.KEY_LEFT_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.x += if (client.input.KEY_RIGHT_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.y += if (client.input.KEY_UP_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.y += if (client.input.KEY_DOWN_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)
//if (ent.onGround)
//ent.velocity += client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1
if (client.input.KEY_LEFT_DOWN) {
/*if (client.input.KEY_LEFT_DOWN) {
ent.movement.moveDirection = Move.MOVE_LEFT
} else if (client.input.KEY_RIGHT_DOWN) {
ent.movement.moveDirection = Move.MOVE_RIGHT
@ -211,13 +230,13 @@ fun main() {
ent.movement.requestJump()
} else if (client.input.KEY_SPACE_RELEASED) {
ent.movement.recallJump()
}
}*/
if (client.input.KEY_ESCAPE_PRESSED) {
glfwSetWindowShouldClose(client.window, true)
}
ent.wantsToDuck = client.input.KEY_DOWN_DOWN
//ent.wantsToDuck = client.input.KEY_DOWN_DOWN
//if (chunkA != null && glfwGetTime() < 10.0) {
// val tile = Starbound.getTileDefinition("alienrock")

View File

@ -52,7 +52,7 @@ class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWo
for (layer in parallax.layers) {
client.gl.matrixStack.push()
client.gl.matrixStack.translateWithMultiplication(x = layer.offset.x.toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f, y = layer.offset.y.toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f)
client.gl.matrixStack.translateWithMultiplication(x = layer.offset.x.toFloat() / PIXELS_IN_STARBOUND_UNITf, y = layer.offset.y.toFloat() / PIXELS_IN_STARBOUND_UNITf)
client.gl.shaderVertexTexture.transform.set(client.gl.matrixStack.last)
@ -65,16 +65,16 @@ class ClientWorld(val client: StarboundClient, seed: Long = 0L) : World<ClientWo
builder.begin()
for (xPos in DoubleEdgeProgression()) {
var x0 = xPos.toFloat() * texture.width / PIXELS_IN_STARBOUND_UNITf / 16f
var x1 = (xPos + 1f) * texture.width / PIXELS_IN_STARBOUND_UNITf / 16f
var x0 = xPos.toFloat() * texture.width / PIXELS_IN_STARBOUND_UNITf
var x1 = (xPos + 1f) * texture.width / PIXELS_IN_STARBOUND_UNITf
val diffx = layer.parallax.x * centre.x - centre.x
val diffy = (layer.parallax.y * (centre.y + 20.0) - centre.y - 20.0).toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f
val diffy = (layer.parallax.y * (centre.y + 20.0) - centre.y - 20.0).toFloat() / PIXELS_IN_STARBOUND_UNITf
x0 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f
x1 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f
x0 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
x1 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
builder.quadZ(x0, diffy, x1, diffy + texture.height.toFloat() / PIXELS_IN_STARBOUND_UNITf / 16f, 1f, VertexTransformers.uv(0f, 1f, 1f, 0f))
builder.quadZ(x0, diffy, x1, diffy + texture.height.toFloat() / PIXELS_IN_STARBOUND_UNITf, 1f, VertexTransformers.uv(0f, 1f, 1f, 0f))
/*if (x1 < size.mins.x) {
break

View File

@ -117,7 +117,7 @@ class StarboundClient : AutoCloseable {
GLFW.glfwMakeContextCurrent(window)
// vsync
GLFW.glfwSwapInterval(1)
GLFW.glfwSwapInterval(0)
GLFW.glfwShowWindow(window)
putDebugLog("Initialized GLFW window")

View File

@ -12,6 +12,8 @@ private interface HowLongHeldDown {
operator fun getValue(thisRef: Any?, property: KProperty<*>?): Double
}
typealias ScrollCallback = (invalidate: () -> Unit, x: Double, y: Double) -> Unit
@Suppress("unused")
class UserInput : IUserInput {
fun keyPressed(code: Int) {
@ -129,15 +131,77 @@ class UserInput : IUserInput {
mappedKey.updateState(action)
}
private var lastScrollUpdate = 0
private val scrollCallbacks = ArrayList<ScrollCallback>()
fun addScrollCallback(callback: ScrollCallback): Boolean {
if (!scrollCallbacks.contains(callback)) {
scrollCallbacks.add(callback)
return true
}
return false
}
private var inScrollCallback = false
fun removeScrollCallback(callback: ScrollCallback): Boolean {
if (inScrollCallback) {
throw ConcurrentModificationException("Can't remove scroll callback while iterating them, use invalidate function inside scroll callback itself!")
}
return scrollCallbacks.remove(callback)
}
var lastScrollX: Double = 0.0
private set
var lastScrollY: Double = 0.0
private set
fun callScroll(x: Double, y: Double) {
lastScrollX = x
lastScrollY = y
lastScrollUpdate = frame
val iterator = scrollCallbacks.iterator()
var i = 0
while (iterator.hasNext()) {
i++
val ithis = i
val value = iterator.next()
value.invoke({
if (i != ithis) {
throw IllegalStateException("What the hell did you just do")
}
iterator.remove()
}, x, y)
}
}
fun installCallback(window: Long) {
glfwSetKeyCallback(window) { _, key, scancode, action, mods ->
callChange(key, action)
}
glfwSetScrollCallback(window) { _, x, y ->
callScroll(x, y)
}
}
private var lastTick = 0.0
var frame = 0
private set
fun think() {
val scrollCooldown = lastScrollUpdate != frame
frame++
val thisTime = glfwGetTime()
val delta = thisTime - lastTick
lastTick = thisTime
@ -145,6 +209,11 @@ class UserInput : IUserInput {
for (key in userKeys) {
key.think(delta, thisTime)
}
if (scrollCooldown) {
lastScrollX = 0.0
lastScrollY = 0.0
}
}
private val keyMap = mapOf(

View File

@ -1,12 +1,8 @@
package ru.dbotthepony.kstarbound.io
import com.google.gson.JsonElement
import it.unimi.dsi.fastutil.ints.IntArraySet
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.File
import java.io.InputStream
import java.io.RandomAccessFile
import java.io.*
import java.util.*
private fun readHeader(reader: RandomAccessFile, required: Char) {
val read = reader.read()
@ -63,7 +59,7 @@ class BTreeDB(val path: File) {
readHeader(reader, '5')
}
val blockSize = reader.readInt().toLong()
val blockSize = reader.readInt()
val dbNameRaw = ByteArray(16).also { reader.read(it) }
val indexKeySize = reader.readInt()
val useNodeTwo = reader.readBoolean()
@ -123,7 +119,7 @@ class BTreeDB(val path: File) {
// иначе, ищем следующий блок
// пропускаем оставшиеся данные, переходим на границу текущего блока-лепестка
val delta = (blockSize - 4 - offset).toInt()
val delta = (blockSize - 4 - offset)
reader.skipBytes(delta)
// ищем следующий блок с нашими данными
@ -217,76 +213,30 @@ class BTreeDB(val path: File) {
}
// мы пришли в лепесток, теперь прямолинейно ищем в linked list
var offset = 6
val keyCount = reader.readInt()
val leafStream = DataInputStream(BufferedInputStream(LeafInputStream(2)))
val keyCount = leafStream.readInt()
for (keyIndex in 0 until keyCount) {
// читаем ключ
reader.read(keyLoader)
offset += indexKeySize
leafStream.read(keyLoader)
// читаем размер данных
var (dataLength, readBytes) = reader.readVarIntInfo()
offset += readBytes
val dataLength = leafStream.readVarInt()
// это наш блок
if (keyLoader.contentEquals(key)) {
val binary = ByteArray(dataLength)
var binaryOffset = 0
// читаем данные
while (true) {
// если конец данных внутри текущего блока, останавливаемся
if (offset + dataLength <= blockSize - 4) {
reader.readFully(binary, binaryOffset, dataLength)
offset += dataLength
binaryOffset += dataLength
break
}
// иначе, ищем следующий блок
// пропускаем оставшиеся данные, переходим на границу текущего блока-лепестка
val delta = (blockSize - 4 - offset).toInt()
reader.readFully(binary, binaryOffset, delta)
binaryOffset += delta
// ищем следующий блок с нашими данными
val nextBlockIndex = reader.readInt()
seekBlock(nextBlockIndex.toLong())
// удостоверяемся что мы попали в лепесток
check(readBlockType() == TreeBlockType.LEAF) { "Did not hit leaf block" }
offset = 2
dataLength -= delta
if (dataLength == 0) {
// нет данных (?)
return binary
}
leafStream.readFully(binary)
return binary
} else {
// это не наш блок, пропускаем его
while (true) {
// если конец данных внутри текущего блока, останавливаемся
if (offset + dataLength <= blockSize - 4) {
reader.skipBytes(dataLength)
offset += dataLength
break
}
// иначе, ищем следующий блок
// пропускаем оставшиеся данные, переходим на границу текущего блока-лепестка
val delta = (blockSize - 4 - offset).toInt()
reader.skipBytes(delta)
// ищем следующий блок с нашими данными
val nextBlockIndex = reader.readInt()
seekBlock(nextBlockIndex.toLong())
// удостоверяемся что мы попали в лепесток
check(readBlockType() == TreeBlockType.LEAF) { "Did not hit leaf block" }
offset = 2
dataLength -= delta
}
leafStream.skipBytes(dataLength)
}
}
@ -295,6 +245,70 @@ class BTreeDB(val path: File) {
fun seekBlock(id: Long) {
require(id >= 0) { "Negative id $id" }
require(id * blockSize + blocksOffsetStart < reader.length()) { "Tried to seek block with $id, but it is outside of file's bounds (file size ${reader.length()} bytes, seeking ${id * blockSize + blocksOffsetStart})! (does not exist)" }
reader.seek(id * blockSize + blocksOffsetStart)
}
private inner class LeafInputStream(private var offset: Int) : InputStream() {
private var canRead = true
override fun read(): Int {
if (offset + 4 >= blockSize) {
if (!seekNextBlock()) {
return -1
}
}
offset++
return reader.read()
}
override fun read(b: ByteArray, off: Int, len: Int): Int {
Objects.checkFromIndexSize(off, len, b.size)
var totalRead = 0
var index = off
while (canRead && totalRead < len) {
if (offset + 4 >= blockSize) {
if (!seekNextBlock()) {
return totalRead
}
}
val readAtMost = (blockSize - 4 - offset).coerceAtMost(len - totalRead)
val readBytes = reader.read(b, index, readAtMost)
if (readBytes <= 0) {
canRead = false
break
}
totalRead += readBytes
index += readBytes
offset += readBytes
}
return totalRead
}
private fun seekNextBlock(): Boolean {
if (!canRead) {
return false
}
val nextBlockIndex = reader.readInt()
if (nextBlockIndex < 0L) {
canRead = false
return false
}
seekBlock(nextBlockIndex.toLong())
check(readBlockType() == TreeBlockType.LEAF) { "Did not hit leaf block" }
offset = 2
return true
}
}
}

View File

@ -70,6 +70,28 @@ fun RandomAccessFile.readVarIntInfo(): VarIntReadResult {
return VarIntReadResult(result, i)
}
/**
* Читает Variable Length Integer как Int
*/
fun InputStream.readVarIntInfo(): VarIntReadResult {
var result = 0
var read = read()
var i = 1
while (true) {
result = (result shl 7) or (read and 0x7F)
if (read and 0x80 == 0) {
break
}
read = read()
i++
}
return VarIntReadResult(result, i)
}
/**
* Читает Variable Length Integer как Long
*/

View File

@ -163,7 +163,10 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
// сразу за метаданными идёт количество файлов внутри данного pak в формате Big Endian variable int
val indexNodeCount = reader.readVarLong()
private val indexNodes = HashMap<String, StarboundPakFile>()
private val _indexNodes = HashMap<String, StarboundPakFile>()
val indexNodes: Map<String, StarboundPakFile> = Collections.unmodifiableMap(_indexNodes)
private val _indexNodesLowercase = HashMap<String, StarboundPakFile>()
val indexNodesLowercase: Map<String, StarboundPakFile> = Collections.unmodifiableMap(_indexNodesLowercase)
val root = StarboundPakDirectory("/")
init {
@ -187,7 +190,10 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
throw IndexOutOfBoundsException("Garbage length at index $i: ${read.length}")
}
indexNodes[read.name] = read
_indexNodes[read.name] = read
// Starbound игнорирует регистр букв когда ищет пути, даже внутри pak архивов
_indexNodesLowercase[read.name.lowercase()] = read
root.resolve(read.directoryHiearchy).writeFile(read)
val last = read.name.substringAfterLast('/').substringAfterLast('.', "")
@ -213,11 +219,11 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
}
override fun pathExists(path: String): Boolean {
return indexNodes.containsKey(path)
return _indexNodesLowercase.containsKey(path)
}
override fun readOrNull(path: String): ByteBuffer? {
val node = indexNodes[path] ?: return null
val node = _indexNodesLowercase[path] ?: return null
return node.read()
}