Finally, client threads
This commit is contained in:
parent
0b961b72ae
commit
278f66d892
@ -48,7 +48,7 @@ fun main() {
|
|||||||
// println(VersionedJson(meta))
|
// println(VersionedJson(meta))
|
||||||
|
|
||||||
val server = IntegratedStarboundServer(File("./"))
|
val server = IntegratedStarboundServer(File("./"))
|
||||||
val client = StarboundClient()
|
val client = StarboundClient.create().get()
|
||||||
val world = ServerWorld(server, 0L, WorldGeometry(Vector2i(3000, 2000), true, false))
|
val world = ServerWorld(server, 0L, WorldGeometry(Vector2i(3000, 2000), true, false))
|
||||||
world.addChunkSource(LegacyChunkSource(db))
|
world.addChunkSource(LegacyChunkSource(db))
|
||||||
world.startThread()
|
world.startThread()
|
||||||
@ -74,54 +74,6 @@ fun main() {
|
|||||||
//ply!!.position = Vector2d(225.0, 680.0)
|
//ply!!.position = Vector2d(225.0, 680.0)
|
||||||
//ply!!.spawn()
|
//ply!!.spawn()
|
||||||
|
|
||||||
//for (chunkX in 17 .. 18) {
|
|
||||||
//for (chunkX in 14 .. 24) {
|
|
||||||
for (chunkX in 0 .. 100) {
|
|
||||||
//for (chunkX in 0 .. 17) {
|
|
||||||
// for (chunkY in 21 .. 21) {
|
|
||||||
for (chunkY in 18 .. 24) {
|
|
||||||
//val data = db.read(byteArrayOf(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte()))
|
|
||||||
//val data2 = db.read(byteArrayOf(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte()))
|
|
||||||
|
|
||||||
/*if (data != null) {
|
|
||||||
var reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data), Inflater())))
|
|
||||||
reader.skipBytes(3)
|
|
||||||
|
|
||||||
val chunk = world.chunkMap.compute(chunkX, chunkY)
|
|
||||||
|
|
||||||
if (chunk != null) {
|
|
||||||
for (y in 0 .. 31) {
|
|
||||||
for (x in 0 .. 31) {
|
|
||||||
check(chunk.setCell(x, y, MutableCell().read(reader)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*if (data2 != null) {
|
|
||||||
val reader = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(data2), Inflater())))
|
|
||||||
val i = reader.readVarInt()
|
|
||||||
|
|
||||||
for (i2 in 0 until i) {
|
|
||||||
val obj = VersionedJson(reader)
|
|
||||||
|
|
||||||
if (obj.identifier == "ObjectEntity") {
|
|
||||||
try {
|
|
||||||
WorldObject(world, obj.content.asJsonObject).spawn()
|
|
||||||
//println(obj.content)
|
|
||||||
//println(created)
|
|
||||||
} catch (err: Throwable) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//val read = BinaryJsonReader.readElement(reader)
|
|
||||||
//println(read)
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
||||||
|
|
||||||
val rand = Random()
|
val rand = Random()
|
||||||
@ -137,47 +89,12 @@ fun main() {
|
|||||||
//item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0))
|
//item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
client.connectToLocalServer(client, server.channels.createLocalChannel(), UUID.randomUUID())
|
client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
|
||||||
}
|
}
|
||||||
|
|
||||||
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
||||||
client.camera.pos = Vector2d(238.0, 685.0)
|
client.camera.pos = Vector2d(238.0, 685.0)
|
||||||
//client.camera.pos = Vector2f(0f, 0f)
|
//client.camera.pos = Vector2f(0f, 0f)
|
||||||
|
|
||||||
client.onDrawGUI {
|
|
||||||
client.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
//ent.spawn()
|
//ent.spawn()
|
||||||
|
|
||||||
client.input.addScrollCallback { _, x, y ->
|
|
||||||
if (y > 0.0) {
|
|
||||||
client.settings.zoom *= y.toFloat() * 2f
|
|
||||||
} else if (y < 0.0) {
|
|
||||||
client.settings.zoom /= -y.toFloat() * 2f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (client.renderFrame()) {
|
|
||||||
val ply = client.activeConnection?.character
|
|
||||||
|
|
||||||
if (ply != null) {
|
|
||||||
client.camera.pos = ply.position
|
|
||||||
|
|
||||||
ply.movement.controlMove = if (client.input.KEY_A_DOWN) Direction.LEFT else if (client.input.KEY_D_DOWN) Direction.RIGHT else null
|
|
||||||
ply.movement.controlJump = client.input.KEY_SPACE_DOWN
|
|
||||||
ply.movement.controlRun = !client.input.KEY_LEFT_SHIFT_DOWN
|
|
||||||
} else {
|
|
||||||
client.camera.pos += Vector2d(
|
|
||||||
(if (client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0),
|
|
||||||
(if (client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.input.KEY_ESCAPE_PRESSED) {
|
|
||||||
glfwSetWindowShouldClose(client.window, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,6 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
|||||||
import ru.dbotthepony.kstarbound.client.input.UserInput
|
import ru.dbotthepony.kstarbound.client.input.UserInput
|
||||||
import ru.dbotthepony.kstarbound.client.network.ClientConnection
|
import ru.dbotthepony.kstarbound.client.network.ClientConnection
|
||||||
import ru.dbotthepony.kstarbound.server.network.packets.TrackedPositionPacket
|
import ru.dbotthepony.kstarbound.server.network.packets.TrackedPositionPacket
|
||||||
import ru.dbotthepony.kstarbound.server.network.packets.TrackedSizePacket
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.Camera
|
import ru.dbotthepony.kstarbound.client.render.Camera
|
||||||
import ru.dbotthepony.kstarbound.client.render.Font
|
import ru.dbotthepony.kstarbound.client.render.Font
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
@ -58,6 +57,7 @@ import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
|||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
import ru.dbotthepony.kstarbound.util.forEachValid
|
import ru.dbotthepony.kstarbound.util.forEachValid
|
||||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||||
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||||
@ -79,6 +79,7 @@ import java.nio.ByteBuffer
|
|||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.ForkJoinPool
|
import java.util.concurrent.ForkJoinPool
|
||||||
import java.util.concurrent.ForkJoinWorkerThread
|
import java.util.concurrent.ForkJoinWorkerThread
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@ -92,19 +93,20 @@ import kotlin.math.absoluteValue
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class StarboundClient : Closeable {
|
class StarboundClient private constructor(val clientID: Int) : Closeable {
|
||||||
val window: Long
|
val window: Long
|
||||||
val camera = Camera(this)
|
val camera = Camera(this)
|
||||||
val input = UserInput()
|
val input = UserInput()
|
||||||
val thread: Thread = Thread.currentThread()
|
val thread: Thread = Thread.currentThread()
|
||||||
private val threadCounter = AtomicInteger()
|
private val threadCounter = AtomicInteger()
|
||||||
|
|
||||||
// client specific executor which will accept tasks which involve probable
|
// client specific executor which will accept tasks which involve probable
|
||||||
// callback to foreground executor to initialize thread-unsafe data
|
// callback to foreground executor to initialize thread-unsafe data
|
||||||
// In above case too many threads will introduce big congestion for resources, stalling entire workload; wasting cpu resources
|
// In above case too many threads will introduce big congestion for resources, stalling entire workload; wasting cpu resources
|
||||||
val executor = ForkJoinPool(Runtime.getRuntime().availableProcessors().coerceAtMost(4), {
|
val executor = ForkJoinPool(Runtime.getRuntime().availableProcessors().coerceAtMost(4), {
|
||||||
object : ForkJoinWorkerThread(it) {
|
object : ForkJoinWorkerThread(it) {
|
||||||
init {
|
init {
|
||||||
name = "Background Executor for '${thread.name}'-${threadCounter.incrementAndGet()}"
|
name = "Starbound Client $clientID executor ${threadCounter.incrementAndGet()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTermination(exception: Throwable?) {
|
override fun onTermination(exception: Throwable?) {
|
||||||
@ -150,7 +152,7 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
var preciseWait = false
|
var preciseWait = false
|
||||||
|
|
||||||
var clientTerminated = false
|
var shouldTerminate = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var viewportMatrixScreen: Matrix3f
|
var viewportMatrixScreen: Matrix3f
|
||||||
@ -172,14 +174,14 @@ class StarboundClient : Closeable {
|
|||||||
activeConnection = ClientConnection.connectToLocalServer(client, address, uuid)
|
activeConnection = ClientConnection.connectToLocalServer(client, address, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connectToLocalServer(client: StarboundClient, address: Channel, uuid: UUID) {
|
fun connectToLocalServer(address: Channel, uuid: UUID) {
|
||||||
check(activeConnection == null) { "Already having active connection to server: $activeConnection" }
|
check(activeConnection == null) { "Already having active connection to server: $activeConnection" }
|
||||||
activeConnection = ClientConnection.connectToLocalServer(client, address, uuid)
|
activeConnection = ClientConnection.connectToLocalServer(this, address, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connectToRemoteServer(client: StarboundClient, address: SocketAddress, uuid: UUID) {
|
fun connectToRemoteServer(address: SocketAddress, uuid: UUID) {
|
||||||
check(activeConnection == null) { "Already having active connection to server: $activeConnection" }
|
check(activeConnection == null) { "Already having active connection to server: $activeConnection" }
|
||||||
activeConnection = ClientConnection.connectToRemoteServer(client, address, uuid)
|
activeConnection = ClientConnection.connectToRemoteServer(this, address, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val scissorStack = LinkedList<ScissorRect>()
|
private val scissorStack = LinkedList<ScissorRect>()
|
||||||
@ -934,7 +936,7 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renderFrame(): Boolean {
|
private fun renderFrame(): Boolean {
|
||||||
ensureSameThread()
|
ensureSameThread()
|
||||||
|
|
||||||
var diff = nextRender - System.nanoTime()
|
var diff = nextRender - System.nanoTime()
|
||||||
@ -1012,6 +1014,12 @@ class StarboundClient : Closeable {
|
|||||||
fn.invoke()
|
fn.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (world != null) {
|
||||||
|
font.render("Camera: ${camera.pos} ${settings.zoom}", y = 140f, scale = 0.25f)
|
||||||
|
font.render("Cursor: $mouseCoordinates -> ${screenToWorld(mouseCoordinates)}", y = 160f, scale = 0.25f)
|
||||||
|
font.render("World chunk: ${world.chunkFromCell(camera.pos)}", y = 180f, scale = 0.25f)
|
||||||
|
}
|
||||||
|
|
||||||
drawPerformanceBasic(false)
|
drawPerformanceBasic(false)
|
||||||
|
|
||||||
GLFW.glfwSwapBuffers(window)
|
GLFW.glfwSwapBuffers(window)
|
||||||
@ -1032,39 +1040,101 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun spin() {
|
||||||
|
try {
|
||||||
|
while (!shouldTerminate && renderFrame()) {
|
||||||
|
val ply = activeConnection?.character
|
||||||
|
|
||||||
|
if (ply != null) {
|
||||||
|
camera.pos = ply.position
|
||||||
|
|
||||||
|
ply.movement.controlMove = if (input.KEY_A_DOWN) Direction.LEFT else if (input.KEY_D_DOWN) Direction.RIGHT else null
|
||||||
|
ply.movement.controlJump = input.KEY_SPACE_DOWN
|
||||||
|
ply.movement.controlRun = !input.KEY_LEFT_SHIFT_DOWN
|
||||||
|
} else {
|
||||||
|
camera.pos += Vector2d(
|
||||||
|
(if (input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / settings.zoom else 0.0) + (if (input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / settings.zoom else 0.0),
|
||||||
|
(if (input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / settings.zoom else 0.0) + (if (input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / settings.zoom else 0.0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.KEY_ESCAPE_PRESSED) {
|
||||||
|
GLFW.glfwSetWindowShouldClose(window, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.fatal("Exception in client loop", err)
|
||||||
|
} finally {
|
||||||
|
executor.shutdown()
|
||||||
|
|
||||||
|
lock.lock()
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (window != MemoryUtil.NULL) {
|
||||||
|
Callbacks.glfwFreeCallbacks(window)
|
||||||
|
GLFW.glfwDestroyWindow(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--clients == 0) {
|
||||||
|
GLFW.glfwTerminate()
|
||||||
|
GLFW.glfwSetErrorCallback(null)?.free()
|
||||||
|
glfwInitialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldTerminate = true
|
||||||
|
|
||||||
|
for (callback in terminateCallbacks) {
|
||||||
|
callback.invoke()
|
||||||
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.fatal("Exception while destroying client", err)
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onTermination(lambda: () -> Unit) {
|
fun onTermination(lambda: () -> Unit) {
|
||||||
terminateCallbacks.add(lambda)
|
terminateCallbacks.add(lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
if (clientTerminated)
|
shouldTerminate = true
|
||||||
return
|
}
|
||||||
|
|
||||||
lock.lock()
|
init {
|
||||||
|
input.addScrollCallback { _, x, y ->
|
||||||
try {
|
if (y > 0.0) {
|
||||||
if (window != MemoryUtil.NULL) {
|
settings.zoom *= y.toFloat() * 2f
|
||||||
Callbacks.glfwFreeCallbacks(window)
|
} else if (y < 0.0) {
|
||||||
GLFW.glfwDestroyWindow(window)
|
settings.zoom /= -y.toFloat() * 2f
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--clients == 0) {
|
|
||||||
GLFW.glfwTerminate()
|
|
||||||
GLFW.glfwSetErrorCallback(null)?.free()
|
|
||||||
glfwInitialized = false
|
|
||||||
}
|
|
||||||
|
|
||||||
clientTerminated = true
|
|
||||||
|
|
||||||
for (callback in terminateCallbacks) {
|
|
||||||
callback.invoke()
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun create(): CompletableFuture<StarboundClient> {
|
||||||
|
val future = CompletableFuture<StarboundClient>()
|
||||||
|
val clientID = COUNTER.getAndIncrement()
|
||||||
|
|
||||||
|
val thread = Thread(Runnable {
|
||||||
|
val client = try {
|
||||||
|
StarboundClient(clientID)
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
future.completeExceptionally(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
future.complete(client)
|
||||||
|
client.spin()
|
||||||
|
}, "Starbound Client $clientID")
|
||||||
|
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
|
||||||
|
private val COUNTER = AtomicInteger()
|
||||||
private val LOGGER = LogManager.getLogger(StarboundClient::class.java)
|
private val LOGGER = LogManager.getLogger(StarboundClient::class.java)
|
||||||
private val CLIENTS = ThreadLocal<StarboundClient>()
|
private val CLIENTS = ThreadLocal<StarboundClient>()
|
||||||
private val WHITE = ByteBuffer.allocateDirect(4).also {
|
private val WHITE = ByteBuffer.allocateDirect(4).also {
|
||||||
|
@ -24,7 +24,8 @@ abstract class StarboundServer(val root: File) : Closeable {
|
|||||||
|
|
||||||
val worlds: MutableList<ServerWorld> = Collections.synchronizedList(ArrayList<ServerWorld>())
|
val worlds: MutableList<ServerWorld> = Collections.synchronizedList(ArrayList<ServerWorld>())
|
||||||
|
|
||||||
val thread = Thread(::runThread, "Starbound Server ${threadCounter.incrementAndGet()}")
|
val serverID = threadCounter.getAndIncrement()
|
||||||
|
val thread = Thread(::runThread, "Starbound Server $serverID")
|
||||||
val mailbox = MailboxExecutorService(thread)
|
val mailbox = MailboxExecutorService(thread)
|
||||||
|
|
||||||
val settings = ServerSettings()
|
val settings = ServerSettings()
|
||||||
@ -42,9 +43,12 @@ abstract class StarboundServer(val root: File) : Closeable {
|
|||||||
|
|
||||||
fun playerInGame(player: ServerConnection) {
|
fun playerInGame(player: ServerConnection) {
|
||||||
val world = worlds.first()
|
val world = worlds.first()
|
||||||
player.world = world
|
|
||||||
world.players.add(player)
|
world.mailbox.execute {
|
||||||
player.send(JoinWorldPacket(world))
|
player.world = world
|
||||||
|
world.players.add(player)
|
||||||
|
player.send(JoinWorldPacket(world))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun close0()
|
protected abstract fun close0()
|
||||||
|
@ -25,7 +25,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
|||||||
needsToRecomputeTrackedChunks = true
|
needsToRecomputeTrackedChunks = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var trackedPosition: Vector2d = Vector2d(238.0, 685.0)
|
var trackedPosition: Vector2d = Vector2d.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
field = value
|
field = value
|
||||||
|
Loading…
Reference in New Issue
Block a user