SQLite as underlying file storage engine
This commit is contained in:
parent
87966e5fa1
commit
c016dade54
@ -22,6 +22,7 @@ val gsonVersion: String by project
|
|||||||
val log4jVersion: String by project
|
val log4jVersion: String by project
|
||||||
val guavaVersion: String by project
|
val guavaVersion: String by project
|
||||||
val junitVersion: String by project
|
val junitVersion: String by project
|
||||||
|
val sqliteVersion: String by project
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
@ -90,6 +91,8 @@ dependencies {
|
|||||||
implementation("com.github.ben-manes.caffeine:caffeine:$caffeineVersion")
|
implementation("com.github.ben-manes.caffeine:caffeine:$caffeineVersion")
|
||||||
implementation(project(":luna"))
|
implementation(project(":luna"))
|
||||||
|
|
||||||
|
implementation("org.xerial:sqlite-jdbc:$sqliteVersion")
|
||||||
|
|
||||||
implementation("io.netty:netty-transport:$nettyVersion")
|
implementation("io.netty:netty-transport:$nettyVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,3 +16,4 @@ gsonVersion=2.8.9
|
|||||||
log4jVersion=2.17.1
|
log4jVersion=2.17.1
|
||||||
guavaVersion=33.0.0-jre
|
guavaVersion=33.0.0-jre
|
||||||
junitVersion=5.8.2
|
junitVersion=5.8.2
|
||||||
|
sqliteVersion=3.45.3.0
|
||||||
|
@ -36,7 +36,6 @@ import ru.dbotthepony.kstarbound.server.world.LegacyWorldStorage
|
|||||||
import ru.dbotthepony.kstarbound.server.world.NativeWorldStorage
|
import ru.dbotthepony.kstarbound.server.world.NativeWorldStorage
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerSystemWorld
|
import ru.dbotthepony.kstarbound.server.world.ServerSystemWorld
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||||
import ru.dbotthepony.kstarbound.server.world.StorageEngine
|
|
||||||
import ru.dbotthepony.kstarbound.world.SystemWorldLocation
|
import ru.dbotthepony.kstarbound.world.SystemWorldLocation
|
||||||
import ru.dbotthepony.kstarbound.world.UniversePos
|
import ru.dbotthepony.kstarbound.world.UniversePos
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
@ -93,16 +92,14 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
|||||||
var shipChunkSource by Delegates.notNull<WorldStorage>()
|
var shipChunkSource by Delegates.notNull<WorldStorage>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private val storageEngine = StorageEngine.Memory({ shipChunks[it]?.orNull() }, { key, value -> shipChunks[key] = KOptional(value) })
|
|
||||||
|
|
||||||
override fun setupLegacy() {
|
override fun setupLegacy() {
|
||||||
super.setupLegacy()
|
super.setupLegacy()
|
||||||
shipChunkSource = LegacyWorldStorage(storageEngine)
|
shipChunkSource = LegacyWorldStorage.Memory({ shipChunks[it]?.orNull() }, { key, value -> shipChunks[key] = KOptional(value) })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupNative() {
|
override fun setupNative() {
|
||||||
super.setupNative()
|
super.setupNative()
|
||||||
shipChunkSource = NativeWorldStorage(storageEngine)
|
shipChunkSource = WorldStorage.NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
fun receiveShipChunks(chunks: Map<ByteKey, KOptional<ByteArray>>) {
|
fun receiveShipChunks(chunks: Map<ByteKey, KOptional<ByteArray>>) {
|
||||||
|
@ -10,7 +10,6 @@ import kotlinx.coroutines.future.asCompletableFuture
|
|||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.io.BTreeDB6
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
import ru.dbotthepony.kstarbound.Globals
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
@ -24,13 +23,13 @@ import ru.dbotthepony.kstarbound.server.world.LegacyWorldStorage
|
|||||||
import ru.dbotthepony.kstarbound.server.world.ServerUniverse
|
import ru.dbotthepony.kstarbound.server.world.ServerUniverse
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerSystemWorld
|
import ru.dbotthepony.kstarbound.server.world.ServerSystemWorld
|
||||||
import ru.dbotthepony.kstarbound.server.world.StorageEngine
|
|
||||||
import ru.dbotthepony.kstarbound.server.world.WorldStorage
|
import ru.dbotthepony.kstarbound.server.world.WorldStorage
|
||||||
import ru.dbotthepony.kstarbound.util.BlockableEventLoop
|
import ru.dbotthepony.kstarbound.util.BlockableEventLoop
|
||||||
import ru.dbotthepony.kstarbound.util.JVMClock
|
import ru.dbotthepony.kstarbound.util.JVMClock
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import ru.dbotthepony.kstarbound.world.UniversePos
|
import ru.dbotthepony.kstarbound.world.UniversePos
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.sql.DriverManager
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -84,23 +83,16 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
|
|
||||||
private suspend fun loadCelestialWorld(location: WorldID.Celestial): ServerWorld {
|
private suspend fun loadCelestialWorld(location: WorldID.Celestial): ServerWorld {
|
||||||
val file = File(universeFolder, location.pos.toString().replace(':', '_') + ".kworld")
|
val file = File(universeFolder, location.pos.toString().replace(':', '_') + ".kworld")
|
||||||
var firstTime = false
|
val firstTime = !file.exists()
|
||||||
|
val storage = LegacyWorldStorage.SQL(file)
|
||||||
|
|
||||||
val engine = if (!file.exists()) {
|
val world = if (firstTime) {
|
||||||
firstTime = true
|
|
||||||
LOGGER.info("Creating celestial world $location")
|
LOGGER.info("Creating celestial world $location")
|
||||||
BTreeDB6.create(file, blockSize = 1024)
|
ServerWorld.create(this, WorldTemplate.create(location.pos, universe), storage, location)
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Loading celestial world $location")
|
LOGGER.info("Loading celestial world $location")
|
||||||
BTreeDB6(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
val storage = LegacyWorldStorage(StorageEngine.DB6(engine))
|
|
||||||
|
|
||||||
val world = if (firstTime)
|
|
||||||
ServerWorld.create(this, WorldTemplate.create(location.pos, universe), storage, location)
|
|
||||||
else
|
|
||||||
ServerWorld.load(this, storage, location).await()
|
ServerWorld.load(this, storage, location).await()
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
world.sky.referenceClock = universeClock
|
world.sky.referenceClock = universeClock
|
||||||
@ -319,10 +311,10 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
|
|
||||||
worlds.values.forEach {
|
worlds.values.forEach {
|
||||||
if (it.isDone && !it.isCompletedExceptionally) {
|
if (it.isDone && !it.isCompletedExceptionally) {
|
||||||
it.get().eventLoop.awaitTermination(10L, TimeUnit.SECONDS)
|
it.get().eventLoop.awaitTermination(60L, TimeUnit.SECONDS)
|
||||||
|
|
||||||
if (!it.get().eventLoop.isTerminated) {
|
if (!it.get().eventLoop.isTerminated) {
|
||||||
LOGGER.warn("World ${it.get()} did not shutdown in 10 seconds, forcing termination. This might leave world in inconsistent state!")
|
LOGGER.warn("World ${it.get()} did not shutdown in 60 seconds, forcing termination. This might leave world in inconsistent state!")
|
||||||
it.get().eventLoop.shutdownNow()
|
it.get().eventLoop.shutdownNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,16 @@ import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
|
|||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.arrays.Object2DArray
|
import ru.dbotthepony.kommons.arrays.Object2DArray
|
||||||
|
import ru.dbotthepony.kommons.io.BTreeDB6
|
||||||
import ru.dbotthepony.kommons.io.ByteKey
|
import ru.dbotthepony.kommons.io.ByteKey
|
||||||
import ru.dbotthepony.kommons.io.readVarInt
|
import ru.dbotthepony.kommons.io.readVarInt
|
||||||
import ru.dbotthepony.kommons.io.writeVarInt
|
import ru.dbotthepony.kommons.io.writeVarInt
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.io.BTreeDB5
|
||||||
import ru.dbotthepony.kstarbound.json.VersionedJson
|
import ru.dbotthepony.kstarbound.json.VersionedJson
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
|
import ru.dbotthepony.kstarbound.util.CarriedExecutor
|
||||||
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.ChunkState
|
import ru.dbotthepony.kstarbound.world.ChunkState
|
||||||
@ -25,18 +28,32 @@ import java.io.BufferedOutputStream
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.ref.Cleaner
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.PreparedStatement
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
|
import java.util.function.Supplier
|
||||||
import java.util.zip.DeflaterOutputStream
|
import java.util.zip.DeflaterOutputStream
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
sealed class LegacyWorldStorage() : WorldStorage() {
|
||||||
|
protected abstract fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>>
|
||||||
|
protected abstract fun write(at: ByteKey, value: ByteArray)
|
||||||
|
|
||||||
class LegacyWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|
||||||
override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Pair<Object2DArray<out AbstractCell>, ChunkState>>> {
|
override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Pair<Object2DArray<out AbstractCell>, ChunkState>>> {
|
||||||
val chunkX = pos.x
|
val chunkX = pos.x
|
||||||
val chunkY = pos.y
|
val chunkY = pos.y
|
||||||
val key = ByteKey(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
val key = ByteKey(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
||||||
|
|
||||||
return engine.load(key).thenApplyAsync(Function {
|
return load(key).thenApplyAsync(Function {
|
||||||
it.map {
|
it.map {
|
||||||
val reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(it))))
|
val reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(it))))
|
||||||
val generationLevel = reader.readVarInt()
|
val generationLevel = reader.readVarInt()
|
||||||
@ -70,13 +87,13 @@ class LegacyWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|||||||
val chunkY = pos.y
|
val chunkY = pos.y
|
||||||
val key = ByteKey(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
val key = ByteKey(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
||||||
|
|
||||||
return engine.load(key).thenApplyAsync(Function {
|
return load(key).thenApplyAsync(Function {
|
||||||
it.map { readEntities(pos, it) }
|
it.map { readEntities(pos, it) }
|
||||||
}, Starbound.EXECUTOR)
|
}, Starbound.EXECUTOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
||||||
return engine.load(metadataKey).thenApplyAsync(Function {
|
return load(metadataKey).thenApplyAsync(Function {
|
||||||
it.flatMap {
|
it.flatMap {
|
||||||
val stream = DataInputStream(BufferedInputStream(InflaterInputStream(FastByteArrayInputStream(it))))
|
val stream = DataInputStream(BufferedInputStream(InflaterInputStream(FastByteArrayInputStream(it))))
|
||||||
|
|
||||||
@ -95,7 +112,7 @@ class LegacyWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|||||||
val chunkY = pos.y
|
val chunkY = pos.y
|
||||||
val key = ByteKey(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
val key = ByteKey(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
||||||
|
|
||||||
engine.write(key, writeEntities(data))
|
write(key, writeEntities(data))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +143,7 @@ class LegacyWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|||||||
val key = ByteKey(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
val key = ByteKey(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())
|
||||||
|
|
||||||
stream.close()
|
stream.close()
|
||||||
engine.write(key, buff.array.copyOf(buff.length))
|
write(key, buff.array.copyOf(buff.length))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,12 +156,96 @@ class LegacyWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|||||||
data.data.write(stream)
|
data.data.write(stream)
|
||||||
|
|
||||||
stream.close()
|
stream.close()
|
||||||
engine.write(metadataKey, buff.array.copyOf(buff.length))
|
write(metadataKey, buff.array.copyOf(buff.length))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
class Memory(private val get: (ByteKey) -> ByteArray?, private val set: (ByteKey, ByteArray) -> Unit) : LegacyWorldStorage() {
|
||||||
engine.close()
|
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
||||||
|
return CompletableFuture.completedFuture(KOptional.ofNullable(get(at)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(at: ByteKey, value: ByteArray) {
|
||||||
|
set(at, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {}
|
||||||
|
override fun sync() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DB5(private val database: BTreeDB5) : LegacyWorldStorage() {
|
||||||
|
private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)
|
||||||
|
|
||||||
|
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
||||||
|
return CompletableFuture.supplyAsync(Supplier { database.read(at) }, carrier)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(at: ByteKey, value: ByteArray) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sync() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
carrier.execute { database.close() }
|
||||||
|
carrier.wait(300L, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SQL(path: File) : LegacyWorldStorage() {
|
||||||
|
private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)
|
||||||
|
private val connection = DriverManager.getConnection("jdbc:sqlite:${path.canonicalPath.replace('\\', '/')}")
|
||||||
|
|
||||||
|
init {
|
||||||
|
connection.autoCommit = false
|
||||||
|
|
||||||
|
connection.createStatement().use {
|
||||||
|
it.execute("""CREATE TABLE IF NOT EXISTS `data` (
|
||||||
|
|`key` BLOB NOT NULL PRIMARY KEY,
|
||||||
|
|`value` BLOB NOT NULL
|
||||||
|
|)""".trimMargin())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sync() {
|
||||||
|
carrier.execute { connection.commit() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// concurrent safety - load(), write() and close() are always called on one thread
|
||||||
|
// and load()/write() will never be called after close()
|
||||||
|
private val loader = connection.prepareStatement("SELECT `value` FROM `data` WHERE `key` = ? LIMIT 1")
|
||||||
|
private val writer = connection.prepareStatement("REPLACE INTO `data` (`key`, `value`) VALUES (?, ?)")
|
||||||
|
|
||||||
|
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
||||||
|
return CompletableFuture.supplyAsync(Supplier {
|
||||||
|
loader.setBytes(1, at.toByteArray())
|
||||||
|
|
||||||
|
loader.executeQuery().use {
|
||||||
|
if (it.next()) {
|
||||||
|
val blob = it.getBytes(1)
|
||||||
|
KOptional(blob)
|
||||||
|
} else {
|
||||||
|
KOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, carrier)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(at: ByteKey, value: ByteArray) {
|
||||||
|
carrier.execute {
|
||||||
|
writer.setBytes(1, at.toByteArray())
|
||||||
|
writer.setBytes(2, value)
|
||||||
|
writer.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
carrier.execute { connection.commit() }
|
||||||
|
carrier.execute { connection.close() }
|
||||||
|
carrier.wait(300L, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -10,7 +10,7 @@ import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
|||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class NativeWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
class NativeWorldStorage() : WorldStorage() {
|
||||||
override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Pair<Object2DArray<out AbstractCell>, ChunkState>>> {
|
override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Pair<Object2DArray<out AbstractCell>, ChunkState>>> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
@ -35,7 +35,11 @@ class NativeWorldStorage(private val engine: StorageEngine) : WorldStorage() {
|
|||||||
return super.saveMetadata(data)
|
return super.saveMetadata(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sync() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
engine.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -555,7 +555,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
|||||||
aabbd,
|
aabbd,
|
||||||
filter = Predicate { it.isPersistent && !it.isRemote && aabbd.isInside(it.position) })
|
filter = Predicate { it.isPersistent && !it.isRemote && aabbd.isInside(it.position) })
|
||||||
|
|
||||||
world.storage.saveCells(pos, cells.value, state)
|
world.storage.saveCells(pos, copyCells(), state)
|
||||||
world.storage.saveEntities(pos, unloadable)
|
world.storage.saveEntities(pos, unloadable)
|
||||||
|
|
||||||
return unloadable
|
return unloadable
|
||||||
|
@ -17,7 +17,6 @@ import ru.dbotthepony.kommons.util.IStruct2i
|
|||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
import ru.dbotthepony.kstarbound.Globals
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.VersionRegistry
|
import ru.dbotthepony.kstarbound.VersionRegistry
|
||||||
import ru.dbotthepony.kstarbound.defs.WarpAction
|
import ru.dbotthepony.kstarbound.defs.WarpAction
|
||||||
@ -169,6 +168,10 @@ class ServerWorld private constructor(
|
|||||||
eventLoop.scheduleAtFixedRate(Runnable {
|
eventLoop.scheduleAtFixedRate(Runnable {
|
||||||
tick(Starbound.TIMESTEP)
|
tick(Starbound.TIMESTEP)
|
||||||
}, 0L, Starbound.TIMESTEP_NANOS, TimeUnit.NANOSECONDS)
|
}, 0L, Starbound.TIMESTEP_NANOS, TimeUnit.NANOSECONDS)
|
||||||
|
|
||||||
|
eventLoop.scheduleWithFixedDelay(Runnable {
|
||||||
|
storage.sync()
|
||||||
|
}, 10L, 10L, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.server.world
|
|
||||||
|
|
||||||
import ru.dbotthepony.kommons.io.BTreeDB6
|
|
||||||
import ru.dbotthepony.kommons.io.ByteKey
|
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.io.BTreeDB5
|
|
||||||
import ru.dbotthepony.kstarbound.util.CarriedExecutor
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
abstract class StorageEngine : Closeable {
|
|
||||||
abstract fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>>
|
|
||||||
abstract fun write(at: ByteKey, value: ByteArray)
|
|
||||||
|
|
||||||
class Memory(private val get: (ByteKey) -> ByteArray?, private val set: (ByteKey, ByteArray) -> Unit) : StorageEngine() {
|
|
||||||
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
|
||||||
return CompletableFuture.completedFuture(KOptional.ofNullable(get(at)))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(at: ByteKey, value: ByteArray) {
|
|
||||||
set(at, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DB5(private val database: BTreeDB5) : StorageEngine() {
|
|
||||||
private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)
|
|
||||||
|
|
||||||
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
|
||||||
return CompletableFuture.supplyAsync(Supplier { database.read(at) }, carrier)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(at: ByteKey, value: ByteArray) {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
carrier.execute { database.close() }
|
|
||||||
carrier.wait(300L, TimeUnit.SECONDS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DB6(private val database: BTreeDB6) : StorageEngine() {
|
|
||||||
private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)
|
|
||||||
|
|
||||||
override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
|
|
||||||
return CompletableFuture.supplyAsync(Supplier { database.read(at) }, carrier)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(at: ByteKey, value: ByteArray) {
|
|
||||||
carrier.execute { database.write(at, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
carrier.execute { database.close() }
|
|
||||||
carrier.wait(300L, TimeUnit.SECONDS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -36,6 +36,8 @@ abstract class WorldStorage : Closeable {
|
|||||||
abstract fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>>
|
abstract fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>>
|
||||||
abstract fun loadMetadata(): CompletableFuture<KOptional<Metadata>>
|
abstract fun loadMetadata(): CompletableFuture<KOptional<Metadata>>
|
||||||
|
|
||||||
|
abstract fun sync()
|
||||||
|
|
||||||
protected fun readEntities(pos: ChunkPos, data: ByteArray): List<AbstractEntity> {
|
protected fun readEntities(pos: ChunkPos, data: ByteArray): List<AbstractEntity> {
|
||||||
val reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data))))
|
val reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data))))
|
||||||
val i = reader.readVarInt()
|
val i = reader.readVarInt()
|
||||||
@ -107,6 +109,8 @@ abstract class WorldStorage : Closeable {
|
|||||||
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
||||||
return CompletableFuture.completedFuture(KOptional())
|
return CompletableFuture.completedFuture(KOptional())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sync() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Nothing : WorldStorage() {
|
object Nothing : WorldStorage() {
|
||||||
@ -121,6 +125,8 @@ abstract class WorldStorage : Closeable {
|
|||||||
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
|
||||||
return CompletableFuture.completedFuture(KOptional())
|
return CompletableFuture.completedFuture(KOptional())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sync() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -163,5 +169,9 @@ abstract class WorldStorage : Closeable {
|
|||||||
override fun close() {
|
override fun close() {
|
||||||
children.forEach { it.close() }
|
children.forEach { it.close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun sync() {
|
||||||
|
children.forEach { it.sync() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user