Load font from pak files, bootstrap window

This commit is contained in:
DBotThePony 2023-12-01 19:03:51 +07:00
parent 2793154bbb
commit b79161cca2
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 214 additions and 89 deletions

View File

@ -65,16 +65,12 @@ fun main() {
Starbound.initializeGame(client.loadingLog) Starbound.initializeGame(client.loadingLog)
client.onTermination {
Starbound.terminateLoading = true
}
var ply: PlayerEntity? = null var ply: PlayerEntity? = null
Starbound.onInitialize { Starbound.mailboxInitialized.submit {
ply = PlayerEntity(client.world!!) ply = PlayerEntity(client.world!!)
ply!!.position = Vector2d(225.0, 745.0) ply!!.position = Vector2d(225.0, 680.0)
ply!!.spawn() ply!!.spawn()
//for (chunkX in 17 .. 18) { //for (chunkX in 17 .. 18) {

View File

@ -62,6 +62,7 @@ import ru.dbotthepony.kstarbound.world.physics.Poly
import java.io.* import java.io.*
import java.lang.ref.Cleaner import java.lang.ref.Cleaner
import java.text.DateFormat import java.text.DateFormat
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinTask import java.util.concurrent.ForkJoinTask
import java.util.concurrent.locks.LockSupport import java.util.concurrent.locks.LockSupport
@ -80,6 +81,8 @@ object Starbound : ISBFileLocator {
val thread = Thread(::universeThread, "Starbound Universe") val thread = Thread(::universeThread, "Starbound Universe")
val mailbox = MailboxExecutorService(thread) val mailbox = MailboxExecutorService(thread)
val mailboxBootstrapped = MailboxExecutorService(thread)
val mailboxInitialized = MailboxExecutorService(thread)
init { init {
thread.isDaemon = true thread.isDaemon = true
@ -274,11 +277,14 @@ object Starbound : ISBFileLocator {
} }
} }
@Volatile
var initializing = false var initializing = false
private set private set
var initialized = false var initialized = false
private set private set
var bootstrapping = false
private set
var bootstrapped = false
private set
@Volatile @Volatile
var terminateLoading = false var terminateLoading = false
@ -370,29 +376,33 @@ object Starbound : ISBFileLocator {
archivePaths.add(pak) archivePaths.add(pak)
} }
private val initCallbacks = ArrayList<() -> Unit>() private fun doBootstrap() {
if (!bootstrapped && !bootstrapping) {
var playerDefinition: PlayerDefinition by WriteOnce() bootstrapping = true
private set } else {
return
private fun doInitialize(log: ILoadingLog, parallel: Boolean) {
var time = System.currentTimeMillis()
if (archivePaths.isNotEmpty()) {
log.line("Searching for pak archives...".also(LOGGER::info))
for (path in archivePaths) {
val line = log.line("Reading index of ${path}...".also(LOGGER::info))
addPak(StarboundPak(path) { _, status ->
line.text = ("${path.parent}/${path.name}: $status")
})
}
} }
log.line("Finished reading pak archives in ${System.currentTimeMillis() - time}ms".also(LOGGER::info)) for (path in archivePaths) {
time = System.currentTimeMillis() addPak(StarboundPak(path))
log.line("Building file index...".also(LOGGER::info)) }
bootstrapped = true
bootstrapping = false
checkMailbox()
}
private fun doInitialize(log: ILoadingLog, parallel: Boolean) {
if (!initializing && !initialized) {
initializing = true
} else {
return
}
doBootstrap()
val time = System.currentTimeMillis()
val ext2files = fileSystems.parallelStream() val ext2files = fileSystems.parallelStream()
.flatMap { it.explore() } .flatMap { it.explore() }
@ -428,7 +438,7 @@ object Starbound : ISBFileLocator {
} }
}) })
log.line("Finished building file index in ${System.currentTimeMillis() - time}ms".also(LOGGER::info)) checkMailbox()
val tasks = ArrayList<ForkJoinTask<*>>() val tasks = ArrayList<ForkJoinTask<*>>()
val pool = if (parallel) ForkJoinPool.commonPool() else ForkJoinPool(1) val pool = if (parallel) ForkJoinPool.commonPool() else ForkJoinPool(1)
@ -437,10 +447,6 @@ object Starbound : ISBFileLocator {
tasks.addAll(RecipeRegistry.load(log, ext2files, pool)) tasks.addAll(RecipeRegistry.load(log, ext2files, pool))
tasks.addAll(GlobalDefaults.load(log, pool)) tasks.addAll(GlobalDefaults.load(log, pool))
AssetPathStack.block("/") {
//playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
}
tasks.forEach { it.join() } tasks.forEach { it.join() }
if (!parallel) if (!parallel)
@ -450,45 +456,64 @@ object Starbound : ISBFileLocator {
initializing = false initializing = false
initialized = true initialized = true
checkMailbox()
log.line("Finished loading in ${System.currentTimeMillis() - time}ms") log.line("Finished loading in ${System.currentTimeMillis() - time}ms")
} }
fun initializeGame(log: ILoadingLog, parallel: Boolean = true) { fun initializeGame(log: ILoadingLog, parallel: Boolean = true) {
if (initializing) {
throw IllegalStateException("Already initializing!")
}
if (initialized) {
throw IllegalStateException("Already initialized!")
}
initializing = true
mailbox.submit { mailbox.submit {
doInitialize(log, parallel) doInitialize(log, parallel)
} }
} }
fun onInitialize(callback: () -> Unit) { fun bootstrapGame() {
if (initialized) { mailbox.submit {
callback() doBootstrap()
} else {
initCallbacks.add(callback)
} }
} }
private fun checkMailbox() {
mailbox.executeQueuedTasks()
if (bootstrapped)
mailboxBootstrapped.executeQueuedTasks()
if (initialized)
mailboxInitialized.executeQueuedTasks()
}
private var fontPath: File? = null
fun loadFont(): CompletableFuture<File> {
val fontPath = fontPath
if (fontPath != null)
return CompletableFuture.completedFuture(fontPath)
return CompletableFuture.supplyAsync(Supplier {
val fontPath = Starbound.fontPath
if (fontPath != null)
return@Supplier fontPath
val file = locate("/hobo.ttf")
if (!file.exists)
throw FileNotFoundException("Unable to locate font file /hobo.ttf")
else if (!file.isFile)
throw FileNotFoundException("/hobo.ttf is not a file!")
else {
val tempPath = File(System.getProperty("java.io.tmpdir"), "sb-hobo.ttf")
tempPath.writeBytes(file.read().array())
Starbound.fontPath = tempPath
return@Supplier tempPath
}
}, mailboxBootstrapped)
}
private fun universeThread() { private fun universeThread() {
while (true) { while (true) {
mailbox.executeQueuedTasks() checkMailbox()
if (initialized && initCallbacks.isNotEmpty()) {
for (callback in initCallbacks) {
callback()
}
initCallbacks.clear()
}
LockSupport.park() LockSupport.park()
} }
} }

View File

@ -78,11 +78,13 @@ import java.util.concurrent.ForkJoinWorkerThread
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.LockSupport import java.util.concurrent.locks.LockSupport
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import java.util.function.Consumer
import java.util.function.IntConsumer import java.util.function.IntConsumer
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.properties.Delegates
class StarboundClient : Closeable { class StarboundClient : Closeable {
val window: Long val window: Long
@ -383,7 +385,23 @@ class StarboundClient : Closeable {
} }
val freeType = FreeType() val freeType = FreeType()
val font = Font()
var font: Font by Delegates.notNull()
private set
private var fontInitialized = false
init {
Starbound.loadFont().thenAcceptAsync(Consumer {
try {
font = Font(it.canonicalPath)
fontInitialized = true
} catch (err: Throwable) {
LOGGER.fatal("Unable to load font", err)
}
}, mailbox)
}
val programs = GLPrograms() val programs = GLPrograms()
init { init {
@ -739,6 +757,42 @@ class StarboundClient : Closeable {
} }
private val layers = LayeredRenderer(this) private val layers = LayeredRenderer(this)
private var dotsIndex = 0
private val dotTime = 4
private var dotInc = 1
private fun renderLoadingText() {
var alpha = 1f
if (System.nanoTime() - loadingLog.lastActivity >= 3_000_000_000L) {
alpha = 1f - (System.nanoTime() - loadingLog.lastActivity - 3_000_000_000L) / 1_000_000_000f
}
stack.push()
stack.last().translate(y = viewportHeight.toFloat())
var shade = 255
for (line in loadingLog) {
if (line.progress in 0.01f ..< 1f) {
quadColor(RGBAColor(0f, shade / 400f * line.progress, 0f, alpha * 0.8f)) {
it.vertex(0f, font.lineHeight * -0.4f)
it.vertex(viewportWidth * line.progress, font.lineHeight * -0.4f)
it.vertex(viewportWidth * line.progress, 0f)
it.vertex(0f, 0f)
}
}
val size = font.render(line.text, alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
stack.last().translate(y = -size.height * 1.2f)
if (shade > 120) {
shade -= 10
}
}
stack.pop()
}
fun renderFrame(): Boolean { fun renderFrame(): Boolean {
ensureSameThread() ensureSameThread()
@ -781,6 +835,71 @@ class StarboundClient : Closeable {
return true return true
} }
if (!Starbound.initialized || !fontInitialized) {
executeQueuedTasks()
updateViewportParams()
clearColor = RGBAColor.BLACK
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
val min = viewportHeight.coerceAtMost(viewportWidth)
val size = min * 0.01f
val program = programs.positionColor
val builder = program.builder
uberShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
stack.clear(Matrix3f.identity())
program.colorMultiplier = RGBAColor.WHITE
if (!fontInitialized) {
builder.builder.begin(GeometryType.QUADS)
dotsIndex += dotInc
if (dotsIndex < 0) {
dotsIndex = 1
dotInc = 1
} else if (dotsIndex > dotTime * 3) {
dotsIndex = dotTime * 3 - 1
dotInc = -1
}
val a = if (dotsIndex / dotTime == 0) RGBAColor.SLATE_GRAY else RGBAColor.WHITE
val b = if (dotsIndex / dotTime == 1) RGBAColor.SLATE_GRAY else RGBAColor.WHITE
val c = if (dotsIndex / dotTime == 2) RGBAColor.SLATE_GRAY else RGBAColor.WHITE
builder.builder.vertex(viewportWidth * 0.5f - size - size * 4f, viewportHeight * 0.5f - size).color(a)
builder.builder.vertex(viewportWidth * 0.5f + size - size * 4f, viewportHeight * 0.5f - size).color(a)
builder.builder.vertex(viewportWidth * 0.5f + size - size * 4f, viewportHeight * 0.5f + size).color(a)
builder.builder.vertex(viewportWidth * 0.5f - size - size * 4f, viewportHeight * 0.5f + size).color(a)
builder.builder.vertex(viewportWidth * 0.5f - size, viewportHeight * 0.5f - size).color(b)
builder.builder.vertex(viewportWidth * 0.5f + size, viewportHeight * 0.5f - size).color(b)
builder.builder.vertex(viewportWidth * 0.5f + size, viewportHeight * 0.5f + size).color(b)
builder.builder.vertex(viewportWidth * 0.5f - size, viewportHeight * 0.5f + size).color(b)
builder.builder.vertex(viewportWidth * 0.5f - size + size * 4f, viewportHeight * 0.5f - size).color(c)
builder.builder.vertex(viewportWidth * 0.5f + size + size * 4f, viewportHeight * 0.5f - size).color(c)
builder.builder.vertex(viewportWidth * 0.5f + size + size * 4f, viewportHeight * 0.5f + size).color(c)
builder.builder.vertex(viewportWidth * 0.5f - size + size * 4f, viewportHeight * 0.5f + size).color(c)
program.use()
builder.upload()
builder.draw()
} else {
renderLoadingText()
}
GLFW.glfwSwapBuffers(window)
GLFW.glfwPollEvents()
return true
}
layers.clear() layers.clear()
uberShaderPrograms.forEachValid { uberShaderPrograms.forEachValid {
@ -875,36 +994,7 @@ class StarboundClient : Closeable {
fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen } fontShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
if (System.nanoTime() - loadingLog.lastActivity <= 4_000_000_000L) { if (System.nanoTime() - loadingLog.lastActivity <= 4_000_000_000L) {
var alpha = 1f //renderLoadingText()
if (System.nanoTime() - loadingLog.lastActivity >= 3_000_000_000L) {
alpha = 1f - (System.nanoTime() - loadingLog.lastActivity - 3_000_000_000L) / 1_000_000_000f
}
stack.push()
stack.last().translate(y = viewportHeight.toFloat())
var shade = 255
for (line in loadingLog) {
if (line.progress in 0.01f ..< 1f) {
quadColor(RGBAColor(0f, shade / 400f * line.progress, 0f, alpha * 0.8f)) {
it.vertex(0f, font.lineHeight * -0.4f)
it.vertex(viewportWidth * line.progress, font.lineHeight * -0.4f)
it.vertex(viewportWidth * line.progress, 0f)
it.vertex(0f, 0f)
}
}
val size = font.render(line.text, alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
stack.last().translate(y = -size.height * 1.2f)
if (shade > 120) {
shade -= 10
}
}
stack.pop()
} }
stack.clear(Matrix3f.identity()) stack.clear(Matrix3f.identity())

View File

@ -10,10 +10,24 @@ import ru.dbotthepony.kstarbound.world.physics.Poly
class PlayerEntity(world: World<*, *>) : Entity(world) { class PlayerEntity(world: World<*, *>) : Entity(world) {
override val movementParameters: PlayerMovementParameters = GlobalDefaults.playerMovementParameters override val movementParameters: PlayerMovementParameters = GlobalDefaults.playerMovementParameters
var isWalking = false
var isRunning = false
var isCrouching = false
var isFlying = false
var isFalling = false
var canJump = false
var isJumping = false
var isGroundMovement = false
var isLiquidMovement = false
init { init {
GlobalDefaults.playerMovementParameters.standingPoly?.let { GlobalDefaults.playerMovementParameters.standingPoly?.let {
//hitboxes.add(it) //hitboxes.add(it)
hitboxes.add(Starbound.gson.fromJson("""[ [-0.75, -2.0], [-0.35, -2.5], [0.35, -2.5], [0.75, -2.0], [0.75, 0.65], [0.35, 1.22], [-0.35, 1.22], [-0.75, 0.65] ]""", Poly::class.java)) hitboxes.add(Starbound.gson.fromJson("""[ [-0.75, -2.0], [-0.35, -2.5], [0.35, -2.5], [0.75, -2.0], [0.75, 0.65], [0.35, 1.22], [-0.35, 1.22], [-0.75, 0.65] ]""", Poly::class.java))
} }
} }
override fun move() {
super.move()
}
} }