Skip to content

Commit c50da72

Browse files
committed
Add caching for MapDefinitions
1 parent 794ef40 commit c50da72

File tree

12 files changed

+156
-157
lines changed

12 files changed

+156
-157
lines changed

engine/src/main/kotlin/world/gregs/voidps/engine/data/ConfigFiles.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ data class ConfigFiles(
2929
fun find(path: String, type: String = "toml"): String = map.getOrDefault(type, emptyList()).firstOrNull { it.endsWith(path) } ?: throw NoSuchFileException("Unable to find config file '$path' in /data/ directory.")
3030
}
3131

32-
fun tempCache(cachePath: String = Settings["storage.cache.temp.path"]): File? {
33-
if (!Settings["storage.cache.temp.active", false]) {
32+
fun tempCache(cachePath: String = Settings["storage.caching.path"]): File? {
33+
if (!Settings["storage.caching.active", false]) {
3434
return null
3535
}
3636
val directory = File(cachePath)

engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/MapDefinitions.kt

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,19 @@ package world.gregs.voidps.engine.data.definition
22

33
import com.github.michaelbull.logging.InlineLogger
44
import world.gregs.voidps.cache.Cache
5-
import world.gregs.voidps.cache.FileCache
65
import world.gregs.voidps.cache.Index
76
import world.gregs.voidps.cache.definition.decoder.MapTileDecoder
8-
import world.gregs.voidps.cache.definition.decoder.ObjectDecoder
97
import world.gregs.voidps.engine.client.ui.chat.plural
10-
import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates
8+
import world.gregs.voidps.engine.data.ConfigFiles
119
import world.gregs.voidps.engine.data.Settings
12-
import world.gregs.voidps.engine.data.configFiles
1310
import world.gregs.voidps.engine.entity.obj.GameObjects
1411
import world.gregs.voidps.engine.map.collision.CollisionDecoder
15-
import world.gregs.voidps.engine.map.collision.Collisions
16-
import world.gregs.voidps.engine.map.collision.GameObjectCollisionAdd
17-
import world.gregs.voidps.engine.map.collision.GameObjectCollisionRemove
1812
import world.gregs.voidps.engine.map.obj.MapObjectsDecoder
1913
import world.gregs.voidps.engine.map.obj.MapObjectsRotatedDecoder
2014
import world.gregs.voidps.type.Region
2115
import world.gregs.voidps.type.Zone
16+
import java.io.File
2217
import kotlin.system.exitProcess
23-
import kotlin.time.measureTimedValue
2418

2519
/**
2620
* Loads map collision and objects fast and direct
@@ -38,30 +32,53 @@ class MapDefinitions(
3832
private val decoder = MapObjectsDecoder(objects, definitions)
3933
private val rotationDecoder = MapObjectsRotatedDecoder(objects, definitions)
4034

41-
fun loadCache(xteas: Map<Int, IntArray>? = null): MapDefinitions {
35+
fun load(configFiles: ConfigFiles, xteas: Map<Int, IntArray>? = null): MapDefinitions {
4236
try {
43-
val start = System.currentTimeMillis()
44-
var regions = 0
45-
val settings = ByteArray(16384)
46-
for (regionX in 0 until 256) {
47-
for (regionY in 0 until 256) {
48-
if (!loadSettings(cache, regionX, regionY, settings)) {
49-
continue
50-
}
51-
collisions.decode(settings, regionX shl 6, regionY shl 6)
52-
val keys = if (xteas != null) xteas[Region.id(regionX, regionY)] else null
53-
decoder.decode(cache, settings, regionX, regionY, keys)
54-
regions++
55-
}
37+
if (!Settings["storage.caching.active", false]) {
38+
loadCache(xteas)
39+
return this
40+
}
41+
val path = Settings["storage.caching.path"]
42+
File(path).mkdirs()
43+
val objectsFile = File("${path}${Settings["storage.caching.objects"]}")
44+
val collisionsFile = File("${path}${Settings["storage.caching.collisions"]}")
45+
if (objectsFile.exists() && collisionsFile.exists() && !configFiles.cacheUpdate) {
46+
val start = System.currentTimeMillis()
47+
val zones = collisions.load(collisionsFile)
48+
objects.load(objectsFile)
49+
logger.info { "Loaded all maps $zones zones ${objects.size} ${"object".plural(objects.size)} in ${System.currentTimeMillis() - start}ms" }
50+
} else {
51+
loadCache(xteas)
52+
val start = System.currentTimeMillis()
53+
collisions.save(collisionsFile)
54+
objects.save(objectsFile)
55+
logger.info { "Cached maps in ${System.currentTimeMillis() - start}ms" }
5656
}
57-
logger.info { "Loaded $regions maps ${objects.size} ${"object".plural(objects.size)} in ${System.currentTimeMillis() - start}ms" }
5857
return this
5958
} catch (e: ArrayIndexOutOfBoundsException) {
6059
logger.error(e) { "Error loading map definition; do you have the latest cache?" }
6160
exitProcess(1)
6261
}
6362
}
6463

64+
private fun loadCache(xteas: Map<Int, IntArray>? = null) {
65+
val start = System.currentTimeMillis()
66+
var regions = 0
67+
val settings = ByteArray(16384)
68+
for (regionX in 0 until 256) {
69+
for (regionY in 0 until 256) {
70+
if (!loadSettings(cache, regionX, regionY, settings)) {
71+
continue
72+
}
73+
collisions.decode(settings, regionX shl 6, regionY shl 6)
74+
val keys = if (xteas != null) xteas[Region.id(regionX, regionY)] else null
75+
decoder.decode(cache, settings, regionX, regionY, keys)
76+
regions++
77+
}
78+
}
79+
logger.info { "Loaded $regions maps ${objects.size} ${"object".plural(objects.size)} in ${System.currentTimeMillis() - start}ms" }
80+
}
81+
6582
fun loadZone(from: Zone, to: Zone, rotation: Int, xteas: Map<Int, IntArray>? = null) {
6683
val start = System.currentTimeMillis()
6784
val settings = loadSettings(cache, from.region.x, from.region.y) ?: return
@@ -95,19 +112,4 @@ class MapDefinitions(
95112
return settings
96113
}
97114

98-
companion object {
99-
@JvmStatic
100-
fun main(args: Array<String>) {
101-
val properties = Settings.load()
102-
// properties["storage.cache.path"] = "./data/cache-old/"
103-
val (cache, duration) = measureTimedValue { FileCache.load(properties) }
104-
println("Loaded cache in ${duration.inWholeMilliseconds}ms")
105-
val files = configFiles()
106-
val definitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).load(cache)).load(files.list(Settings["definitions.objects"]))
107-
val collisions = Collisions()
108-
val add = GameObjectCollisionAdd(collisions)
109-
val remove = GameObjectCollisionRemove(collisions)
110-
val defs = MapDefinitions(CollisionDecoder(collisions), definitions, GameObjects(add, remove, ZoneBatchUpdates(), definitions, storeUnused = true), cache).loadCache()
111-
}
112-
}
113115
}

engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectArrayMap.kt

Lines changed: 0 additions & 74 deletions
This file was deleted.

engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMap.kt

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
package world.gregs.voidps.engine.entity.obj
22

33
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap
4+
import world.gregs.voidps.buffer.read.ArrayReader
5+
import world.gregs.voidps.buffer.write.ArrayWriter
46
import world.gregs.voidps.type.Tile
57
import world.gregs.voidps.type.Zone
8+
import java.io.File
69

710
/**
811
* Stores [GameObject]s by zone + [ObjectLayer]
9-
* Fast and low memory usage for a small (<100k) number of objects.
1012
*/
11-
class GameObjectHashMap : GameObjectMap {
12-
private val data = Int2IntOpenHashMap(EXPECTED_OBJECT_COUNT)
13+
class GameObjectHashMap {
14+
private var data = Int2IntOpenHashMap(EXPECTED_OBJECT_COUNT)
1315

14-
override fun get(obj: GameObject): Int = data.getOrDefault(index(obj), -1)
16+
operator fun get(obj: GameObject): Int = data.getOrDefault(index(obj), -1)
1517

16-
override operator fun get(x: Int, y: Int, level: Int, layer: Int): Int = data.getOrDefault(index(x, y, level, layer), -1)
18+
operator fun get(x: Int, y: Int, level: Int, layer: Int): Int = data.getOrDefault(index(x, y, level, layer), -1)
1719

18-
override operator fun set(x: Int, y: Int, level: Int, layer: Int, mask: Int) {
20+
operator fun set(x: Int, y: Int, level: Int, layer: Int, mask: Int) {
1921
data[index(x, y, level, layer)] = mask
2022
}
2123

22-
override fun add(obj: GameObject, mask: Int) {
24+
fun add(obj: GameObject, mask: Int) {
2325
val index = index(obj)
2426
val currentFlags = data.getOrDefault(index, 0)
2527
data[index] = currentFlags or mask
2628
}
2729

28-
override fun remove(obj: GameObject, mask: Int) {
30+
fun remove(obj: GameObject, mask: Int) {
2931
val index = index(obj)
3032
val currentFlags = data.getOrDefault(index, -1)
3133
data[index] = currentFlags and mask.inv()
3234
}
3335

34-
override fun deallocateZone(zoneX: Int, zoneY: Int, level: Int) {
36+
fun deallocateZone(zoneX: Int, zoneY: Int, level: Int) {
3537
val zone = Zone.id(zoneX, zoneY, level)
3638
for (x in 0 until 8) {
3739
for (y in 0 until 8) {
@@ -43,10 +45,34 @@ class GameObjectHashMap : GameObjectMap {
4345
}
4446
}
4547

46-
override fun clear() {
48+
fun clear() {
4749
data.clear()
4850
}
4951

52+
fun save(file: File) {
53+
val writer = ArrayWriter(4 + data.size * 8)
54+
writer.writeInt(data.size)
55+
val list = data.toList()
56+
for (pair in list) {
57+
writer.writeInt(pair.first)
58+
}
59+
for (pair in list) {
60+
writer.writeInt(pair.second)
61+
}
62+
file.writeBytes(writer.toArray())
63+
}
64+
65+
fun load(file: File): Int {
66+
val reader = ArrayReader(file.readBytes())
67+
val size = reader.readInt()
68+
val keys = IntArray(size)
69+
val values = IntArray(size)
70+
reader.readBytes(keys)
71+
reader.readBytes(values)
72+
data = Int2IntOpenHashMap(keys, values)
73+
return size
74+
}
75+
5076
companion object {
5177
private const val EXPECTED_OBJECT_COUNT = 74_000
5278
private fun index(obj: GameObject): Int = index(obj.x, obj.y, obj.level, ObjectLayer.layer(obj.shape))

engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectMap.kt

Lines changed: 0 additions & 21 deletions
This file was deleted.

engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjects.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import world.gregs.voidps.network.login.protocol.encode.zone.ObjectAddition
1616
import world.gregs.voidps.network.login.protocol.encode.zone.ObjectRemoval
1717
import world.gregs.voidps.type.Tile
1818
import world.gregs.voidps.type.Zone
19+
import java.io.File
1920

2021
/**
2122
* Stores GameObjects and modifications mainly for verifying interactions
@@ -32,12 +33,20 @@ class GameObjects(
3233
private val definitions: ObjectDefinitions,
3334
private val storeUnused: Boolean = false,
3435
) : ZoneBatchUpdates.Sender {
35-
private val map = if (storeUnused) GameObjectArrayMap() else GameObjectHashMap()
36+
private val map = GameObjectHashMap()
3637
private val replacements: MutableMap<Int, Int> = Int2IntOpenHashMap()
3738
val timers = GameObjectTimers()
3839
var size = 0
3940
private set
4041

42+
fun load(file: File) {
43+
size = map.load(file)
44+
}
45+
46+
fun save(file: File) {
47+
map.save(file)
48+
}
49+
4150
/**
4251
* Adds a temporary object with [id] [tile] [shape] and [rotation]
4352
* Optionally removed after [ticks]

0 commit comments

Comments
 (0)