diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index ffd0597b..6ec8cc73 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -90,7 +90,7 @@ object Starbound : ISBFileLocator { const val NATIVE_PROTOCOL_VERSION = 748 const val LEGACY_PROTOCOL_VERSION = 747 const val TIMESTEP = 1.0 / 60.0 - const val TICK_TIME_ADVANCE_NANOS = (TIMESTEP * 1_000_000_000L).toLong() + const val TIMESTEP_NANOS = (TIMESTEP * 1_000_000_000L).toLong() // compile flags. uuuugh const val DEDUP_CELL_STATES = true diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index f122a7b1..867efcda 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -681,7 +681,7 @@ class StarboundClient private constructor(val clientID: Int) : Closeable { blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA } - val spinner = ExecutionSpinner(::executeQueuedTasks, ::renderFrame, Starbound.TICK_TIME_ADVANCE_NANOS) + val spinner = ExecutionSpinner(::executeQueuedTasks, ::renderFrame, Starbound.TIMESTEP_NANOS) val settings = ClientSettings() val viewportCells: ICellAccess = object : ICellAccess { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/actor/HumanoidData.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/actor/HumanoidData.kt index c07041bb..0fd12ab7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/actor/HumanoidData.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/actor/HumanoidData.kt @@ -70,9 +70,19 @@ data class HumanoidData( stream.writeBinaryString(facialMaskDirectives) stream.writeBinaryString(personalityIdle) stream.writeBinaryString(personalityArmIdle) - if (isLegacy) stream.writeStruct2f(personalityHeadOffset.toFloatVector()) else stream.writeStruct2d(personalityHeadOffset) - if (isLegacy) stream.writeStruct2f(personalityArmOffset.toFloatVector()) else stream.writeStruct2d(personalityArmOffset) - stream.writeColor(color) + + if (isLegacy) { + stream.writeStruct2f(personalityHeadOffset.toFloatVector()) + stream.writeStruct2f(personalityArmOffset.toFloatVector()) + stream.writeByte(color.redInt) + stream.writeByte(color.greenInt) + stream.writeByte(color.blueInt) + stream.writeByte(color.alphaInt) + } else { + stream.writeStruct2d(personalityHeadOffset) + stream.writeStruct2d(personalityArmOffset) + stream.writeColor(color) + } stream.writeBoolean(imagePath != null) @@ -101,13 +111,12 @@ data class HumanoidData( val facialMaskType = stream.readInternedString() val facialMaskDirectives = stream.readInternedString() - val color: RGBAColor = stream.readColor() - val personalityIdle: String = stream.readInternedString() val personalityArmIdle: String = stream.readInternedString() val personalityHeadOffset: Vector2d = if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d() val personalityArmOffset: Vector2d = if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d() + val color: RGBAColor = if (isLegacy) RGBAColor(stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte()) else stream.readColor() val imagePath: String? = if (stream.readBoolean()) stream.readInternedString() else null return HumanoidData( diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt index a84d631b..2290a045 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/item/ItemDescriptor.kt @@ -103,7 +103,7 @@ data class ItemDescriptor( constructor(ref: Registry.Ref, count: Long, parameters: JsonObject) : this(ref.key.left(), count, parameters) val isEmpty get() = count <= 0L || name == "" || ref.isEmpty - val ref by lazy { Registries.items.ref(name) } + val ref by lazy { if (name == "") Registries.items.emptyRef else Registries.items.ref(name) } override fun toString(): String { return "ItemDescriptor[$name, $count, $parameters]" diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/Streams.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/Streams.kt index fa88a155..8c558aeb 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/Streams.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/Streams.kt @@ -102,8 +102,8 @@ fun OutputStream.writeAABBLegacyOptional(value: KOptional) { }.ifNotPresent { writeFloat(Float.MAX_VALUE) writeFloat(Float.MAX_VALUE) - writeFloat(Float.MIN_VALUE) - writeFloat(Float.MIN_VALUE) + writeFloat(-Float.MAX_VALUE) + writeFloat(-Float.MAX_VALUE) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemStack.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemStack.kt index 4417a275..3e56bd00 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemStack.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/item/ItemStack.kt @@ -32,7 +32,7 @@ open class ItemStack { } constructor(descriptor: ItemDescriptor) { - this.config = Registries.items.ref(descriptor.name) + this.config = descriptor.ref this.count = descriptor.count this.parameters = descriptor.parameters.deepCopy() } @@ -211,7 +211,7 @@ open class ItemStack { val EMPTY = ItemStack() fun create(descriptor: ItemDescriptor): ItemStack { - return EMPTY + return ItemStack(descriptor) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt index 37642253..5e148250 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt @@ -236,6 +236,7 @@ class PacketRegistry(val isLegacy: Boolean) { networkReadBuffer.removeElements(0, reader.position().toInt()) } catch (err: EOFException) { // Ignore EOF, since it is caused by segmented nature of TCP + break } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityCreatePacket.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityCreatePacket.kt index fb829f89..b734fa79 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityCreatePacket.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityCreatePacket.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.network.packets +import it.unimi.dsi.fastutil.bytes.ByteArrayList import it.unimi.dsi.fastutil.io.FastByteArrayInputStream import org.apache.logging.log4j.LogManager import ru.dbotthepony.kommons.io.readByteArray @@ -16,18 +17,18 @@ import java.io.DataInputStream import java.io.DataOutputStream import java.io.File -class EntityCreatePacket(val entityType: EntityType, val storeData: ByteArray, val firstNetState: ByteArray, val entityID: Int) : IServerPacket, IClientPacket { +class EntityCreatePacket(val entityType: EntityType, val storeData: ByteArrayList, val firstNetState: ByteArrayList, val entityID: Int) : IServerPacket, IClientPacket { constructor(stream: DataInputStream, isLegacy: Boolean) : this( EntityType.entries[stream.readUnsignedByte()], - stream.readByteArray(), - stream.readByteArray(), + ByteArrayList.wrap(stream.readByteArray()), + ByteArrayList.wrap(stream.readByteArray()), stream.readSignedVarInt() ) override fun write(stream: DataOutputStream, isLegacy: Boolean) { stream.writeByte(entityType.ordinal) - stream.writeByteArray(storeData) - stream.writeByteArray(firstNetState) + stream.writeByteArray(storeData.elements(), 0, storeData.size) + stream.writeByteArray(firstNetState.elements(), 0, firstNetState.size) stream.writeSignedVarInt(entityID) } @@ -37,15 +38,25 @@ class EntityCreatePacket(val entityType: EntityType, val storeData: ByteArray, v } else { val entity = when (entityType) { EntityType.PLAYER -> { - val player = PlayerEntity(DataInputStream(FastByteArrayInputStream(storeData)), connection.isLegacy) - player.networkGroup.read(firstNetState, isLegacy = connection.isLegacy) - player + try { + val player = PlayerEntity(DataInputStream(FastByteArrayInputStream(storeData.elements(), 0, storeData.size)), connection.isLegacy) + player.networkGroup.read(firstNetState, isLegacy = connection.isLegacy) + + val (data) = player.networkGroup.write(isLegacy = true) + player.networkGroup.read(data, isLegacy = true) + player + } catch (err: Throwable) { + LOGGER.error("", err) + null + } } else -> null } entity?.entityID = entityID + entity?.isRemote = true + entity?.networkGroup?.upstream?.enableInterpolation(0.0) connection.enqueue { entity?.joinWorld(this) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityUpdateSetPacket.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityUpdateSetPacket.kt index 29ac8455..97852435 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityUpdateSetPacket.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/EntityUpdateSetPacket.kt @@ -10,6 +10,7 @@ import ru.dbotthepony.kommons.io.readVarInt import ru.dbotthepony.kommons.io.writeByteArray import ru.dbotthepony.kommons.io.writeSignedVarInt import ru.dbotthepony.kommons.io.writeVarInt +import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.client.ClientConnection import ru.dbotthepony.kstarbound.network.IClientPacket import ru.dbotthepony.kstarbound.network.IServerPacket @@ -17,14 +18,14 @@ import ru.dbotthepony.kstarbound.server.ServerConnection import java.io.DataInputStream import java.io.DataOutputStream -class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap) : IServerPacket, IClientPacket { +class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap) : IServerPacket, IClientPacket { override fun write(stream: DataOutputStream, isLegacy: Boolean) { stream.writeVarInt(forConnection) stream.writeVarInt(deltas.size) for ((k, v) in deltas.entries) { stream.writeSignedVarInt(k) - stream.writeByteArray(v) + stream.writeByteArray(v.elements(), 0, v.size) } } @@ -34,7 +35,7 @@ class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap() + val deltas = Int2ObjectAVLTreeMap() for (i in 0 until size) { val k = stream.readSignedVarInt() val v = stream.readByteArray() - deltas[k] = v + deltas[k] = ByteArrayList.wrap(v) } return EntityUpdateSetPacket(forConnection, deltas) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt index bbc5aead..8f41e4f1 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt @@ -145,6 +145,8 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va value = read valueListeners.accept(read) } + + bumpVersion() } override fun writeDelta(data: DataOutputStream, remoteVersion: Long, isLegacy: Boolean) { @@ -193,9 +195,10 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va } override fun tickInterpolation(delta: Double) { + require(delta >= 0.0) { "Negative interpolation delta: $delta" } currentTime += delta - if (isInterpolating) { + if (isInterpolating && queue.size >= 2) { while (queue.size > 2 && queue[1].first <= currentTime) { queue.removeFirst() } @@ -220,13 +223,23 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va val (time0, value0) = queue[0] val (time1, value1) = queue[1] - return interpolator.interpolate(((actualTime - time0) / (time1 - time0)).coerceAtLeast(-extrapolation), value0, value1) + var diff = ((actualTime - time0) / (time1 - time0)).coerceAtLeast(-extrapolation) + + if (diff.isNaN() || !diff.isFinite()) + diff = 0.0 + + return interpolator.interpolate(diff, value0, value1) } else if (actualTime > queue.last().first) { // extrapolate into future val (time0, value0) = queue[queue.size - 2] val (time1, value1) = queue[queue.size - 1] - return interpolator.interpolate(((actualTime - time1) / (time1 - time0)).coerceAtMost(extrapolation + 1.0), value0, value1) + var diff = ((actualTime - time1) / (time1 - time0)).coerceAtMost(extrapolation + 1.0) + + if (diff.isNaN() || !diff.isFinite()) + diff = 0.0 + + return interpolator.interpolate(diff, value0, value1) } else { // normal interpolation for (i in 0 until queue.size - 1) { @@ -234,7 +247,12 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va val (time1, value1) = queue[i + 1] if (actualTime in time0 .. time1) { - return interpolator.interpolate((actualTime - time0) / (time1 - time0), value0, value1) + var diff = (actualTime - time0) / (time1 - time0) + + if (diff.isNaN() || !diff.isFinite()) + diff = 0.0 + + return interpolator.interpolate(diff, value0, value1) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedMap.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedMap.kt index e19d32c9..f8767715 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedMap.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/NetworkedMap.kt @@ -93,6 +93,8 @@ class NetworkedMap( } fun write(data: DataOutputStream, isLegacy: Boolean, self: NetworkedMap) { + data.writeByte(action.ordinal) + if (isLegacy) { when (action) { Action.ADD -> { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt index 79a6f2b0..4b7b1290 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt @@ -3,6 +3,10 @@ package ru.dbotthepony.kstarbound.server import com.google.gson.JsonObject import io.netty.channel.ChannelHandlerContext import it.unimi.dsi.fastutil.bytes.ByteArrayList +import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap +import it.unimi.dsi.fastutil.ints.Int2ObjectMap +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps +import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet @@ -21,6 +25,8 @@ import ru.dbotthepony.kstarbound.network.ConnectionSide import ru.dbotthepony.kstarbound.network.ConnectionType import ru.dbotthepony.kstarbound.network.IServerPacket import ru.dbotthepony.kstarbound.network.packets.ClientContextUpdatePacket +import ru.dbotthepony.kstarbound.network.packets.EntityCreatePacket +import ru.dbotthepony.kstarbound.network.packets.EntityUpdateSetPacket import ru.dbotthepony.kstarbound.network.packets.clientbound.LegacyTileArrayUpdatePacket import ru.dbotthepony.kstarbound.network.packets.clientbound.PlayerWarpResultPacket import ru.dbotthepony.kstarbound.network.packets.clientbound.ServerDisconnectPacket @@ -32,6 +38,8 @@ import ru.dbotthepony.kstarbound.world.IChunkListener import ru.dbotthepony.kstarbound.world.api.ImmutableCell import ru.dbotthepony.kstarbound.world.entities.AbstractEntity import ru.dbotthepony.kstarbound.world.entities.WorldObject +import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity +import java.io.DataOutputStream import java.util.HashMap import java.util.concurrent.ConcurrentLinkedQueue import kotlin.properties.Delegates @@ -250,6 +258,12 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn } } + private val entityVersions = Int2LongOpenHashMap() + + init { + entityVersions.defaultReturnValue(-1L) + } + fun tickWorld() { val world = world @@ -284,6 +298,33 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn itr.remove() } + + for ((id, entity) in world.entities) { + if (entity.connectionID != connectionID && entity is PlayerEntity) { + if (entityVersions.get(id) == -1L) { + // never networked + val initial = FastByteArrayOutputStream() + entity.writeNetwork(DataOutputStream(initial), isLegacy) + val (data, version) = entity.networkGroup.write(isLegacy = isLegacy) + + entityVersions.put(id, version) + + send(EntityCreatePacket( + entity.type, + ByteArrayList.wrap(initial.array, initial.length), + data, + entity.entityID + )) + } else { + val (data, version) = entity.networkGroup.write(remoteVersion = entityVersions.get(id), isLegacy = isLegacy) + entityVersions.put(id, version) + + if (data.isNotEmpty()) { + send(EntityUpdateSetPacket(entity.connectionID, Int2ObjectMaps.singleton(entity.entityID, data))) + } + } + } + } } override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { @@ -316,9 +357,17 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn shipWorld = it shipWorld.thread.start() send(PlayerWarpResultPacket(true, WarpAlias.OwnShip, false)) - shipWorld.acceptPlayer(this).exceptionally { + shipWorld.acceptPlayer(this).thenAccept { + for (conn in server.channels.connections) { + if (conn.isLegacy && conn !== this) { + conn.shipWorld.acceptPlayer(this) + break + } + } + }.exceptionally { LOGGER.error("Shipworld of $this rejected to accept its owner", it) disconnect("Shipworld rejected player warp request: $it") + null } }.exceptionally { LOGGER.error("Error while initializing shipworld for $this", it) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt index 564c5a5a..04bb0fec 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt @@ -31,7 +31,7 @@ sealed class StarboundServer(val root: File) : Closeable { val serverID = threadCounter.getAndIncrement() val mailbox = MailboxExecutorService() - val spinner = ExecutionSpinner(mailbox::executeQueuedTasks, ::spin, Starbound.TICK_TIME_ADVANCE_NANOS) + val spinner = ExecutionSpinner(mailbox::executeQueuedTasks, ::spin, Starbound.TIMESTEP_NANOS) val thread = Thread(spinner, "Starbound Server $serverID") val universe = ServerUniverse() val chat = ChatHandler(this) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt index 718cd989..a8c5bd9a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt @@ -113,7 +113,7 @@ class ServerWorld private constructor( } } - val spinner = ExecutionSpinner(mailbox::executeQueuedTasks, ::spin, Starbound.TICK_TIME_ADVANCE_NANOS) + val spinner = ExecutionSpinner(mailbox::executeQueuedTasks, ::spin, Starbound.TIMESTEP_NANOS) val thread = Thread(spinner, "Starbound Server World Thread") val ticketListLock = ReentrantLock() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/ExecutionSpinner.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/ExecutionSpinner.kt index 40c3968e..0ad4e627 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/ExecutionSpinner.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/ExecutionSpinner.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.util import org.apache.logging.log4j.LogManager import org.lwjgl.system.MemoryStack -import ru.dbotthepony.kommons.util.MailboxExecutorService import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.WindowsBindings import java.util.concurrent.locks.LockSupport @@ -47,7 +46,7 @@ class ExecutionSpinner(private val waiter: Runnable, private val spinner: Boolea } private fun timeUntilNextFrame(): Long { - return Starbound.TICK_TIME_ADVANCE_NANOS - (System.nanoTime() - lastRender) - frameRenderTime + return Starbound.TIMESTEP_NANOS - (System.nanoTime() - lastRender) - frameRenderTime } private var carrier: Thread? = null diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 5080acfe..0319d31b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -269,7 +269,7 @@ abstract class World, ChunkType : Chunk) { } protected open fun onRemove(world: World<*, *>) { } - abstract fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean) - - abstract fun readDelta(stream: ByteArrayList, interpolationTime: Double = 0.0, isLegacy: Boolean) + val networkGroup = MasterElement(NetworkedGroup()) + abstract fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) fun joinWorld(world: World<*, *>) { if (innerWorld != null) @@ -124,8 +134,7 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) { innerWorld = null } - open val isRemote: Boolean - get() = innerWorld?.isRemote ?: false + var isRemote: Boolean = false fun think() { thinkShared() @@ -142,7 +151,7 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) { } protected open fun thinkRemote() { - + networkGroup.upstream.tickInterpolation(Starbound.TIMESTEP) } protected open fun thinkLocal() { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/StatusController.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/StatusController.kt index f3a551da..901171c7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/StatusController.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/StatusController.kt @@ -133,8 +133,8 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf val networkGroup = NetworkedGroup() var duration by networkedFixedPoint(0.01).also { networkGroup.add(it); it.interpolator = Interpolator.Linear } - var maxDuration by networkedFixedPoint(0.01).also { networkGroup.add(it) } - var sourceEntity by networkedData(KOptional(), KOptionalIntValueCodec) + var maxDuration by networkedFloat().also { networkGroup.add(it) } + var sourceEntity by networkedData(KOptional(), KOptionalIntValueCodec).also { networkGroup.add(it) } } // stats diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt index f7af0757..0b5fe520 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt @@ -21,6 +21,7 @@ import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation import ru.dbotthepony.kstarbound.server.world.ServerWorld import ru.dbotthepony.kommons.gson.get import ru.dbotthepony.kommons.gson.set +import ru.dbotthepony.kstarbound.defs.EntityType import ru.dbotthepony.kstarbound.world.Side import ru.dbotthepony.kstarbound.world.LightCalculator import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf @@ -46,11 +47,10 @@ open class WorldObject( } } - override fun readDelta(stream: ByteArrayList, interpolationTime: Double, isLegacy: Boolean) { - TODO("Not yet implemented") - } + override val type: EntityType + get() = EntityType.OBJECT - override fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean) { + override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) { TODO("Not yet implemented") } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerEntity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerEntity.kt index 87acc150..42eccbab 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerEntity.kt @@ -2,11 +2,13 @@ package ru.dbotthepony.kstarbound.world.entities.player import com.google.gson.JsonObject import it.unimi.dsi.fastutil.bytes.ByteArrayList +import ru.dbotthepony.kommons.io.writeBinaryString import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kommons.vector.Vector2d import ru.dbotthepony.kstarbound.GlobalDefaults import ru.dbotthepony.kstarbound.defs.EntityDamageTeam +import ru.dbotthepony.kstarbound.defs.EntityType import ru.dbotthepony.kstarbound.defs.actor.HumanoidData import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote import ru.dbotthepony.kstarbound.defs.actor.player.PlayerGamemode @@ -53,10 +55,16 @@ class PlayerEntity() : HumanoidActorEntity("/") { println(humanoidData) } + override val type: EntityType + get() = EntityType.PLAYER + var gamemode = PlayerGamemode.CASUAL - override fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean) { - TODO("Not yet implemented") + override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) { + stream.writeBinaryString(uniqueID!!) + stream.writeBinaryString("") + if (isLegacy) stream.writeInt(gamemode.ordinal) else stream.writeByte(gamemode.ordinal) + humanoidData.write(stream, isLegacy) } val inventory = PlayerInventory() @@ -65,12 +73,6 @@ class PlayerEntity() : HumanoidActorEntity("/") { override val statusController = StatusController(this, GlobalDefaults.player.statusControllerSettings) val techController = TechController(this) - val networkGroup = MasterElement(NetworkedGroup()) - - override fun readDelta(stream: ByteArrayList, interpolationTime: Double, isLegacy: Boolean) { - networkGroup.read(stream, interpolationTime, isLegacy) - } - var state by networkGroup.upstream.add(networkedEnum(State.IDLE)) var shifting by networkGroup.upstream.add(networkedBoolean()) private var xAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125)) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerInventory.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerInventory.kt index bbb7f37e..57e1e536 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerInventory.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/player/PlayerInventory.kt @@ -58,6 +58,8 @@ class PlayerInventory { } } + val networkGroup = NetworkedGroup() + // here it gets interesting, original code is using List#sorted, which itself uses Star::sort, // which is just an alias for std::sort, and std::sort is ***not*** stable sort, meaning // if bags have same priority, PlayerInventory behavior becomes undefined @@ -69,8 +71,6 @@ class PlayerInventory { .map { it.key to Bag(it.value.size) } .collect(ImmutableMap.toImmutableMap({ it.first }, { it.second })) - val networkGroup = NetworkedGroup() - val equipment: ImmutableMap = EquipmentSlot.entries .stream() .map { it to networkedItem() } diff --git a/src/test/kotlin/ru/dbotthepony/kstarbound/test/NetworkedElementTests.kt b/src/test/kotlin/ru/dbotthepony/kstarbound/test/NetworkedElementTests.kt index 2a67d8e9..800f3e67 100644 --- a/src/test/kotlin/ru/dbotthepony/kstarbound/test/NetworkedElementTests.kt +++ b/src/test/kotlin/ru/dbotthepony/kstarbound/test/NetworkedElementTests.kt @@ -108,7 +108,7 @@ object NetworkedElementTests { val result = master.write().first - slave.read(FastByteArrayInputStream(result.array, 0, result.length)) + slave.read(result) assertEquals(567, slaveField1.get()) assertEquals(17000, slaveField2.get())