Multithreaded game loading
This commit is contained in:
parent
5901d756ee
commit
97d441deba
71
src/main/kotlin/ru/dbotthepony/kstarbound/LoadingLog.kt
Normal file
71
src/main/kotlin/ru/dbotthepony/kstarbound/LoadingLog.kt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package ru.dbotthepony.kstarbound
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntIterators
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
|
|
||||||
|
interface ILoadingLog : Iterable<String> {
|
||||||
|
interface ILine {
|
||||||
|
var text: String
|
||||||
|
}
|
||||||
|
|
||||||
|
fun line(text: String): ILine
|
||||||
|
|
||||||
|
companion object : ILoadingLog, ILine {
|
||||||
|
override var text: String
|
||||||
|
get() = ""
|
||||||
|
set(value) {}
|
||||||
|
|
||||||
|
override fun line(text: String): ILine {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<String> {
|
||||||
|
return ObjectIterators.emptyIterator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadingLog : ILoadingLog {
|
||||||
|
private val lines = arrayOfNulls<Line>(128)
|
||||||
|
private var index = 0
|
||||||
|
private val lock = Any()
|
||||||
|
private var size = 0
|
||||||
|
var lastActivity: Long = System.nanoTime()
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun line(text: String): ILoadingLog.ILine {
|
||||||
|
return Line(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Line(text: String) : ILoadingLog.ILine {
|
||||||
|
override var text: String = text
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
lastActivity = System.nanoTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
lastActivity = System.nanoTime()
|
||||||
|
|
||||||
|
synchronized(lock) {
|
||||||
|
lines[index++ and 127] = this
|
||||||
|
size = (size + 1).coerceAtMost(127)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<String> {
|
||||||
|
return object : Iterator<String> {
|
||||||
|
private val index = (this@LoadingLog.index - 1) and 127
|
||||||
|
private val parent = IntIterators.fromTo(0, size)
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parent.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): String {
|
||||||
|
return lines[(index - parent.nextInt()) and 127]!!.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,9 +52,7 @@ fun main() {
|
|||||||
|
|
||||||
//Starbound.addPakPath(File("packed.pak"))
|
//Starbound.addPakPath(File("packed.pak"))
|
||||||
|
|
||||||
Starbound.initializeGame { finished, replaceStatus, status ->
|
Starbound.initializeGame(client.loadingLog)
|
||||||
client.putDebugLog(status, replaceStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.onTermination {
|
client.onTermination {
|
||||||
Starbound.terminateLoading = true
|
Starbound.terminateLoading = true
|
||||||
@ -159,7 +157,8 @@ fun main() {
|
|||||||
client.font.render("${ent.position}", y = 100f, scale = 0.25f)
|
client.font.render("${ent.position}", y = 100f, scale = 0.25f)
|
||||||
client.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
|
client.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
|
||||||
client.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
|
client.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
|
||||||
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos)}", y = 160f, scale = 0.25f)
|
client.font.render("Cursor: ${client.mouseCoordinates} -> ${client.screenToWorld(client.mouseCoordinates)}", y = 160f, scale = 0.25f)
|
||||||
|
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos)}", y = 180f, scale = 0.25f)
|
||||||
}
|
}
|
||||||
|
|
||||||
client.onPreDrawWorld {
|
client.onPreDrawWorld {
|
||||||
|
@ -7,14 +7,15 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|||||||
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
||||||
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
|
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
class RecipeRegistry {
|
class RecipeRegistry {
|
||||||
private val recipesInternal = ArrayList<RegistryObject<RecipeDefinition>>()
|
private val recipesInternal = ArrayList<RegistryObject<RecipeDefinition>>()
|
||||||
private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RegistryObject<RecipeDefinition>>>()
|
private val group2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
|
||||||
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
||||||
private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RegistryObject<RecipeDefinition>>>()
|
private val output2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
|
||||||
private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
||||||
private val input2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RegistryObject<RecipeDefinition>>>()
|
private val input2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
|
||||||
private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
|
||||||
|
|
||||||
val recipes: List<RegistryObject<RecipeDefinition>> = Collections.unmodifiableList(recipesInternal)
|
val recipes: List<RegistryObject<RecipeDefinition>> = Collections.unmodifiableList(recipesInternal)
|
||||||
@ -28,21 +29,21 @@ class RecipeRegistry {
|
|||||||
|
|
||||||
for (group in value.groups) {
|
for (group in value.groups) {
|
||||||
group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p ->
|
group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p ->
|
||||||
ArrayList<RegistryObject<RecipeDefinition>>().also {
|
LinkedList<RegistryObject<RecipeDefinition>>().also {
|
||||||
group2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
group2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
||||||
}
|
}
|
||||||
}).add(recipe)
|
}).add(recipe)
|
||||||
}
|
}
|
||||||
|
|
||||||
output2recipesInternal.computeIfAbsent(value.output.item.name, Object2ObjectFunction { p ->
|
output2recipesInternal.computeIfAbsent(value.output.item.name, Object2ObjectFunction { p ->
|
||||||
ArrayList<RegistryObject<RecipeDefinition>>().also {
|
LinkedList<RegistryObject<RecipeDefinition>>().also {
|
||||||
output2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
output2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
||||||
}
|
}
|
||||||
}).add(recipe)
|
}).add(recipe)
|
||||||
|
|
||||||
for (input in value.input) {
|
for (input in value.input) {
|
||||||
input2recipesInternal.computeIfAbsent(input.item.name, Object2ObjectFunction { p ->
|
input2recipesInternal.computeIfAbsent(input.item.name, Object2ObjectFunction { p ->
|
||||||
ArrayList<RegistryObject<RecipeDefinition>>().also {
|
LinkedList<RegistryObject<RecipeDefinition>>().also {
|
||||||
input2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
input2recipesBacking[p as String] = Collections.unmodifiableList(it)
|
||||||
}
|
}
|
||||||
}).add(recipe)
|
}).add(recipe)
|
||||||
|
@ -85,6 +85,8 @@ import ru.dbotthepony.kstarbound.util.set
|
|||||||
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
import ru.dbotthepony.kstarbound.util.traverseJsonPath
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
import java.util.concurrent.ForkJoinPool
|
||||||
|
import java.util.concurrent.ForkJoinTask
|
||||||
import java.util.function.BiConsumer
|
import java.util.function.BiConsumer
|
||||||
import java.util.function.BinaryOperator
|
import java.util.function.BinaryOperator
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
@ -901,7 +903,7 @@ object Starbound : ISBFileLocator {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
private fun loadStage(
|
private fun loadStage(
|
||||||
callback: (Boolean, Boolean, String) -> Unit,
|
log: ILoadingLog,
|
||||||
loader: ((String) -> Unit) -> Unit,
|
loader: ((String) -> Unit) -> Unit,
|
||||||
name: String,
|
name: String,
|
||||||
) {
|
) {
|
||||||
@ -909,27 +911,25 @@ object Starbound : ISBFileLocator {
|
|||||||
return
|
return
|
||||||
|
|
||||||
val time = System.currentTimeMillis()
|
val time = System.currentTimeMillis()
|
||||||
callback(false, false, "Loading $name...")
|
val line = log.line("Loading $name...".also(logger::info))
|
||||||
logger.info("Loading $name...")
|
|
||||||
|
|
||||||
loader {
|
loader {
|
||||||
if (terminateLoading) {
|
if (terminateLoading) {
|
||||||
throw InterruptedException("Game is terminating")
|
throw InterruptedException("Game is terminating")
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(false, true, it)
|
line.text = it
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(false, true, "Loaded $name in ${System.currentTimeMillis() - time}ms")
|
line.text = ("Loaded $name in ${System.currentTimeMillis() - time}ms".also(logger::info))
|
||||||
logger.info("Loaded $name in ${System.currentTimeMillis() - time}ms")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : Any> loadStage(
|
private fun <T : Any> loadStage(
|
||||||
callback: (Boolean, Boolean, String) -> Unit,
|
log: ILoadingLog,
|
||||||
registry: ObjectRegistry<T>,
|
registry: ObjectRegistry<T>,
|
||||||
files: List<IStarboundFile>,
|
files: List<IStarboundFile>,
|
||||||
) {
|
) {
|
||||||
loadStage(callback, loader = {
|
loadStage(log, loader = {
|
||||||
for (listedFile in files) {
|
for (listedFile in files) {
|
||||||
try {
|
try {
|
||||||
it("Loading $listedFile")
|
it("Loading $listedFile")
|
||||||
@ -945,24 +945,24 @@ object Starbound : ISBFileLocator {
|
|||||||
}, registry.name)
|
}, registry.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doInitialize(callback: (finished: Boolean, replaceStatus: Boolean, status: String) -> Unit) {
|
private fun doInitialize(log: ILoadingLog) {
|
||||||
var time = System.currentTimeMillis()
|
var time = System.currentTimeMillis()
|
||||||
|
|
||||||
if (archivePaths.isNotEmpty()) {
|
if (archivePaths.isNotEmpty()) {
|
||||||
callback(false, false, "Searching for pak archives...".also(logger::info))
|
log.line("Searching for pak archives...".also(logger::info))
|
||||||
|
|
||||||
for (path in archivePaths) {
|
for (path in archivePaths) {
|
||||||
callback(false, false, "Reading index of ${path}...".also(logger::info))
|
val line = log.line("Reading index of ${path}...".also(logger::info))
|
||||||
|
|
||||||
addPak(StarboundPak(path) { _, status ->
|
addPak(StarboundPak(path) { _, status ->
|
||||||
callback(false, true, "${path.parent}/${path.name}: $status")
|
line.text = ("${path.parent}/${path.name}: $status")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(false, false, "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()
|
time = System.currentTimeMillis()
|
||||||
callback(false, false, "Building file index...".also(logger::info))
|
log.line("Building file index...".also(logger::info))
|
||||||
|
|
||||||
val ext2files = fileSystems.parallelStream()
|
val ext2files = fileSystems.parallelStream()
|
||||||
.flatMap { it.explore() }
|
.flatMap { it.explore() }
|
||||||
@ -998,39 +998,45 @@ object Starbound : ISBFileLocator {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
callback(false, false, "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))
|
||||||
|
|
||||||
loadStage(callback, { loadItemDefinitions(it, ext2files) }, "item definitions")
|
val pool = ForkJoinPool.commonPool()
|
||||||
loadStage(callback, { loadJsonFunctions(it, ext2files["functions"] ?: listOf()) }, "json functions")
|
val tasks = ArrayList<ForkJoinTask<*>>()
|
||||||
loadStage(callback, { loadJson2Functions(it, ext2files["2functions"] ?: listOf()) }, "json 2functions")
|
|
||||||
loadStage(callback, { loadRecipes(it, ext2files["recipe"] ?: listOf()) }, "recipes")
|
|
||||||
loadStage(callback, { loadTreasurePools(it, ext2files["treasurepools"] ?: listOf()) }, "treasure pools")
|
|
||||||
|
|
||||||
loadStage(callback, _tiles, ext2files["material"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, { loadItemDefinitions(it, ext2files) }, "item definitions") })
|
||||||
loadStage(callback, _tileModifiers, ext2files["matmod"] ?: listOf())
|
|
||||||
loadStage(callback, _liquid, ext2files["liquid"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, { loadJsonFunctions(it, ext2files["functions"] ?: listOf()) }, "json functions") })
|
||||||
loadStage(callback, _worldObjects, ext2files["object"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, { loadJson2Functions(it, ext2files["2functions"] ?: listOf()) }, "json 2functions") })
|
||||||
loadStage(callback, _statusEffects, ext2files["statuseffect"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, { loadRecipes(it, ext2files["recipe"] ?: listOf()) }, "recipes") })
|
||||||
loadStage(callback, _species, ext2files["species"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, { loadTreasurePools(it, ext2files["treasurepools"] ?: listOf()) }, "treasure pools") })
|
||||||
loadStage(callback, _particles, ext2files["particle"] ?: listOf())
|
|
||||||
loadStage(callback, _questTemplates, ext2files["questtemplate"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _tiles, ext2files["material"] ?: listOf()) })
|
||||||
loadStage(callback, _techs, ext2files["tech"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _tileModifiers, ext2files["matmod"] ?: listOf()) })
|
||||||
loadStage(callback, _npcTypes, ext2files["npctype"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _liquid, ext2files["liquid"] ?: listOf()) })
|
||||||
//loadStage(callback, _projectiles, ext2files["projectile"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _worldObjects, ext2files["object"] ?: listOf()) })
|
||||||
//loadStage(callback, _tenants, ext2files["tenant"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _statusEffects, ext2files["statuseffect"] ?: listOf()) })
|
||||||
loadStage(callback, _monsterSkills, ext2files["monsterskill"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _species, ext2files["species"] ?: listOf()) })
|
||||||
//loadStage(callback, _monsterTypes, ext2files["monstertype"] ?: listOf())
|
tasks.add(pool.submit { loadStage(log, _particles, ext2files["particle"] ?: listOf()) })
|
||||||
|
tasks.add(pool.submit { loadStage(log, _questTemplates, ext2files["questtemplate"] ?: listOf()) })
|
||||||
|
tasks.add(pool.submit { loadStage(log, _techs, ext2files["tech"] ?: listOf()) })
|
||||||
|
tasks.add(pool.submit { loadStage(log, _npcTypes, ext2files["npctype"] ?: listOf()) })
|
||||||
|
// tasks.add(pool.submit { loadStage(log, _projectiles, ext2files["projectile"] ?: listOf()) })
|
||||||
|
// tasks.add(pool.submit { loadStage(log, _tenants, ext2files["tenant"] ?: listOf()) })
|
||||||
|
tasks.add(pool.submit { loadStage(log, _monsterSkills, ext2files["monsterskill"] ?: listOf()) })
|
||||||
|
// tasks.add(pool.submit { loadStage(log, _monsterTypes, ext2files["monstertype"] ?: listOf()) })
|
||||||
|
|
||||||
AssetPathStack.block("/") {
|
AssetPathStack.block("/") {
|
||||||
//playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
//playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.forEach { it.join() }
|
||||||
|
|
||||||
initializing = false
|
initializing = false
|
||||||
initialized = true
|
initialized = true
|
||||||
callback(true, false, "Finished loading in ${System.currentTimeMillis() - time}ms")
|
log.line("Finished loading in ${System.currentTimeMillis() - time}ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initializeGame(callback: (finished: Boolean, replaceStatus: Boolean, status: String) -> Unit) {
|
fun initializeGame(log: ILoadingLog) {
|
||||||
if (initializing) {
|
if (initializing) {
|
||||||
throw IllegalStateException("Already initializing!")
|
throw IllegalStateException("Already initializing!")
|
||||||
}
|
}
|
||||||
@ -1040,7 +1046,7 @@ object Starbound : ISBFileLocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initializing = true
|
initializing = true
|
||||||
Thread({ doInitialize(callback) }, "Asset Loader").also {
|
Thread({ doInitialize(log) }, "Asset Loader").also {
|
||||||
it.isDaemon = true
|
it.isDaemon = true
|
||||||
it.start()
|
it.start()
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import org.lwjgl.opengl.GL45.*
|
|||||||
import org.lwjgl.opengl.GLCapabilities
|
import org.lwjgl.opengl.GLCapabilities
|
||||||
import org.lwjgl.system.MemoryStack
|
import org.lwjgl.system.MemoryStack
|
||||||
import org.lwjgl.system.MemoryUtil
|
import org.lwjgl.system.MemoryUtil
|
||||||
|
import ru.dbotthepony.kstarbound.LoadingLog
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
@ -109,7 +110,7 @@ class StarboundClient : Closeable {
|
|||||||
var viewportTopRight = Vector2d()
|
var viewportTopRight = Vector2d()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var fullbright = true
|
var fullbright = false
|
||||||
|
|
||||||
var clientTerminated = false
|
var clientTerminated = false
|
||||||
private set
|
private set
|
||||||
@ -133,8 +134,7 @@ class StarboundClient : Closeable {
|
|||||||
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
||||||
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
||||||
private val terminateCallbacks = ArrayList<() -> Unit>()
|
private val terminateCallbacks = ArrayList<() -> Unit>()
|
||||||
private val startupTextList = ArrayList<String>()
|
val loadingLog = LoadingLog()
|
||||||
private var finishStartupRendering = System.currentTimeMillis() + 4000L
|
|
||||||
|
|
||||||
private val cleaner = Cleaner.create { r ->
|
private val cleaner = Cleaner.create { r ->
|
||||||
val thread = Thread(r, "OpenGL Cleaner for '${thread.name}'")
|
val thread = Thread(r, "OpenGL Cleaner for '${thread.name}'")
|
||||||
@ -180,7 +180,7 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
|
window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
|
||||||
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
|
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
|
||||||
startupTextList.add("Created GLFW window")
|
loadingLog.line("Created GLFW window")
|
||||||
|
|
||||||
input.installCallback(window)
|
input.installCallback(window)
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ class StarboundClient : Closeable {
|
|||||||
GLFW.glfwSwapInterval(0)
|
GLFW.glfwSwapInterval(0)
|
||||||
|
|
||||||
GLFW.glfwShowWindow(window)
|
GLFW.glfwShowWindow(window)
|
||||||
putDebugLog("Initialized GLFW window")
|
loadingLog.line("Initialized GLFW window")
|
||||||
}
|
}
|
||||||
|
|
||||||
val maxTextureBlocks = glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS)
|
val maxTextureBlocks = glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS)
|
||||||
@ -521,6 +521,20 @@ class StarboundClient : Closeable {
|
|||||||
builder.draw(GL_LINES)
|
builder.draw(GL_LINES)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun lines(color: RGBAColor = RGBAColor.WHITE, lambda: (VertexBuilder) -> Unit) {
|
||||||
|
val builder = programs.position.builder
|
||||||
|
|
||||||
|
builder.builder.begin(GeometryType.LINES)
|
||||||
|
lambda.invoke(builder.builder)
|
||||||
|
builder.upload()
|
||||||
|
|
||||||
|
programs.position.use()
|
||||||
|
programs.position.colorMultiplier = color
|
||||||
|
programs.position.modelMatrix = stack.last()
|
||||||
|
|
||||||
|
builder.draw(GL_LINES)
|
||||||
|
}
|
||||||
|
|
||||||
fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER)
|
fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER)
|
||||||
fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER)
|
fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
@ -531,20 +545,6 @@ class StarboundClient : Closeable {
|
|||||||
fun internalFragment(file: String) = GLShader(readInternal(file), GL_FRAGMENT_SHADER)
|
fun internalFragment(file: String) = GLShader(readInternal(file), GL_FRAGMENT_SHADER)
|
||||||
fun internalGeometry(file: String) = GLShader(readInternal(file), GL_GEOMETRY_SHADER)
|
fun internalGeometry(file: String) = GLShader(readInternal(file), GL_GEOMETRY_SHADER)
|
||||||
|
|
||||||
fun putDebugLog(text: String, replace: Boolean = false) {
|
|
||||||
if (replace) {
|
|
||||||
if (startupTextList.isEmpty()) {
|
|
||||||
startupTextList.add(text)
|
|
||||||
} else {
|
|
||||||
startupTextList[startupTextList.size - 1] = text
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
startupTextList.add(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
finishStartupRendering = System.currentTimeMillis() + 4000L
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isMe(state: StarboundClient?) {
|
private fun isMe(state: StarboundClient?) {
|
||||||
if (state != null && state != this) {
|
if (state != null && state != this) {
|
||||||
throw InvalidArgumentException("Provided object does not belong to $this state tracker (belongs to $state)")
|
throw InvalidArgumentException("Provided object does not belong to $this state tracker (belongs to $state)")
|
||||||
@ -569,7 +569,7 @@ class StarboundClient : Closeable {
|
|||||||
xMousePos.position(0)
|
xMousePos.position(0)
|
||||||
yMousePos.position(0)
|
yMousePos.position(0)
|
||||||
|
|
||||||
return Vector2d(xMousePos.get(), yMousePos.get())
|
return Vector2d(xMousePos.get(), viewportHeight - yMousePos.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
val mouseCoordinatesF: Vector2f get() {
|
val mouseCoordinatesF: Vector2f get() {
|
||||||
@ -579,7 +579,7 @@ class StarboundClient : Closeable {
|
|||||||
xMousePos.position(0)
|
xMousePos.position(0)
|
||||||
yMousePos.position(0)
|
yMousePos.position(0)
|
||||||
|
|
||||||
return Vector2f(xMousePos.get().toFloat(), yMousePos.get().toFloat())
|
return Vector2f(xMousePos.get().toFloat(), viewportHeight - yMousePos.get().toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun screenToWorld(x: Double, y: Double): Vector2d {
|
fun screenToWorld(x: Double, y: Double): Vector2d {
|
||||||
@ -600,7 +600,6 @@ class StarboundClient : Closeable {
|
|||||||
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
putDebugLog("Initialized OpenGL context")
|
|
||||||
clearColor = RGBAColor.SLATE_GRAY
|
clearColor = RGBAColor.SLATE_GRAY
|
||||||
|
|
||||||
blend = true
|
blend = true
|
||||||
@ -798,6 +797,10 @@ class StarboundClient : Closeable {
|
|||||||
size = viewportRectangle)
|
size = viewportRectangle)
|
||||||
|
|
||||||
if (viewportLightingMem != null && !fullbright) {
|
if (viewportLightingMem != null && !fullbright) {
|
||||||
|
val spos = screenToWorld(mouseCoordinates)
|
||||||
|
|
||||||
|
viewportLighting.addPointLight(roundTowardsPositiveInfinity(spos.x - viewportCellX), roundTowardsPositiveInfinity(spos.y - viewportCellY), 1f, 1f, 1f)
|
||||||
|
|
||||||
viewportLightingMem.position(0)
|
viewportLightingMem.position(0)
|
||||||
BufferUtils.zeroBuffer(viewportLightingMem)
|
BufferUtils.zeroBuffer(viewportLightingMem)
|
||||||
viewportLightingMem.position(0)
|
viewportLightingMem.position(0)
|
||||||
@ -844,21 +847,19 @@ class StarboundClient : Closeable {
|
|||||||
uberShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
|
uberShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
|
||||||
fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
|
fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
|
||||||
|
|
||||||
val thisTime = System.currentTimeMillis()
|
if (System.nanoTime() - loadingLog.lastActivity <= 4_000_000_000L) {
|
||||||
|
|
||||||
if (startupTextList.isNotEmpty() && thisTime <= finishStartupRendering) {
|
|
||||||
var alpha = 1f
|
var alpha = 1f
|
||||||
|
|
||||||
if (finishStartupRendering - thisTime < 1000L) {
|
if (System.nanoTime() - loadingLog.lastActivity >= 3_000_000_000L) {
|
||||||
alpha = (finishStartupRendering - thisTime) / 1000f
|
alpha = 1f - (System.nanoTime() - loadingLog.lastActivity - 3_000_000_000L) / 1_000_000_000f
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.push()
|
stack.push()
|
||||||
stack.last().translate(y = viewportHeight.toFloat())
|
stack.last().translate(y = viewportHeight.toFloat())
|
||||||
var shade = 255
|
var shade = 255
|
||||||
|
|
||||||
for (i in startupTextList.size - 1 downTo 0) {
|
for (line in loadingLog) {
|
||||||
val size = font.render(startupTextList[i], alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
val size = font.render(line, alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
||||||
stack.last().translate(y = -size.height * 1.2f)
|
stack.last().translate(y = -size.height * 1.2f)
|
||||||
|
|
||||||
if (shade > 120) {
|
if (shade > 120) {
|
||||||
|
@ -2,6 +2,8 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||||
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
|
|
||||||
enum class RenderLayer {
|
enum class RenderLayer {
|
||||||
BackgroundOverlay,
|
BackgroundOverlay,
|
||||||
@ -29,33 +31,49 @@ enum class RenderLayer {
|
|||||||
FrontParticle,
|
FrontParticle,
|
||||||
Overlay;
|
Overlay;
|
||||||
|
|
||||||
val base = Point(this)
|
private val base = Point(this)
|
||||||
|
|
||||||
fun point(offset: Long = 0L): Point {
|
fun point(offset: Long = 0L, index: Long = 0L, hueShift: Float = 0f, colorVariant: TileColor = TileColor.DEFAULT): Point {
|
||||||
return if (offset == 0L)
|
return if (offset == 0L && index == 0L && hueShift == 0f && colorVariant === TileColor.DEFAULT)
|
||||||
base
|
base
|
||||||
else
|
else
|
||||||
Point(this, offset)
|
Point(this, offset, index, hueShift, colorVariant)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Point(val base: RenderLayer, val offset: Long = 0L) : Comparable<Point> {
|
fun point(): Point {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Point(val base: RenderLayer, val offset: Long = 0L, val index: Long = 0L, val hueShift: Float = 0f, val colorVariant: TileColor = TileColor.DEFAULT) : Comparable<Point> {
|
||||||
override fun compareTo(other: Point): Int {
|
override fun compareTo(other: Point): Int {
|
||||||
|
if (this === other) return 0
|
||||||
var cmp = base.compareTo(other.base)
|
var cmp = base.compareTo(other.base)
|
||||||
if (cmp == 0) cmp = offset.compareTo(other.offset)
|
if (cmp == 0) cmp = offset.compareTo(other.offset)
|
||||||
|
if (cmp == 0) cmp = index.compareTo(other.index)
|
||||||
|
if (cmp == 0) cmp = hueShift.compareTo(other.hueShift)
|
||||||
|
if (cmp == 0) cmp = colorVariant.compareTo(other.colorVariant)
|
||||||
return cmp
|
return cmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun tileLayer(isBackground: Boolean, isModifier: Boolean, offset: Long = 0L): Point {
|
fun tileLayer(isBackground: Boolean, isModifier: Boolean, offset: Long = 0L, index: Long = 0L, hueShift: Float = 0f, colorVariant: TileColor = TileColor.DEFAULT): Point {
|
||||||
if (isBackground && isModifier) {
|
if (isBackground && isModifier) {
|
||||||
return BackgroundTileMod.point(offset)
|
return BackgroundTileMod.point(offset, index, hueShift, colorVariant)
|
||||||
} else if (isBackground) {
|
} else if (isBackground) {
|
||||||
return BackgroundTile.point(offset)
|
return BackgroundTile.point(offset, index, hueShift, colorVariant)
|
||||||
} else if (isModifier) {
|
} else if (isModifier) {
|
||||||
return ForegroundTileMod.point(offset)
|
return ForegroundTileMod.point(offset, index, hueShift, colorVariant)
|
||||||
} else {
|
} else {
|
||||||
return ForegroundTile.point(offset)
|
return ForegroundTile.point(offset, index, hueShift, colorVariant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: ITileState): Point {
|
||||||
|
if (isModifier) {
|
||||||
|
return tileLayer(isBackground, true, tile.modifier?.renderParameters?.zLevel ?: 0L, tile.modifier?.modId?.toLong() ?: 0L, tile.modifierHueShift)
|
||||||
|
} else {
|
||||||
|
return tileLayer(isBackground, false, tile.material.renderParameters.zLevel, tile.material.materialId.toLong(), tile.hueShift)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
|
import com.github.benmanes.caffeine.cache.Scheduler
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.opengl.GL45.*
|
import org.lwjgl.opengl.GL45.*
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
@ -15,6 +18,7 @@ import ru.dbotthepony.kstarbound.world.api.TileColor
|
|||||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
|
import java.time.Duration
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,22 +27,37 @@ import kotlin.collections.HashMap
|
|||||||
* Создаётся единожды как потомок [Graphics]
|
* Создаётся единожды как потомок [Graphics]
|
||||||
*/
|
*/
|
||||||
class TileRenderers(val client: StarboundClient) {
|
class TileRenderers(val client: StarboundClient) {
|
||||||
private val foreground = HashMap<GLTexture2D, Config>()
|
private val foreground: Cache<GLTexture2D, Config> = Caffeine.newBuilder()
|
||||||
private val background = HashMap<GLTexture2D, Config>()
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
private val matCache = HashMap<String, TileRenderer>()
|
.scheduler(Scheduler.systemScheduler())
|
||||||
private val modCache = HashMap<String, TileRenderer>()
|
.build()
|
||||||
|
|
||||||
|
private val background: Cache<GLTexture2D, Config> = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val matCache: Cache<String, TileRenderer> = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val modCache: Cache<String, TileRenderer> = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
|
.build()
|
||||||
|
|
||||||
fun getMaterialRenderer(defName: String): TileRenderer {
|
fun getMaterialRenderer(defName: String): TileRenderer {
|
||||||
return matCache.computeIfAbsent(defName) {
|
return matCache.get(defName) {
|
||||||
val def = Starbound.tiles[defName] // TODO: Пустой рендерер
|
val def = Starbound.tiles[defName] // TODO: Пустой рендерер
|
||||||
return@computeIfAbsent TileRenderer(this, def!!.value)
|
TileRenderer(this, def!!.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModifierRenderer(defName: String): TileRenderer {
|
fun getModifierRenderer(defName: String): TileRenderer {
|
||||||
return modCache.computeIfAbsent(defName) {
|
return modCache.get(defName) {
|
||||||
val def = Starbound.tileModifiers[defName] // TODO: Пустой рендерер
|
val def = Starbound.tileModifiers[defName] // TODO: Пустой рендерер
|
||||||
return@computeIfAbsent TileRenderer(this, def!!.value)
|
TileRenderer(this, def!!.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +81,11 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun foreground(texture: GLTexture2D): RenderConfig<UberShader> {
|
fun foreground(texture: GLTexture2D): RenderConfig<UberShader> {
|
||||||
return foreground.computeIfAbsent(texture) { Config(it, FOREGROUND_COLOR) }
|
return foreground.get(texture) { Config(it, FOREGROUND_COLOR) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun background(texture: GLTexture2D): RenderConfig<UberShader> {
|
fun background(texture: GLTexture2D): RenderConfig<UberShader> {
|
||||||
return background.computeIfAbsent(texture) { Config(it, BACKGROUND_COLOR) }
|
return background.get(texture) { Config(it, BACKGROUND_COLOR) }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -175,7 +194,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
|
|
||||||
tesselateAt(
|
tesselateAt(
|
||||||
self, renderPiece.piece, getter,
|
self, renderPiece.piece, getter,
|
||||||
meshBuilder.getBuilder(RenderLayer.tileLayer(isBackground, isModifier, def.renderParameters.zLevel), program).mode(GeometryType.QUADS),
|
meshBuilder.getBuilder(RenderLayer.tileLayer(isBackground, isModifier, self), program).mode(GeometryType.QUADS),
|
||||||
pos, renderPiece.offset, isModifier)
|
pos, renderPiece.offset, isModifier)
|
||||||
} else {
|
} else {
|
||||||
tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
|
tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
|
||||||
@ -217,7 +236,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
val template = def.renderTemplate.value ?: return
|
val template = def.renderTemplate.value ?: return
|
||||||
|
|
||||||
val vertexBuilder = meshBuilder
|
val vertexBuilder = meshBuilder
|
||||||
.getBuilder(RenderLayer.tileLayer(isBackground, isModifier, def.renderParameters.zLevel), if (isBackground) bakedBackgroundProgramState!! else bakedProgramState!!)
|
.getBuilder(RenderLayer.tileLayer(isBackground, isModifier, self), if (isBackground) bakedBackgroundProgramState!! else bakedProgramState!!)
|
||||||
.mode(GeometryType.QUADS)
|
.mode(GeometryType.QUADS)
|
||||||
|
|
||||||
for ((_, matcher) in template.matches) {
|
for ((_, matcher) in template.matches) {
|
||||||
|
@ -16,6 +16,8 @@ import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
|||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE
|
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
|
import ru.dbotthepony.kstarbound.world.NonSolidRayFilter
|
||||||
|
import ru.dbotthepony.kstarbound.world.SolidRayFilter
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
||||||
@ -24,8 +26,12 @@ import ru.dbotthepony.kstarbound.world.positiveModulo
|
|||||||
import ru.dbotthepony.kvector.api.IStruct2i
|
import ru.dbotthepony.kvector.api.IStruct2i
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
import ru.dbotthepony.kvector.vector.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
class ClientWorld(
|
class ClientWorld(
|
||||||
val client: StarboundClient,
|
val client: StarboundClient,
|
||||||
@ -282,26 +288,42 @@ class ClientWorld(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*layers.add(-999999) {
|
/*layers.add(RenderLayer.Overlay.base) {
|
||||||
val rayFan = ArrayList<Vector2d>()
|
val rayFan = ArrayList<Vector2d>()
|
||||||
|
val pos = client.screenToWorld(client.mouseCoordinates)
|
||||||
|
|
||||||
for (i in 0 .. 359) {
|
//for (i in 0 .. 359) {
|
||||||
rayFan.add(Vector2d(cos(i / 180.0 * PI), sin(i / 180.0 * PI)))
|
// rayFan.add(Vector2d(cos(i / 180.0 * PI), sin(i / 180.0 * PI)))
|
||||||
|
//}
|
||||||
|
|
||||||
|
rayFan.add(Vector2d(0.5, 0.7).unitVector)
|
||||||
|
|
||||||
|
client.quadWireframe(RGBAColor(1f, 1f, 1f, 0.4f)) {
|
||||||
|
for (x in -20 .. 20) {
|
||||||
|
for (y in -20 .. 20) {
|
||||||
|
it.vertex(pos.x.toInt().toFloat() + x, pos.y.toInt().toFloat() + y)
|
||||||
|
it.vertex(pos.x.toInt().toFloat() + x + 1f, pos.y.toInt().toFloat() + y)
|
||||||
|
it.vertex(pos.x.toInt().toFloat() + x + 1f, pos.y.toInt().toFloat() + 1f + y)
|
||||||
|
it.vertex(pos.x.toInt().toFloat() + x, pos.y.toInt().toFloat() + 1f + y)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ray in rayFan) {
|
client.lines {
|
||||||
val trace = castRayNaive(pos, ray, 16.0)
|
for (ray in rayFan) {
|
||||||
|
val trace = castRayExact(pos, ray, 16.0, NonSolidRayFilter)
|
||||||
|
|
||||||
client.gl.quadWireframe {
|
it.vertex(pos.x.toFloat(), pos.y.toFloat())
|
||||||
for ((tpos, tile) in trace.traversedTiles) {
|
it.vertex(pos.x.toFloat() + ray.x.toFloat() * trace.fraction.toFloat() * 16f, pos.y.toFloat() + ray.y.toFloat() * trace.fraction.toFloat() * 16f)
|
||||||
if (tile.foreground.material != null)
|
|
||||||
it.quad(
|
/*for ((tpos, tile) in trace.traversedTiles) {
|
||||||
tpos.x.toFloat(),
|
if (!tile.foreground.material.renderParameters.lightTransparent) {
|
||||||
tpos.y.toFloat(),
|
it.vertex(tpos.x.toFloat(), tpos.y.toFloat())
|
||||||
tpos.x + 1f,
|
it.vertex(tpos.x.toFloat() + 1f, tpos.y.toFloat())
|
||||||
tpos.y + 1f
|
it.vertex(tpos.x.toFloat() + 1f, tpos.y.toFloat() + 1f)
|
||||||
)
|
it.vertex(tpos.x.toFloat(), tpos.y.toFloat() + 1f)
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.defs.image
|
|||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache
|
import com.github.benmanes.caffeine.cache.Cache
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
|
import com.github.benmanes.caffeine.cache.Scheduler
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
@ -274,10 +275,10 @@ class Image private constructor(
|
|||||||
private val cleaner = Cleaner.create { Thread(it, "STB Image Cleaner") }
|
private val cleaner = Cleaner.create { Thread(it, "STB Image Cleaner") }
|
||||||
|
|
||||||
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
|
private val dataCache: Cache<String, ByteBuffer> = Caffeine.newBuilder()
|
||||||
.softValues()
|
.expireAfterAccess(Duration.ofMinutes(1))
|
||||||
.expireAfterAccess(Duration.ofMinutes(5))
|
|
||||||
.weigher<String, ByteBuffer> { key, value -> value.capacity() }
|
.weigher<String, ByteBuffer> { key, value -> value.capacity() }
|
||||||
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
|
.maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */)
|
||||||
|
.scheduler(Scheduler.systemScheduler())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -97,9 +97,11 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.seek(innerOffset + offset)
|
synchronized(lock) {
|
||||||
innerOffset++
|
reader.seek(innerOffset + offset)
|
||||||
return reader.read()
|
innerOffset++
|
||||||
|
return reader.read()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readNBytes(len: Int): ByteArray {
|
override fun readNBytes(len: Int): ByteArray {
|
||||||
@ -110,15 +112,13 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
|||||||
if (readMax == 0)
|
if (readMax == 0)
|
||||||
return ByteArray(0)
|
return ByteArray(0)
|
||||||
|
|
||||||
val b = ByteArray(readMax)
|
synchronized(lock) {
|
||||||
reader.seek(innerOffset + offset)
|
val b = ByteArray(readMax)
|
||||||
val readBytes = reader.read(b)
|
reader.seek(innerOffset + offset)
|
||||||
|
reader.readFully(b)
|
||||||
if (readBytes != readMax)
|
innerOffset += readMax
|
||||||
throw IOError(RuntimeException("Reading $readMax bytes returned only $readBytes bytes"))
|
return b
|
||||||
|
}
|
||||||
innerOffset += readBytes
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||||
@ -133,14 +133,16 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
|||||||
if (readMax <= 0)
|
if (readMax <= 0)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
reader.seek(innerOffset + offset)
|
synchronized(lock) {
|
||||||
val readBytes = reader.read(b, off, readMax)
|
reader.seek(innerOffset + offset)
|
||||||
|
val readBytes = reader.read(b, off, readMax)
|
||||||
|
|
||||||
if (readBytes == -1)
|
if (readBytes == -1)
|
||||||
throw RuntimeException("Unexpected EOF, want to read $readMax bytes from starting $offset in $path")
|
throw RuntimeException("Unexpected EOF, want to read $readMax bytes from starting $offset in $path")
|
||||||
|
|
||||||
innerOffset += readBytes
|
innerOffset += readBytes
|
||||||
return readBytes
|
return readBytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +152,8 @@ class StarboundPak(val path: File, callback: (finished: Boolean, status: String)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val reader = RandomAccessFile(path, "r")
|
private val reader = RandomAccessFile(path, "r")
|
||||||
|
private val lock = Any()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
readHeader(reader, 0x53) // S
|
readHeader(reader, 0x53) // S
|
||||||
|
Loading…
Reference in New Issue
Block a user