More work towards implementing legacy protocol, new entity class tree
This commit is contained in:
parent
afc45aac92
commit
8149fcb48d
@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
|
||||
|
||||
kotlinVersion=1.9.10
|
||||
kotlinCoroutinesVersion=1.8.0
|
||||
kommonsVersion=2.9.23
|
||||
kommonsVersion=2.9.25
|
||||
|
||||
ffiVersion=2.2.13
|
||||
lwjglVersion=3.3.0
|
||||
|
@ -14,14 +14,11 @@ import ru.dbotthepony.kstarbound.server.world.LegacyWorldStorage
|
||||
import ru.dbotthepony.kstarbound.server.world.ServerUniverse
|
||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.Inflater
|
||||
import java.util.zip.InflaterInputStream
|
||||
|
||||
@ -116,19 +113,6 @@ fun main() {
|
||||
|
||||
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
|
||||
|
||||
val rand = Random()
|
||||
|
||||
for (i in 0 until 0) {
|
||||
val item = ItemEntity(Registries.items.keys.values.random().value)
|
||||
|
||||
item.position = Vector2d(225.0 - i, 785.0)
|
||||
item.joinWorld(world)
|
||||
item.movement.velocity = Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0)
|
||||
|
||||
item.mailbox.scheduleAtFixedRate({ item.movement.velocity += Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0) }, 1000 + rand.nextLong(-100, 100), 1000 + rand.nextLong(-100, 100), TimeUnit.MILLISECONDS)
|
||||
//item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0))
|
||||
}
|
||||
|
||||
client.connectToLocalServer(server.channels.createLocalChannel())
|
||||
//client.connectToRemoteServer(InetSocketAddress("127.0.0.1", 21025))
|
||||
//client2.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
|
||||
|
@ -233,7 +233,7 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapterFactory(ThingDescription.Factory(STRINGS))
|
||||
registerTypeAdapterFactory(TerrainSelectorType.Companion)
|
||||
|
||||
registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.NORMAL))
|
||||
registerTypeAdapter(EnumAdapter(DamageType::class, default = DamageType.DAMAGE))
|
||||
|
||||
registerTypeAdapter(InventoryIcon.Companion)
|
||||
|
||||
|
@ -19,7 +19,7 @@ class ForgetEntityPacket(val uuid: UUID) : IClientPacket {
|
||||
val world = connection.client.world ?: return
|
||||
|
||||
world.mailbox.execute {
|
||||
world.entities.firstOrNull { it.uuid == uuid }?.remove()
|
||||
world.entities.firstOrNull { it.entityID == 0 }?.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class SpawnWorldObjectPacket(val uuid: UUID, val data: JsonObject) : IClientPack
|
||||
connection.client.mailbox.execute {
|
||||
val world = connection.client.world ?: return@execute
|
||||
val obj = WorldObject.fromJson(data)
|
||||
obj.uuid = uuid
|
||||
//obj.uuid = uuid
|
||||
obj.joinWorld(world)
|
||||
}
|
||||
}
|
||||
|
74
src/main/kotlin/ru/dbotthepony/kstarbound/defs/Damage.kt
Normal file
74
src/main/kotlin/ru/dbotthepony/kstarbound/defs/Damage.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
enum class TeamType(override val jsonName: String) : IStringSerializable {
|
||||
NULL("null"),
|
||||
// non-PvP-enabled players and player allied NPCs
|
||||
FRIENDLY("friendly"),
|
||||
// hostile and neutral NPCs and monsters
|
||||
ENEMY("enemy"),
|
||||
// PvP-enabled players
|
||||
PVP("pvp"),
|
||||
// cannot damage anything, can be damaged by Friendly/PVP/Assistant
|
||||
PASSIVE("passive"),
|
||||
// cannot damage or be damaged
|
||||
GHOSTLY("ghostly"),
|
||||
// cannot damage enemies, can be damaged by anything except enemy
|
||||
ENVIRONMENT("environment"),
|
||||
// damages anything except ghostly, damaged by anything except ghostly/passive
|
||||
// used for self damage
|
||||
INDISCRIMINATE("indiscriminate"),
|
||||
// cannot damage friendlies and cannot be damaged by anything
|
||||
ASSISTANT("assistant");
|
||||
}
|
||||
|
||||
enum class HitType(override val jsonName: String) : IStringSerializable {
|
||||
HIT("Hit"),
|
||||
STRONG_HIT("StrongHit"),
|
||||
WEAK_HIT("WeakHit"),
|
||||
SHIELD_HIT("ShieldHit"),
|
||||
KILL("Kill");
|
||||
}
|
||||
|
||||
enum class DamageType(override val jsonName: String) : IStringSerializable {
|
||||
NO_DAMAGE("NoDamage"),
|
||||
DAMAGE("Damage"),
|
||||
IGNORE_DEFENCE("IgnoresDef"),
|
||||
KNOCKBACK("IgnoresDef"),
|
||||
ENVIRONMENT("Environment"),
|
||||
STATUS("Environment");
|
||||
}
|
||||
|
||||
@JsonFactory
|
||||
data class EntityDamageTeam(val type: TeamType = TeamType.NULL, val team: Int = 0) {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(TeamType.entries[stream.readUnsignedByte()], stream.readUnsignedShort())
|
||||
|
||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.writeByte(type.ordinal)
|
||||
stream.writeShort(team)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC = nativeCodec(::EntityDamageTeam, EntityDamageTeam::write)
|
||||
val LEGACY_CODEC = legacyCodec(::EntityDamageTeam, EntityDamageTeam::write)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonFactory
|
||||
data class TouchDamage(
|
||||
val poly: ImmutableList<Vector2d> = ImmutableList.of(),
|
||||
val teamType: TeamType = TeamType.ENVIRONMENT,
|
||||
val damage: Double = 0.0,
|
||||
val damageSourceKind: String = "",
|
||||
val knockback: Double = 0.0,
|
||||
val statusEffects: ImmutableSet<String> = ImmutableSet.of(),
|
||||
)
|
@ -1,23 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
|
||||
enum class DamageType(private vararg val aliases: String) : IStringSerializable {
|
||||
NORMAL,
|
||||
IGNORE_DEFENCE("IGNORESDEF", "IGNOREDEF"),
|
||||
STATUS,
|
||||
NO_DAMAGE("NODAMAGE");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
for (alias in aliases)
|
||||
if (name == alias)
|
||||
return true
|
||||
|
||||
return name == this.name
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(this.name)
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package ru.dbotthepony.kstarbound.defs
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
|
||||
enum class EntityType(val jsonName: String) : IStringSerializable {
|
||||
enum class EntityType(override val jsonName: String) : IStringSerializable {
|
||||
PLANT("PlantEntity"),
|
||||
OBJECT("ObjectEntity"),
|
||||
VEHICLE("VehicleEntity"),
|
||||
@ -14,12 +14,4 @@ enum class EntityType(val jsonName: String) : IStringSerializable {
|
||||
MONSTER("MonsterEntity"),
|
||||
NPC("NpcEntity"),
|
||||
PLAYER("PlayerEntity");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.name(jsonName)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ data class PerlinNoiseParameters(
|
||||
require(scale >= 16) { "Too little perlin noise scale" }
|
||||
}
|
||||
|
||||
enum class Type(val jsonName: String) : IStringSerializable {
|
||||
enum class Type(override val jsonName: String) : IStringSerializable {
|
||||
PERLIN("perlin"),
|
||||
BILLOW("billow"),
|
||||
RIDGED_MULTI("ridgedmulti");
|
||||
@ -30,10 +30,6 @@ data class PerlinNoiseParameters(
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -35,7 +35,7 @@ sealed class AbstractWarpTarget {
|
||||
sealed class WarpAlias(val index: Int) : AbstractWarpTarget() {
|
||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.write(3)
|
||||
// because it is defined as enum class WarpAlias, which defaults to int32_t for some reason.
|
||||
// because it is defined as enum class WarpAlias, without specifying uint8_t as type
|
||||
stream.writeInt(index)
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
enum class TeamType {
|
||||
NULL,
|
||||
// non-PvP-enabled players and player allied NPCs
|
||||
FRIENDLY,
|
||||
// hostile and neutral NPCs and monsters
|
||||
ENEMY,
|
||||
// PvP-enabled players
|
||||
PVP,
|
||||
// cannot damage anything, can be damaged by Friendly/PVP/Assistant
|
||||
PASSIVE,
|
||||
// cannot damage or be damaged
|
||||
GHOSTLY,
|
||||
// cannot damage enemies, can be damaged by anything except enemy
|
||||
ENVIRONMENT,
|
||||
// damages anything except ghostly, damaged by anything except ghostly/passive
|
||||
// used for self damage
|
||||
INDISCRIMINATE,
|
||||
// cannot damage friendlies and cannot be damaged by anything
|
||||
ASSISTANT
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
|
||||
@JsonFactory
|
||||
data class TouchDamage(
|
||||
val poly: ImmutableList<Vector2d> = ImmutableList.of(),
|
||||
val teamType: TeamType = TeamType.ENVIRONMENT,
|
||||
val damage: Double = 0.0,
|
||||
val damageSourceKind: String = "",
|
||||
val knockback: Double = 0.0,
|
||||
val statusEffects: ImmutableSet<String> = ImmutableSet.of(),
|
||||
)
|
@ -0,0 +1,120 @@
|
||||
package ru.dbotthepony.kstarbound.defs.actor
|
||||
|
||||
import ru.dbotthepony.kommons.io.readVector2d
|
||||
import ru.dbotthepony.kommons.io.readVector2f
|
||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
||||
import ru.dbotthepony.kommons.io.writeStruct2f
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.io.readColor
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.io.writeColor
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
@JsonFactory
|
||||
data class HumanoidData(
|
||||
val name: String = "Humanoid",
|
||||
val species: String = "human",
|
||||
val gender: Gender = Gender.MALE,
|
||||
|
||||
val hairGroup: String = "hair",
|
||||
// Must have :normal and :climb
|
||||
val hairType: String = "male1",
|
||||
val hairDirectives: String = "",
|
||||
val bodyDirectives: String = "",
|
||||
val emoteDirectives: String = bodyDirectives,
|
||||
val facialHairGroup: String = "",
|
||||
val facialHairType: String = "",
|
||||
val facialHairDirectives: String = "",
|
||||
val facialMaskGroup: String = "",
|
||||
val facialMaskType: String = "",
|
||||
val facialMaskDirectives: String = "",
|
||||
|
||||
val color: RGBAColor = RGBAColor.BLACK,
|
||||
|
||||
val personalityIdle: String = "",
|
||||
val personalityArmIdle: String = "",
|
||||
val personalityHeadOffset: Vector2d = Vector2d.ZERO,
|
||||
val personalityArmOffset: Vector2d = Vector2d.ZERO,
|
||||
|
||||
val imagePath: String? = null,
|
||||
) : IPersonality {
|
||||
override val idle: String
|
||||
get() = personalityIdle
|
||||
override val armIdle: String
|
||||
get() = personalityArmIdle
|
||||
override val handOffset: Vector2d
|
||||
get() = personalityHeadOffset
|
||||
override val armOffset: Vector2d
|
||||
get() = personalityArmOffset
|
||||
|
||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
stream.writeBinaryString(name)
|
||||
stream.writeBinaryString(species)
|
||||
stream.writeByte(gender.ordinal)
|
||||
stream.writeBinaryString(hairGroup)
|
||||
stream.writeBinaryString(hairType)
|
||||
stream.writeBinaryString(hairDirectives)
|
||||
stream.writeBinaryString(bodyDirectives)
|
||||
stream.writeBinaryString(emoteDirectives)
|
||||
stream.writeBinaryString(facialHairGroup)
|
||||
stream.writeBinaryString(facialHairType)
|
||||
stream.writeBinaryString(facialHairDirectives)
|
||||
stream.writeBinaryString(facialMaskGroup)
|
||||
stream.writeBinaryString(facialMaskType)
|
||||
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)
|
||||
|
||||
stream.writeBoolean(imagePath != null)
|
||||
|
||||
if (imagePath != null)
|
||||
stream.writeBinaryString(imagePath)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC = nativeCodec(::read, HumanoidData::write)
|
||||
val LEGACY_CODEC = legacyCodec(::read, HumanoidData::write)
|
||||
|
||||
fun read(stream: DataInputStream, isLegacy: Boolean): HumanoidData {
|
||||
val name: String = stream.readInternedString()
|
||||
val species: String = stream.readInternedString()
|
||||
val gender: Gender = Gender.entries[stream.readUnsignedByte()]
|
||||
|
||||
val hairGroup = stream.readInternedString()
|
||||
val hairType = stream.readInternedString()
|
||||
val hairDirectives = stream.readInternedString()
|
||||
val bodyDirectives = stream.readInternedString()
|
||||
val emoteDirectives = stream.readInternedString()
|
||||
val facialHairGroup = stream.readInternedString()
|
||||
val facialHairType = stream.readInternedString()
|
||||
val facialHairDirectives = stream.readInternedString()
|
||||
val facialMaskGroup = stream.readInternedString()
|
||||
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 imagePath: String? = if (stream.readBoolean()) stream.readInternedString() else null
|
||||
|
||||
return HumanoidData(
|
||||
name, species, gender, hairGroup, hairType, hairDirectives, bodyDirectives,
|
||||
emoteDirectives, facialHairGroup, facialHairType, facialHairDirectives,
|
||||
facialMaskGroup, facialMaskType, facialMaskDirectives, color, personalityIdle,
|
||||
personalityArmIdle, personalityHeadOffset, personalityArmOffset, imagePath)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.dbotthepony.kstarbound.defs.actor
|
||||
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
|
||||
// this is because personality data structure is used inconsistently across original source code
|
||||
interface IPersonality {
|
||||
val idle: String
|
||||
val armIdle: String
|
||||
val handOffset: Vector2d
|
||||
val armOffset: Vector2d
|
||||
}
|
||||
|
||||
@JsonFactory(asList = true)
|
||||
data class Personality(
|
||||
override val idle: String,
|
||||
override val armIdle: String,
|
||||
override val handOffset: Vector2d,
|
||||
override val armOffset: Vector2d
|
||||
) : IPersonality
|
@ -0,0 +1,24 @@
|
||||
package ru.dbotthepony.kstarbound.defs.actor
|
||||
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
|
||||
enum class Gender(override val jsonName: String) : IStringSerializable {
|
||||
MALE("Male"), FEMALE("Female");
|
||||
}
|
||||
|
||||
enum class HumanoidEmote(override val jsonName: String) : IStringSerializable {
|
||||
IDLE("Idle"),
|
||||
BLABBERING("Blabbering"),
|
||||
SHOUTING("Shouting"),
|
||||
HAPPY("Happy"),
|
||||
SAD("Sad"),
|
||||
NEUTRAL("NEUTRAL"),
|
||||
LAUGH("Laugh"),
|
||||
ANNOYED("Annoyed"),
|
||||
OH("Oh"),
|
||||
OOOH("OOOH"),
|
||||
BLINK("Blink"),
|
||||
WINK("Wink"),
|
||||
EAT("Eat"),
|
||||
SLEEP("Sleep");
|
||||
}
|
@ -3,7 +3,7 @@ package ru.dbotthepony.kstarbound.defs.item
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
|
||||
enum class ItemRarity(val canonical: String) : IStringSerializable {
|
||||
enum class ItemRarity(override val jsonName: String) : IStringSerializable {
|
||||
COMMON("Common"),
|
||||
UNCOMMON("Uncommon"),
|
||||
RARE("Rare"),
|
||||
@ -11,10 +11,6 @@ enum class ItemRarity(val canonical: String) : IStringSerializable {
|
||||
ESSENTIAL("Essential");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name == this.canonical || name.lowercase() == this.name.lowercase()
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(canonical)
|
||||
return name == this.jsonName || name.lowercase() == this.name.lowercase()
|
||||
}
|
||||
}
|
||||
|
@ -47,19 +47,11 @@ enum class WarpPhase(val stupidassbitch: Int) {
|
||||
SPEEDING_UP(1)
|
||||
}
|
||||
|
||||
enum class SkyOrbiterType(val sname: String) : IStringSerializable {
|
||||
enum class SkyOrbiterType(override val jsonName: String) : IStringSerializable {
|
||||
SUN("sun"),
|
||||
MOON("moon"),
|
||||
HORIZON_CLOUD("horizoncloud"),
|
||||
SPACE_DEBRIS("scapedebris");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == sname
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(sname)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonFactory
|
||||
|
@ -20,49 +20,25 @@ import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
enum class BeamUpRule(val jsonName: String) : IStringSerializable {
|
||||
NOWHERE("nowhere"),
|
||||
SURFACE("surface"),
|
||||
ANYWHERE("anywhere"),
|
||||
ANYWHERE_WITH_WARNING("anywherewithwarning");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
}
|
||||
enum class BeamUpRule(override val jsonName: String) : IStringSerializable {
|
||||
NOWHERE("Nowhere"),
|
||||
SURFACE("Surface"),
|
||||
ANYWHERE("Anywhere"),
|
||||
ANYWHERE_WITH_WARNING("AnywhereWithWarning");
|
||||
}
|
||||
|
||||
enum class WorldEdgeForceRegion(val sname: String) : IStringSerializable {
|
||||
NONE("none"),
|
||||
TOP("top"),
|
||||
BOTTOM("bottom"),
|
||||
TOP_AND_BOTTOM("topandbottom");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == sname
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(sname)
|
||||
}
|
||||
enum class WorldEdgeForceRegion(override val jsonName: String) : IStringSerializable {
|
||||
NONE("None"),
|
||||
TOP("Top"),
|
||||
BOTTOM("Bottom"),
|
||||
TOP_AND_BOTTOM("TopAndBottom");
|
||||
}
|
||||
|
||||
enum class VisitableWorldParametersType(val jsonName: String, val token: TypeToken<out VisitableWorldParameters>) : IStringSerializable {
|
||||
enum class VisitableWorldParametersType(override val jsonName: String, val token: TypeToken<out VisitableWorldParameters>) : IStringSerializable {
|
||||
TERRESTRIAL("TerrestrialWorldParameters", TypeToken.get(TerrestrialWorldParameters::class.java)),
|
||||
ASTEROIDS("AsteroidsWorldParameters", TypeToken.get(AsteroidsWorldParameters::class.java)),
|
||||
FLOATING_DUNGEON("FloatingDungeonWorldParameters", TypeToken.get(FloatingDungeonWorldParameters::class.java));
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
val ADAPTER = DispatchingAdapter("type", { type }, { token }, entries)
|
||||
|
||||
|
@ -9,7 +9,7 @@ import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.Direction1D
|
||||
|
||||
@JsonFactory
|
||||
data class WorldStructure(
|
||||
@ -33,7 +33,7 @@ data class WorldStructure(
|
||||
data class Obj(
|
||||
val position: Vector2i,
|
||||
val name: String,
|
||||
val direction: Direction,
|
||||
val direction: Direction1D,
|
||||
val parameters: JsonElement,
|
||||
val residual: Boolean = false,
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ import java.util.random.RandomGenerator
|
||||
import java.util.stream.IntStream
|
||||
|
||||
enum class BiomePlacementDistributionType(
|
||||
val jsonName: String,
|
||||
override val jsonName: String,
|
||||
val def: TypeToken<out BiomePlaceablesDefinition.DistributionData>,
|
||||
val data: TypeToken<out BiomePlaceables.DistributionData>,
|
||||
) : IStringSerializable {
|
||||
@ -47,10 +47,6 @@ enum class BiomePlacementDistributionType(
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFINITION_ADAPTER = DispatchingAdapter("type", { type }, { def }, entries)
|
||||
val DATA_ADAPTER = DispatchingAdapter("type", { type }, { data }, entries)
|
||||
@ -58,7 +54,7 @@ enum class BiomePlacementDistributionType(
|
||||
}
|
||||
|
||||
enum class BiomePlacementItemType(
|
||||
val jsonName: String,
|
||||
override val jsonName: String,
|
||||
val def: TypeToken<out BiomePlaceablesDefinition.DistributionItemData>,
|
||||
val data: TypeToken<out BiomePlaceables.Item>,
|
||||
) : IStringSerializable {
|
||||
@ -74,10 +70,6 @@ enum class BiomePlacementItemType(
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFINITION_ADAPTER = DispatchingAdapter("type", { type }, { def }, entries)
|
||||
val DATA_ADAPTER = DispatchingAdapter("type", { type }, { data }, entries)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.io
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||
import ru.dbotthepony.kommons.io.readBinaryString
|
||||
import ru.dbotthepony.kommons.util.IStruct2d
|
||||
import ru.dbotthepony.kommons.util.IStruct2i
|
||||
import ru.dbotthepony.kommons.io.readDouble
|
||||
@ -14,6 +15,7 @@ import ru.dbotthepony.kommons.io.writeSignedVarInt
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
@ -54,3 +56,5 @@ fun OutputStream.writeColor(color: RGBAColor) {
|
||||
fun InputStream.readColor(): RGBAColor {
|
||||
return RGBAColor(readFloat(), readFloat(), readFloat(), readFloat())
|
||||
}
|
||||
|
||||
fun InputStream.readInternedString(): String = Starbound.STRINGS.intern(readBinaryString())
|
||||
|
@ -22,8 +22,11 @@ import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSuperclassOf
|
||||
|
||||
interface IStringSerializable {
|
||||
fun match(name: String): Boolean
|
||||
fun write(out: JsonWriter)
|
||||
val jsonName: String
|
||||
|
||||
fun match(name: String): Boolean {
|
||||
return name == jsonName
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> EnumAdapter(values: Stream<T> = Arrays.stream(T::class.java.enumConstants), default: T? = null): EnumAdapter<T> {
|
||||
@ -85,7 +88,7 @@ class EnumAdapter<T : Enum<T>>(private val enum: KClass<T>, values: Stream<T> =
|
||||
if (value == null) {
|
||||
out.nullValue()
|
||||
} else if (value is IStringSerializable) {
|
||||
value.write(out)
|
||||
out.value(value.jsonName)
|
||||
} else {
|
||||
out.value(value.name)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ object RGBAColorTypeAdapter : TypeAdapter<RGBAColor>() {
|
||||
out.value(value.redInt)
|
||||
out.value(value.greenInt)
|
||||
out.value(value.blueInt)
|
||||
if (value.alphaInt != 255) out.value(value.alphaInt)
|
||||
out.value(value.alphaInt)
|
||||
|
||||
out.endArray()
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import ru.dbotthepony.kommons.io.readKOptional
|
||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeKOptional
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import java.io.DataInputStream
|
||||
@ -48,7 +49,7 @@ class JsonRPC {
|
||||
|
||||
companion object {
|
||||
fun native(stream: DataInputStream): Entry {
|
||||
return Entry(Command.entries[stream.read()], stream.readInt(), stream.readKOptional { readBinaryString() }, stream.readKOptional { readJsonElement() })
|
||||
return Entry(Command.entries[stream.read()], stream.readInt(), stream.readKOptional { Starbound.STRINGS.intern(readBinaryString()) }, stream.readKOptional { readJsonElement() })
|
||||
}
|
||||
|
||||
fun legacy(stream: DataInputStream): Entry {
|
||||
|
@ -28,6 +28,7 @@ object PingPacket : IServerPacket {
|
||||
}
|
||||
|
||||
override fun play(connection: ServerConnection) {
|
||||
// immediately respond to ping packets
|
||||
connection.sendAndFlush(PongPacket)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.network.syncher
|
||||
|
||||
import ru.dbotthepony.kommons.io.UnsignedVarLongCodec
|
||||
import java.io.DataInputStream
|
||||
import java.util.function.Consumer
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
package ru.dbotthepony.kstarbound.network.syncher
|
||||
|
||||
import com.google.gson.TypeAdapter
|
||||
import ru.dbotthepony.kommons.io.BinaryStringCodec
|
||||
import ru.dbotthepony.kommons.io.BooleanValueCodec
|
||||
import ru.dbotthepony.kommons.io.StreamCodec
|
||||
import ru.dbotthepony.kommons.io.UnsignedVarIntCodec
|
||||
import ru.dbotthepony.kommons.io.UnsignedVarLongCodec
|
||||
import ru.dbotthepony.kommons.io.VarIntValueCodec
|
||||
import ru.dbotthepony.kommons.io.VarLongValueCodec
|
||||
import ru.dbotthepony.kommons.io.Vector2dCodec
|
||||
@ -16,22 +19,49 @@ import ru.dbotthepony.kommons.io.writeVarLong
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.world.SkyType
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
fun <TYPE> BasicNetworkedElement(value: TYPE, codec: StreamCodec<TYPE>): BasicNetworkedElement<TYPE, TYPE> {
|
||||
return BasicNetworkedElement(value, codec, codec, { it }, { it })
|
||||
}
|
||||
|
||||
val UnsignedVarLongCodec = StreamCodec.Impl(DataInputStream::readVarLong, DataOutputStream::writeVarLong)
|
||||
val UnsignedVarIntCodec = StreamCodec.Impl(DataInputStream::readVarInt, DataOutputStream::writeVarInt)
|
||||
|
||||
val ByteArrayCodec = StreamCodec.Impl(DataInputStream::readByteArray, DataOutputStream::writeByteArray)
|
||||
|
||||
// networking size_t...
|
||||
// god help us all
|
||||
val SizeTCodec = StreamCodec.Impl({ stream -> stream.readVarLong().let { if (it == 0L) -1L else it - 1L } }, { stream, value -> if (value in 0L..<Long.MAX_VALUE) stream.writeVarLong(value + 1L) else stream.writeVarLong(0L) })
|
||||
|
||||
fun InputStream.readPointer(): Long {
|
||||
val read = readVarLong()
|
||||
|
||||
if (read == 0L) {
|
||||
return -1L
|
||||
} else {
|
||||
return read - 1L
|
||||
}
|
||||
}
|
||||
|
||||
fun OutputStream.writePointer(value: Int) {
|
||||
if (value < 0L) {
|
||||
writeVarLong(0L)
|
||||
} else {
|
||||
writeVarLong(value + 1L)
|
||||
}
|
||||
}
|
||||
|
||||
fun OutputStream.writePointer(value: Long) {
|
||||
if (value in 0L..<Long.MAX_VALUE) {
|
||||
writeVarLong(value + 1L)
|
||||
} else {
|
||||
writeVarLong(0L)
|
||||
}
|
||||
}
|
||||
|
||||
fun networkedFloat(value: Double = 0.0) = FloatingNetworkedElement.float(value)
|
||||
fun networkedDouble(value: Double = 0.0) = FloatingNetworkedElement.double(value)
|
||||
fun networkedFixedPoint(base: Double, value: Double = 0.0) = FloatingNetworkedElement.fixed(base, value)
|
||||
@ -45,6 +75,14 @@ fun networkedVec2f(value: Vector2d = Vector2d.ZERO) = BasicNetworkedElement(valu
|
||||
fun networkedBytes(value: ByteArray = ByteArray(0)) = BasicNetworkedElement(value, ByteArrayCodec)
|
||||
fun <E : Enum<E>> networkedEnum(value: E) = BasicNetworkedElement(value, StreamCodec.Enum(value::class.java))
|
||||
|
||||
fun <T> networkedData(value: T, codec: StreamCodec<T>) = BasicNetworkedElement(value, codec)
|
||||
fun <T> networkedData(value: T, codec: StreamCodec<T>, legacyCodec: StreamCodec<T>) = BasicNetworkedElement(value, codec, legacyCodec, { it }, { it })
|
||||
|
||||
fun networkedEventCounter() = EventCounterElement()
|
||||
fun networkedString(value: String = "") = BasicNetworkedElement(value, BinaryStringCodec)
|
||||
|
||||
fun networkedPoly(value: Poly) = networkedData(value, Poly.CODEC, Poly.LEGACY_CODEC)
|
||||
|
||||
inline fun <reified T> networkedJson(value: T, adapter: TypeAdapter<T> = Starbound.gson.getAdapter(T::class.java), legacyIsArray: Boolean = true): BasicNetworkedElement<T, *> {
|
||||
if (legacyIsArray) {
|
||||
return BasicNetworkedElement(value, JsonCodec(adapter), JsonCodec(adapter, true), { it }, { it })
|
||||
@ -53,12 +91,22 @@ inline fun <reified T> networkedJson(value: T, adapter: TypeAdapter<T> = Starbou
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* properly networks an enum on native protocol;
|
||||
* networks a signed variable length integer on legacy protocol.
|
||||
* this is way too dumb beyond my comprehension
|
||||
*/
|
||||
fun <T> nativeCodec(reader: DataInputStream.(Boolean) -> T, writer: T.(DataOutputStream, Boolean) -> Unit): StreamCodec<T> {
|
||||
return StreamCodec.Impl({ reader(it, false) }, { a, b -> writer(b, a, false) })
|
||||
}
|
||||
|
||||
fun <T> legacyCodec(reader: DataInputStream.(Boolean) -> T, writer: T.(DataOutputStream, Boolean) -> Unit): StreamCodec<T> {
|
||||
return StreamCodec.Impl({ reader(it, true) }, { a, b -> writer(b, a, true) })
|
||||
}
|
||||
|
||||
// networks a signed variable length integer on legacy protocol
|
||||
fun <E : Enum<E>> networkedEnumStupid(value: E): BasicNetworkedElement<E, Int> {
|
||||
val codec = StreamCodec.Enum(value::class.java)
|
||||
return BasicNetworkedElement(value, codec, VarIntValueCodec, { it.ordinal.shl(1) }, { codec.values[it.ushr(1)] })
|
||||
}
|
||||
|
||||
// networks enum as string on legacy protocol
|
||||
fun <E : Enum<E>> networkedEnumExtraStupid(value: E): BasicNetworkedElement<E, String> {
|
||||
val codec = StreamCodec.Enum(value::class.java)
|
||||
return BasicNetworkedElement(value, codec, BinaryStringCodec, { if (it is IStringSerializable) it.jsonName else it.name }, { s -> codec.values.firstOrNull { if (it is IStringSerializable) it.match(s) else it.name == s } ?: throw NoSuchElementException(s) })
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import java.util.function.DoubleSupplier
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
// works solely with doubles, but networks as either float, double or fixed point
|
||||
class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, val legacyOps: Ops = ops, val interpolator: Interpolator = Interpolator.NearestMiddle) : NetworkedElement(), ListenableDelegate<Double>, DoubleSupplier {
|
||||
class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, val legacyOps: Ops = ops, var interpolator: Interpolator = Interpolator.NearestMiddle) : NetworkedElement(), ListenableDelegate<Double>, DoubleSupplier {
|
||||
interface Ops {
|
||||
fun write(data: DataOutputStream, value: Double)
|
||||
fun read(data: DataInputStream): Double
|
||||
@ -251,7 +251,7 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va
|
||||
}
|
||||
|
||||
fun fixed(base: Double, value: Double = 0.0): FloatingNetworkedElement {
|
||||
return FloatingNetworkedElement(value, FixedPointOps(base))
|
||||
return FloatingNetworkedElement(value, DoubleOps, FixedPointOps(base))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class GroupElement() : NetworkedElement() {
|
||||
elements.forEach { it.first.tickInterpolation(delta) }
|
||||
}
|
||||
|
||||
fun add(element: NetworkedElement, propagateInterpolation: Boolean = true): GroupElement {
|
||||
fun <E : NetworkedElement> add(element: E, propagateInterpolation: Boolean = true): E {
|
||||
require(elements.none { it.first == element }) { "Already has element $element in $this" }
|
||||
elements.add(element to propagateInterpolation)
|
||||
|
||||
@ -62,7 +62,7 @@ class GroupElement() : NetworkedElement() {
|
||||
})
|
||||
|
||||
this.version = this.version.coerceAtLeast(element.version)
|
||||
return this
|
||||
return element
|
||||
}
|
||||
|
||||
override fun readInitial(data: DataInputStream, isLegacy: Boolean) {
|
||||
|
@ -121,12 +121,12 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
||||
|
||||
private inner class ChunkListener(val pos: ChunkPos) : IChunkListener {
|
||||
override fun onEntityAdded(entity: AbstractEntity) {
|
||||
if (entity is WorldObject && !isLegacy)
|
||||
send(SpawnWorldObjectPacket(entity.uuid, entity.serialize()))
|
||||
//if (entity is WorldObject && !isLegacy)
|
||||
// send(SpawnWorldObjectPacket(entity.uuid, entity.serialize()))
|
||||
}
|
||||
|
||||
override fun onEntityRemoved(entity: AbstractEntity) {
|
||||
send(ForgetEntityPacket(entity.uuid))
|
||||
//send(ForgetEntityPacket(entity.uuid))
|
||||
}
|
||||
|
||||
override fun onCellChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
|
@ -1,21 +1,36 @@
|
||||
package ru.dbotthepony.kstarbound.world
|
||||
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kommons.io.StreamCodec
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
|
||||
enum class Direction(val normal: Vector2d, val jsonName: String) : IStringSerializable {
|
||||
UP(Vector2d.POSITIVE_Y, "up"),
|
||||
RIGHT(Vector2d.POSITIVE_X, "right"),
|
||||
DOWN(Vector2d.NEGATIVE_Y, "down"),
|
||||
enum class Direction(val normal: Vector2d, override val jsonName: String) : IStringSerializable {
|
||||
LEFT(Vector2d.NEGATIVE_X, "left"),
|
||||
RIGHT(Vector2d.POSITIVE_X, "right"),
|
||||
|
||||
UP(Vector2d.POSITIVE_Y, "up"),
|
||||
DOWN(Vector2d.NEGATIVE_Y, "down"),
|
||||
NONE(Vector2d.ZERO, "any");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
override fun write(out: JsonWriter) {
|
||||
out.value(jsonName)
|
||||
companion object {
|
||||
val CODEC = StreamCodec.Enum(Direction::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Direction1D(val normal: Vector2d, override val jsonName: String) : IStringSerializable {
|
||||
LEFT(Vector2d.NEGATIVE_X, "left"),
|
||||
RIGHT(Vector2d.POSITIVE_X, "right");
|
||||
|
||||
override fun match(name: String): Boolean {
|
||||
return name.lowercase() == jsonName
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC = StreamCodec.Enum(Direction1D::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import ru.dbotthepony.kstarbound.world.physics.getBlockPlatforms
|
||||
import ru.dbotthepony.kstarbound.world.physics.getBlocksMarchingSquares
|
||||
import java.io.Closeable
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.function.Predicate
|
||||
import java.util.random.RandomGenerator
|
||||
@ -43,6 +44,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
val sky = Sky()
|
||||
val geometry: WorldGeometry = template.geometry
|
||||
|
||||
val nextEntityID = AtomicInteger()
|
||||
|
||||
override fun getCellDirect(x: Int, y: Int): AbstractCell {
|
||||
if (!geometry.x.inBoundsCell(x) || !geometry.y.inBoundsCell(y)) return AbstractCell.NULL
|
||||
return getCell(x, y)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.util.MailboxExecutorService
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.defs.JsonDriven
|
||||
@ -8,6 +9,7 @@ import ru.dbotthepony.kstarbound.world.Chunk
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import java.io.DataOutputStream
|
||||
import java.util.UUID
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@ -41,8 +43,16 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) {
|
||||
}
|
||||
}
|
||||
|
||||
var uuid: UUID = UUID.randomUUID()
|
||||
abstract val chunkPos: ChunkPos
|
||||
abstract val position: Vector2d
|
||||
|
||||
var entityID: Int = 0
|
||||
set(value) {
|
||||
if (field != 0)
|
||||
throw IllegalStateException("Already has Entity ID set (to $field)")
|
||||
|
||||
field = value
|
||||
}
|
||||
|
||||
var mailbox = MailboxExecutorService()
|
||||
private set
|
||||
@ -55,6 +65,15 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) {
|
||||
val isSpawned: Boolean
|
||||
get() = innerWorld != null
|
||||
|
||||
|
||||
/**
|
||||
* If set, then the entity will be discoverable by its unique id and will be
|
||||
* indexed in the stored world. Unique ids must be different across all
|
||||
* entities in a single world.
|
||||
*/
|
||||
var uniqueID: String? = null
|
||||
protected set
|
||||
|
||||
/**
|
||||
* Whenever this entity should be removed when chunk containing it is being unloaded
|
||||
*
|
||||
@ -67,6 +86,8 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) {
|
||||
protected open fun onJoinWorld(world: World<*, *>) { }
|
||||
protected open fun onRemove(world: World<*, *>) { }
|
||||
|
||||
abstract fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean)
|
||||
|
||||
/**
|
||||
* MUST be called by [World] itself
|
||||
*/
|
||||
@ -74,6 +95,9 @@ abstract class AbstractEntity(path: String) : JsonDriven(path) {
|
||||
if (innerWorld != null)
|
||||
throw IllegalStateException("Already spawned (in world $innerWorld)")
|
||||
|
||||
if (entityID == 0)
|
||||
entityID = world.nextEntityID.incrementAndGet()
|
||||
|
||||
world.ensureSameThread()
|
||||
|
||||
if (mailbox.isShutdown)
|
||||
|
@ -0,0 +1,8 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
/**
|
||||
* Monsters, NPCs, Players
|
||||
*/
|
||||
abstract class ActorEntity(path: String) : DynamicEntity(path) {
|
||||
final override val movement: ActorMovementController = ActorMovementController()
|
||||
}
|
@ -1,85 +1,93 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||
import ru.dbotthepony.kstarbound.defs.JumpProfile
|
||||
import ru.dbotthepony.kstarbound.defs.MovementParameters
|
||||
import ru.dbotthepony.kstarbound.defs.player.ActorMovementModifiers
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedBoolean
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEnum
|
||||
import ru.dbotthepony.kstarbound.util.GameTimer
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.Direction1D
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
|
||||
abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
abstract var controlRun: Boolean
|
||||
abstract var controlCrouch: Boolean
|
||||
abstract var controlDown: Boolean
|
||||
abstract var lastControlDown: Boolean
|
||||
abstract var controlFly: Vector2d?
|
||||
abstract var controlFace: Direction?
|
||||
class ActorMovementController : MovementController() {
|
||||
var controlRun: Boolean = false
|
||||
var controlCrouch: Boolean = false
|
||||
var controlDown: Boolean = false
|
||||
var lastControlDown: Boolean = false
|
||||
var controlFly: Vector2d? = null
|
||||
var controlFace: Direction1D? = null
|
||||
|
||||
abstract var isRunning: Boolean
|
||||
protected set
|
||||
abstract var isWalking: Boolean
|
||||
protected set
|
||||
abstract var isCrouching: Boolean
|
||||
protected set
|
||||
abstract var isFlying: Boolean
|
||||
protected set
|
||||
abstract var isFalling: Boolean
|
||||
protected set
|
||||
abstract var isJumping: Boolean
|
||||
protected set
|
||||
var isWalking: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
var isRunning: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
|
||||
abstract var canJump: Boolean
|
||||
abstract var controlJump: Boolean
|
||||
abstract var controlJumpAnyway: Boolean
|
||||
var movingDirection: Direction1D by networkGroup.upstream.add(networkedEnum(Direction1D.RIGHT))
|
||||
private set
|
||||
var facingDirection: Direction1D by networkGroup.upstream.add(networkedEnum(Direction1D.RIGHT))
|
||||
private set
|
||||
|
||||
abstract var lastControlJump: Boolean
|
||||
protected set
|
||||
abstract var lastControlCrouch: Boolean
|
||||
protected set
|
||||
var isCrouching: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
|
||||
abstract var isGroundMovement: Boolean
|
||||
protected set
|
||||
abstract var isLiquidMovement: Boolean
|
||||
protected set
|
||||
var isFlying: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
var isFalling: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
|
||||
abstract var controlRotationRate: Double
|
||||
abstract var controlAcceleration: Vector2d
|
||||
abstract var controlForce: Vector2d
|
||||
var canJump: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
var isJumping: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
|
||||
abstract var fallThroughSustain: Int
|
||||
protected set
|
||||
var isGroundMovement: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
var isLiquidMovement: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
private set
|
||||
|
||||
var controlJump: Boolean = false
|
||||
var controlJumpAnyway: Boolean = false
|
||||
|
||||
var lastControlJump: Boolean = false
|
||||
private set
|
||||
var lastControlCrouch: Boolean = false
|
||||
private set
|
||||
|
||||
var controlRotationRate: Double = 0.0
|
||||
var controlAcceleration: Vector2d = Vector2d.ZERO
|
||||
var controlForce: Vector2d = Vector2d.ZERO
|
||||
|
||||
var fallThroughSustain: Int = 0
|
||||
private set
|
||||
|
||||
// Target horizontal velocity for walking / running
|
||||
abstract var targetHorizontalAmbulatingVelocity: Double
|
||||
protected set
|
||||
var targetHorizontalAmbulatingVelocity: Double = 0.0
|
||||
private set
|
||||
|
||||
abstract var controlPathMove: Pair<Vector2d, Boolean>?
|
||||
abstract var pathMoveResult: Pair<Vector2d, Boolean>?
|
||||
var controlPathMove: Pair<Vector2d, Boolean>? = null
|
||||
var pathMoveResult: Pair<Vector2d, Boolean>? = null
|
||||
|
||||
abstract var controlMove: Direction?
|
||||
var controlMove: Direction? = null
|
||||
|
||||
abstract var actorMovementParameters: ActorMovementParameters
|
||||
abstract var movementModifiers: ActorMovementModifiers
|
||||
var actorMovementParameters: ActorMovementParameters = ActorMovementParameters.EMPTY
|
||||
var movementModifiers: ActorMovementModifiers = ActorMovementModifiers.EMPTY
|
||||
|
||||
abstract var controlActorMovementParameters: ActorMovementParameters
|
||||
abstract var controlMovementModifiers: ActorMovementModifiers
|
||||
var controlActorMovementParameters: ActorMovementParameters = ActorMovementParameters.EMPTY
|
||||
var controlMovementModifiers: ActorMovementModifiers = ActorMovementModifiers.EMPTY
|
||||
|
||||
abstract val approachVelocities: MutableList<ApproachVelocityCommand>
|
||||
abstract val approachVelocityAngles: MutableList<ApproachVelocityAngleCommand>
|
||||
val approachVelocities = ArrayList<ApproachVelocityCommand>()
|
||||
val approachVelocityAngles = ArrayList<ApproachVelocityAngleCommand>()
|
||||
|
||||
abstract var movingDirection: Direction?
|
||||
abstract var facingDirection: Direction?
|
||||
|
||||
// this is set internally on each move step
|
||||
final override var movementParameters: MovementParameters = MovementParameters.EMPTY
|
||||
|
||||
abstract var anchorEntity: DynamicEntity?
|
||||
var anchorEntity: DynamicEntity? = null
|
||||
|
||||
var pathController: PathController? = null
|
||||
var groundMovementSustainTimer: GameTimer = GameTimer(0.0)
|
||||
@ -177,7 +185,7 @@ abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
return params
|
||||
}
|
||||
|
||||
open fun clearControls() {
|
||||
fun clearControls() {
|
||||
controlRotationRate = 0.0
|
||||
controlAcceleration = Vector2d.ZERO
|
||||
controlForce = Vector2d.ZERO
|
||||
@ -268,7 +276,7 @@ abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
isLiquidMovement = liquidPercentage >= (actorMovementParameters.minimumLiquidPercentage ?: 0.0)
|
||||
val liquidImpedance = liquidPercentage * (actorMovementParameters.liquidImpedance ?: 0.0)
|
||||
|
||||
var updatedMovingDirection: Direction? = null
|
||||
var updatedMovingDirection: Direction1D? = null
|
||||
val isRunning = controlRun && !movementModifiers.runningSuppressed
|
||||
|
||||
if (controlFly != null) {
|
||||
@ -283,9 +291,9 @@ abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
approachVelocity(flyVelocity * movementModifiers.speedModifier, movementParameters.airForce ?: 0.0)
|
||||
|
||||
if (flyVelocity.x > 0.0)
|
||||
updatedMovingDirection = Direction.RIGHT
|
||||
updatedMovingDirection = Direction1D.RIGHT
|
||||
else if (flyVelocity.x < 0.0)
|
||||
updatedMovingDirection = Direction.LEFT
|
||||
updatedMovingDirection = Direction1D.LEFT
|
||||
|
||||
groundMovementSustainTimer = GameTimer(0.0)
|
||||
} else {
|
||||
@ -365,10 +373,10 @@ abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
}
|
||||
|
||||
if (controlMove == Direction.LEFT) {
|
||||
updatedMovingDirection = Direction.LEFT
|
||||
updatedMovingDirection = Direction1D.LEFT
|
||||
targetHorizontalAmbulatingVelocity = -1.0 * (if (isRunning) movementParameters.runSpeed ?: 0.0 else movementParameters.walkSpeed ?: 0.0) * movementModifiers.speedModifier
|
||||
} else if (controlMove == Direction.RIGHT) {
|
||||
updatedMovingDirection = Direction.RIGHT
|
||||
updatedMovingDirection = Direction1D.RIGHT
|
||||
targetHorizontalAmbulatingVelocity = 1.0 * (if (isRunning) movementParameters.runSpeed ?: 0.0 else movementParameters.walkSpeed ?: 0.0) * movementModifiers.speedModifier
|
||||
}
|
||||
|
||||
@ -391,15 +399,16 @@ abstract class AbstractActorMovementController : AbstractMovementController() {
|
||||
}
|
||||
}
|
||||
|
||||
movingDirection = updatedMovingDirection
|
||||
if (updatedMovingDirection != null)
|
||||
movingDirection = updatedMovingDirection
|
||||
|
||||
if (!movementModifiers.facingSuppressed) {
|
||||
if (controlFace != null)
|
||||
facingDirection = controlFace
|
||||
facingDirection = controlFace!!
|
||||
else if (updatedMovingDirection != null)
|
||||
facingDirection = updatedMovingDirection
|
||||
else if (controlPathMove != null && pathController?.controlFace != null)
|
||||
facingDirection = pathController?.controlFace
|
||||
facingDirection = pathController?.controlFace!!
|
||||
}
|
||||
|
||||
isGroundMovement = !groundMovementSustainTimer.hasFinished
|
@ -8,6 +8,7 @@ import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* Entities with dynamics (Player, Drops, Projectiles, NPCs, etc)
|
||||
@ -15,35 +16,33 @@ import ru.dbotthepony.kstarbound.world.World
|
||||
abstract class DynamicEntity(path: String) : AbstractEntity(path) {
|
||||
private var forceChunkRepos = false
|
||||
|
||||
var position = Vector2d()
|
||||
override var position
|
||||
get() = movement.position
|
||||
set(value) {
|
||||
val old = field
|
||||
|
||||
if (isSpawned) {
|
||||
field = world.geometry.wrap(value)
|
||||
|
||||
val oldChunkPos = world.geometry.chunkFromCell(old)
|
||||
val newChunkPos = world.geometry.chunkFromCell(field)
|
||||
|
||||
chunkPos = newChunkPos
|
||||
|
||||
if (oldChunkPos != newChunkPos || forceChunkRepos) {
|
||||
chunk = world.chunkMap[newChunkPos]
|
||||
forceChunkRepos = false
|
||||
}
|
||||
} else {
|
||||
field = value
|
||||
}
|
||||
movement.position = value
|
||||
}
|
||||
|
||||
abstract val movement: AbstractMovementController
|
||||
abstract val movement: MovementController
|
||||
|
||||
final override var chunkPos: ChunkPos = ChunkPos.ZERO
|
||||
private set
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
world.dynamicEntities.add(this)
|
||||
movement.world = world
|
||||
forceChunkRepos = true
|
||||
position = position
|
||||
|
||||
/*movement.positionListeners.addListener(Consumer { field ->
|
||||
val oldChunkPos = world.geometry.chunkFromCell(old)
|
||||
val newChunkPos = world.geometry.chunkFromCell(field)
|
||||
|
||||
chunkPos = newChunkPos
|
||||
|
||||
if (oldChunkPos != newChunkPos || forceChunkRepos) {
|
||||
chunk = world.chunkMap[newChunkPos]
|
||||
forceChunkRepos = false
|
||||
}
|
||||
})*/
|
||||
}
|
||||
|
||||
override fun onRemove(world: World<*, *>) {
|
||||
@ -65,7 +64,5 @@ abstract class DynamicEntity(path: String) : AbstractEntity(path) {
|
||||
|
||||
companion object {
|
||||
val BLOCK_COLLISION_COLOR = RGBAColor(65, 179, 217)
|
||||
const val SEPARATION_STEPS = 3
|
||||
const val SEPARATION_TOLERANCE = 0.001
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.GlobalDefaults
|
||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||
import ru.dbotthepony.kstarbound.defs.player.ActorMovementModifiers
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
|
||||
class EntityActorMovementController(val entity: DynamicEntity) : AbstractActorMovementController() {
|
||||
override val world: World<*, *> by entity::world
|
||||
override var position: Vector2d by entity::position
|
||||
override var actorMovementParameters: ActorMovementParameters = GlobalDefaults.actorMovementParameters
|
||||
|
||||
override var lastControlDown: Boolean = false
|
||||
override var fallThroughSustain: Int = 0
|
||||
override var targetHorizontalAmbulatingVelocity: Double = 0.0
|
||||
|
||||
override var controlRun: Boolean = false
|
||||
override var controlCrouch: Boolean = false
|
||||
override var controlDown: Boolean = false
|
||||
override var controlFly: Vector2d? = null
|
||||
override var isFlying: Boolean = false
|
||||
override var isFalling: Boolean = false
|
||||
override var canJump: Boolean = true
|
||||
override var controlJump: Boolean = false
|
||||
override var isGroundMovement: Boolean = false
|
||||
override var isLiquidMovement: Boolean = false
|
||||
override var controlPathMove: Pair<Vector2d, Boolean>? = null
|
||||
override var pathMoveResult: Pair<Vector2d, Boolean>? = null
|
||||
override var controlMove: Direction? = null
|
||||
override var movementModifiers: ActorMovementModifiers = ActorMovementModifiers.EMPTY
|
||||
|
||||
override var controlActorMovementParameters: ActorMovementParameters = ActorMovementParameters.EMPTY
|
||||
override var controlMovementModifiers: ActorMovementModifiers = ActorMovementModifiers.EMPTY
|
||||
|
||||
override var isOnGround: Boolean = false
|
||||
override var isColliding: Boolean = false
|
||||
override var isCollisionStuck: Boolean = false
|
||||
override var isCollidingWithNull: Boolean = false
|
||||
override var stickingDirection: Double? = null
|
||||
override var surfaceSlope: Vector2d = Vector2d.ZERO
|
||||
override var surfaceVelocity: Vector2d = Vector2d.ZERO
|
||||
override var collisionCorrection: Vector2d = Vector2d.ZERO
|
||||
override var liquidPercentage: Double = 0.0
|
||||
override var appliedForceRegion: Boolean = false
|
||||
override var isZeroGravity: Boolean = false
|
||||
|
||||
override var controlRotationRate: Double = 0.0
|
||||
override var controlAcceleration: Vector2d = Vector2d.ZERO
|
||||
override var controlForce: Vector2d = Vector2d.ZERO
|
||||
override var rotation: Double = 0.0
|
||||
|
||||
override var lastControlCrouch: Boolean = false
|
||||
override var controlFace: Direction? = null
|
||||
override var isRunning: Boolean = false
|
||||
override var isWalking: Boolean = false
|
||||
override var isCrouching: Boolean = false
|
||||
override var isJumping: Boolean = false
|
||||
override var lastControlJump: Boolean = false
|
||||
override var controlJumpAnyway: Boolean = false
|
||||
override val approachVelocities: MutableList<ApproachVelocityCommand> = ArrayList()
|
||||
override val approachVelocityAngles: MutableList<ApproachVelocityAngleCommand> = ArrayList()
|
||||
override var movingDirection: Direction? = null
|
||||
override var facingDirection: Direction? = null
|
||||
override var anchorEntity: DynamicEntity? = null
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.GlobalDefaults
|
||||
import ru.dbotthepony.kstarbound.defs.MovementParameters
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
|
||||
class EntityMovementController(val entity: DynamicEntity) : AbstractMovementController() {
|
||||
override val world: World<*, *> by entity::world
|
||||
override var position: Vector2d by entity::position
|
||||
override var movementParameters: MovementParameters = GlobalDefaults.movementParameters
|
||||
|
||||
override var isOnGround: Boolean = false
|
||||
override var isColliding: Boolean = false
|
||||
override var isCollisionStuck: Boolean = false
|
||||
override var isCollidingWithNull: Boolean = false
|
||||
override var stickingDirection: Double? = null
|
||||
override var surfaceSlope: Vector2d = Vector2d.ZERO
|
||||
override var surfaceVelocity: Vector2d = Vector2d.ZERO
|
||||
override var collisionCorrection: Vector2d = Vector2d.ZERO
|
||||
override var liquidPercentage: Double = 0.0
|
||||
override var appliedForceRegion: Boolean = false
|
||||
override var isZeroGravity: Boolean = false
|
||||
override var rotation: Double = 0.0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
|
||||
/**
|
||||
* Players and NPCs
|
||||
*/
|
||||
abstract class HumanoidActorEntity(path: String) : ActorEntity(path) {
|
||||
abstract val aimPosition: Vector2d
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
|
||||
class ItemEntity(val def: IItemDefinition) : DynamicEntity("/") {
|
||||
override val movement = EntityMovementController(this)
|
||||
|
||||
override fun defs(): Collection<JsonObject> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
init {
|
||||
movement.movementParameters = movement.movementParameters.copy(collisionPoly = Either.left(Poly(AABB.rectangle(Vector2d.ZERO, 0.75, 0.75))))
|
||||
}
|
||||
}
|
@ -1,11 +1,28 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
||||
import ru.dbotthepony.kommons.io.DoubleValueCodec
|
||||
import ru.dbotthepony.kommons.io.FloatValueCodec
|
||||
import ru.dbotthepony.kommons.io.StreamCodec
|
||||
import ru.dbotthepony.kommons.io.koptional
|
||||
import ru.dbotthepony.kommons.io.map
|
||||
import ru.dbotthepony.kommons.math.linearInterpolation
|
||||
import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kommons.util.Listenable
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.times
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.MovementParameters
|
||||
import ru.dbotthepony.kstarbound.network.syncher.GroupElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.MasterElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedBoolean
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedData
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFixedPoint
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedPoly
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
@ -16,15 +33,14 @@ import kotlin.math.absoluteValue
|
||||
import kotlin.math.acos
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
abstract class AbstractMovementController() {
|
||||
abstract val world: World<*, *>
|
||||
open class MovementController() {
|
||||
var world: World<*, *> by Delegates.notNull()
|
||||
|
||||
val localHitboxes: Stream<Poly>
|
||||
get() { return (movementParameters.collisionPoly?.map({ Stream.of(it) }, { it.stream() }) ?: return Stream.of()).map { it.rotate(rotation) + position } }
|
||||
|
||||
abstract var position: Vector2d
|
||||
|
||||
open fun shouldCollideWithType(type: CollisionType): Boolean {
|
||||
return type !== CollisionType.NONE
|
||||
}
|
||||
@ -33,43 +49,75 @@ abstract class AbstractMovementController() {
|
||||
return shouldCollideWithType(body.type)
|
||||
}
|
||||
|
||||
private val legacyPoly = networkedPoly(Poly.EMPTY)
|
||||
|
||||
protected val networkGroup = MasterElement(GroupElement(legacyPoly))
|
||||
|
||||
var mass by networkGroup.upstream.add(networkedFloat())
|
||||
|
||||
private var xPosition by networkGroup.upstream.add(networkedFixedPoint(0.0125))
|
||||
private var yPosition by networkGroup.upstream.add(networkedFixedPoint(0.0125))
|
||||
private var xVelocity by networkGroup.upstream.add(networkedFixedPoint(0.00625))
|
||||
private var yVelocity by networkGroup.upstream.add(networkedFixedPoint(0.00625))
|
||||
|
||||
var rotation by networkGroup.upstream.add(networkedFixedPoint(0.01))
|
||||
|
||||
var isColliding: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
protected set
|
||||
var isCollisionStuck: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
protected set
|
||||
var isCollidingWithNull: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
protected set
|
||||
|
||||
private val stickingDirectionField = networkGroup.upstream.add(networkedData(KOptional(), DoubleValueCodec.koptional(), FloatValueCodec.map({ toDouble() }, { toFloat() }).koptional()))
|
||||
|
||||
var isOnGround: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
protected set
|
||||
var isZeroGravity: Boolean by networkGroup.upstream.add(networkedBoolean())
|
||||
protected set
|
||||
|
||||
private val surfaceMovingCollisionField = networkGroup.upstream.add(networkedData(KOptional(), StreamCodec.Impl(::MovingCollisionID, { a, b -> b.write(a) }).koptional()))
|
||||
private val relativeXSurfaceVelocity = networkGroup.upstream.add(networkedFloat())
|
||||
private val relativeYSurfaceVelocity = networkGroup.upstream.add(networkedFloat())
|
||||
|
||||
var position: Vector2d
|
||||
get() = Vector2d(xPosition, yPosition)
|
||||
set(value) {
|
||||
xPosition = value.x
|
||||
yPosition = value.y
|
||||
}
|
||||
|
||||
val positionListeners = Listenable.Impl<Vector2d>()
|
||||
|
||||
// Movement variables
|
||||
abstract var isOnGround: Boolean
|
||||
var stickingDirection: Double?
|
||||
get() = stickingDirectionField.get().orNull()
|
||||
protected set(value) {
|
||||
stickingDirectionField.accept(KOptional.ofNullable(value))
|
||||
}
|
||||
var surfaceSlope: Vector2d = Vector2d.ZERO
|
||||
protected set
|
||||
abstract var isColliding: Boolean
|
||||
var surfaceVelocity: Vector2d = Vector2d.ZERO
|
||||
protected set
|
||||
abstract var isCollisionStuck: Boolean
|
||||
var collisionCorrection: Vector2d = Vector2d.ZERO
|
||||
protected set
|
||||
abstract var isCollidingWithNull: Boolean
|
||||
protected set
|
||||
abstract var stickingDirection: Double?
|
||||
protected set
|
||||
abstract var surfaceSlope: Vector2d
|
||||
protected set
|
||||
abstract var surfaceVelocity: Vector2d
|
||||
protected set
|
||||
abstract var collisionCorrection: Vector2d
|
||||
protected set
|
||||
abstract var liquidPercentage: Double
|
||||
var liquidPercentage: Double = 0.0
|
||||
protected set
|
||||
|
||||
abstract var appliedForceRegion: Boolean
|
||||
var appliedForceRegion: Boolean = false
|
||||
protected set
|
||||
|
||||
abstract var isZeroGravity: Boolean
|
||||
protected set
|
||||
var movementParameters: MovementParameters = MovementParameters.EMPTY
|
||||
|
||||
abstract var movementParameters: MovementParameters
|
||||
var gravityMultiplier = 1.0
|
||||
var isGravityDisabled = false
|
||||
|
||||
abstract var rotation: Double
|
||||
|
||||
open var gravityMultiplier = 1.0
|
||||
open var isGravityDisabled = false
|
||||
|
||||
val mass: Double
|
||||
get() = movementParameters.mass ?: 1.0
|
||||
|
||||
var velocity = Vector2d.ZERO
|
||||
var velocity: Vector2d
|
||||
get() = Vector2d(xVelocity, yVelocity)
|
||||
set(value) {
|
||||
xVelocity = value.x
|
||||
yVelocity = value.y
|
||||
}
|
||||
|
||||
fun determineGravity(): Vector2d {
|
||||
if (isZeroGravity || isGravityDisabled)
|
@ -0,0 +1,21 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.io.readInt
|
||||
import ru.dbotthepony.kommons.io.writeInt
|
||||
import ru.dbotthepony.kstarbound.network.syncher.readPointer
|
||||
import ru.dbotthepony.kstarbound.network.syncher.writePointer
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
data class MovingCollisionID(val entity: Int = 0, val collisionIndex: Int = -1) {
|
||||
constructor(stream: InputStream) : this(stream.readInt(), stream.readPointer().toInt())
|
||||
|
||||
fun write(stream: OutputStream) {
|
||||
stream.writeInt(entity)
|
||||
stream.writePointer(collisionIndex)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ZERO = MovingCollisionID(0, -1)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.Direction1D
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
|
||||
class PathController(val world: World<*, *>, var edgeTimer: Double = 0.0) {
|
||||
@ -9,7 +9,7 @@ class PathController(val world: World<*, *>, var edgeTimer: Double = 0.0) {
|
||||
private set
|
||||
var endPosition: Vector2d? = null
|
||||
private set
|
||||
var controlFace: Direction? = null
|
||||
var controlFace: Direction1D? = null
|
||||
private set
|
||||
|
||||
fun reset() {
|
||||
|
@ -1,12 +1,62 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidData
|
||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||
import ru.dbotthepony.kstarbound.network.syncher.GroupElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.MasterElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedBoolean
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedData
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEnum
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEnumExtraStupid
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEnumStupid
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEventCounter
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFixedPoint
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
||||
import java.io.DataOutputStream
|
||||
import java.util.UUID
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class PlayerEntity() : DynamicEntity("/") {
|
||||
override val movement = EntityActorMovementController(this)
|
||||
class PlayerEntity() : HumanoidActorEntity("/") {
|
||||
enum class State(override val jsonName: String) : IStringSerializable {
|
||||
IDLE("Idle"),
|
||||
WALK("Walk"),
|
||||
RUN("Run"),
|
||||
JUMP("Jump"),
|
||||
FALL("Fall"),
|
||||
SWIM("Swim"),
|
||||
SWIM_IDLE("SwimIdle"),
|
||||
TELEPORT_IN("TeleportIn"),
|
||||
TELEPORT_OUT("TeleportOut"),
|
||||
CROUCH("Crouch"),
|
||||
LOUNGE("Lounge");
|
||||
}
|
||||
|
||||
override fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
val networkGroup = MasterElement(GroupElement())
|
||||
|
||||
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))
|
||||
private var yAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125).also { it.interpolator = Interpolator.Linear })
|
||||
var humanoidData by networkGroup.upstream.add(networkedData(HumanoidData(), HumanoidData.CODEC, HumanoidData.LEGACY_CODEC))
|
||||
var teamState by networkGroup.upstream.add(networkedData(EntityDamageTeam(), EntityDamageTeam.CODEC, EntityDamageTeam.LEGACY_CODEC))
|
||||
val landed = networkGroup.upstream.add(networkedEventCounter())
|
||||
var chatMessage by networkGroup.upstream.add(networkedString())
|
||||
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
||||
var emote by networkGroup.upstream.add(networkedEnumExtraStupid(HumanoidEmote.IDLE))
|
||||
|
||||
override val aimPosition: Vector2d
|
||||
get() = Vector2d(xAimPosition, yAimPosition)
|
||||
|
||||
override val isApplicableForUnloading: Boolean
|
||||
get() = false
|
||||
@ -15,36 +65,6 @@ class PlayerEntity() : DynamicEntity("/") {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
init {
|
||||
movement.actorMovementParameters = movement.actorMovementParameters.merge(
|
||||
Starbound.gson.fromJson("""
|
||||
{
|
||||
"standingPoly" : [ [-0.75, -2.0], [-0.35, -2.5], [0.35, -2.5], [0.75, -2.0], [0.75, 0.65], [0.35, 1.22], [-0.35, 1.22], [-0.75, 0.65] ],
|
||||
"crouchingPoly" : [ [-0.75, -2.0], [-0.35, -2.5], [0.35, -2.5], [0.75, -2.0], [0.75, -1], [0.35, -0.5], [-0.35, -0.5], [-0.75, -1] ],
|
||||
"mass" : 1.6,
|
||||
|
||||
// should keep the player from teleporting through walls
|
||||
"maximumCorrection" : 3,
|
||||
"maxMovementPerStep" : 0.4,
|
||||
|
||||
"liquidFriction" : 13.0,
|
||||
"normalGroundFriction" : 35.0,
|
||||
|
||||
"groundForce" : 250.0,
|
||||
"airForce" : 50.0,
|
||||
"liquidForce" : 80.0
|
||||
}
|
||||
""".trimIndent(), ActorMovementParameters::class.java)
|
||||
).merge(Starbound.gson.fromJson("""
|
||||
{
|
||||
"flySpeed" : 0,
|
||||
"airFriction" : 0.5,
|
||||
"airJumpProfile" : {
|
||||
"jumpSpeed" : 23.0,
|
||||
"jumpInitialPercentage" : 0.75,
|
||||
"jumpHoldTime" : 0.2
|
||||
}
|
||||
}
|
||||
""".trimIndent(), ActorMovementParameters::class.java))
|
||||
}
|
||||
var uuid: UUID by Delegates.notNull()
|
||||
private set
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
@ -11,7 +12,7 @@ import ru.dbotthepony.kstarbound.world.World
|
||||
abstract class TileEntity(path: String) : AbstractEntity(path) {
|
||||
private var forceChunkRepos = false
|
||||
|
||||
var position = Vector2i()
|
||||
var tilePosition = Vector2i()
|
||||
set(value) {
|
||||
val old = field
|
||||
|
||||
@ -32,13 +33,16 @@ abstract class TileEntity(path: String) : AbstractEntity(path) {
|
||||
}
|
||||
}
|
||||
|
||||
override val position: Vector2d
|
||||
get() = tilePosition.toDoubleVector()
|
||||
|
||||
final override var chunkPos: ChunkPos = ChunkPos.ZERO
|
||||
private set
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
world.tileEntities.add(this)
|
||||
forceChunkRepos = true
|
||||
position = position
|
||||
tilePosition = tilePosition
|
||||
}
|
||||
|
||||
override fun onRemove(world: World<*, *>) {
|
||||
|
@ -27,6 +27,7 @@ import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
open class WorldObject(
|
||||
@ -47,10 +48,14 @@ open class WorldObject(
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
fun serialize(): JsonObject {
|
||||
val into = JsonObject()
|
||||
into["name"] = prototype.key
|
||||
into["tilePosition"] = vectors.toJsonTree(position)
|
||||
into["tilePosition"] = vectors.toJsonTree(tilePosition)
|
||||
into["direction"] = directions.toJsonTree(direction)
|
||||
into["orientationIndex"] = orientationIndex
|
||||
into["interactive"] = interactive
|
||||
@ -170,7 +175,7 @@ open class WorldObject(
|
||||
color *= sample
|
||||
}
|
||||
|
||||
lightCalculator.addPointLight(position.x - xOffset, position.y - yOffset, color)
|
||||
lightCalculator.addPointLight(tilePosition.x - xOffset, tilePosition.y - yOffset, color)
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +199,7 @@ open class WorldObject(
|
||||
val prototype = Registries.worldObjects[content["name"]?.asString ?: throw IllegalArgumentException("Missing object name")] ?: throw IllegalArgumentException("No such object defined for '${content["name"]}'")
|
||||
val pos = content.get("tilePosition", vectors) { throw IllegalArgumentException("No tilePosition was present in saved data") }
|
||||
val result = WorldObject(prototype)
|
||||
result.position = pos
|
||||
result.tilePosition = pos
|
||||
result.deserialize(content)
|
||||
return result
|
||||
}
|
||||
|
@ -18,7 +18,16 @@ import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||
import ru.dbotthepony.kommons.gson.consumeNull
|
||||
import ru.dbotthepony.kommons.io.StreamCodec
|
||||
import ru.dbotthepony.kommons.io.readCollection
|
||||
import ru.dbotthepony.kommons.io.readVector2d
|
||||
import ru.dbotthepony.kommons.io.readVector2f
|
||||
import ru.dbotthepony.kommons.io.writeCollection
|
||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
||||
import ru.dbotthepony.kommons.io.writeStruct2f
|
||||
import ru.dbotthepony.kstarbound.json.listAdapter
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
@ -42,7 +51,8 @@ private fun calculateEdges(points: List<Vector2d>): Pair<ImmutableList<Poly.Edge
|
||||
newPoints.add(p1 - normal * 0.001)
|
||||
newPoints.add(p0 - normal * 0.001)
|
||||
|
||||
return calculateEdges(newPoints.build())
|
||||
val (f) = calculateEdges(newPoints.build())
|
||||
return f to ImmutableList.copyOf(points)
|
||||
} else {
|
||||
val edges = ImmutableList.Builder<Poly.Edge>()
|
||||
|
||||
@ -77,10 +87,20 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
constructor(points: List<Vector2d>) : this(calculateEdges(points))
|
||||
constructor(aabb: AABB) : this(listOf(aabb.bottomLeft, aabb.topLeft, aabb.topRight, aabb.bottomRight))
|
||||
|
||||
val aabb = AABB(
|
||||
Vector2d(vertices.minOf { it.x }, vertices.minOf { it.y }),
|
||||
Vector2d(vertices.maxOf { it.x }, vertices.maxOf { it.y }),
|
||||
)
|
||||
val aabb: AABB
|
||||
val isEmpty: Boolean
|
||||
get() = vertices.isEmpty()
|
||||
|
||||
init {
|
||||
if (vertices.isEmpty()) {
|
||||
aabb = AABB.ZERO
|
||||
} else {
|
||||
aabb = AABB(
|
||||
Vector2d(vertices.minOf { it.x }, vertices.minOf { it.y }),
|
||||
Vector2d(vertices.maxOf { it.x }, vertices.maxOf { it.y }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class Edge(val p0: Vector2d, val p1: Vector2d, val normal: Vector2d) {
|
||||
operator fun plus(other: IStruct2d): Edge {
|
||||
@ -109,6 +129,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
operator fun plus(value: Penetration): Poly {
|
||||
if (isEmpty) return this
|
||||
val vertices = ImmutableList.Builder<Vector2d>()
|
||||
val edges = ImmutableList.Builder<Edge>()
|
||||
|
||||
@ -119,6 +140,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
operator fun plus(value: IStruct2d): Poly {
|
||||
if (isEmpty) return this
|
||||
val vertices = ImmutableList.Builder<Vector2d>()
|
||||
val edges = ImmutableList.Builder<Edge>()
|
||||
|
||||
@ -129,6 +151,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
operator fun minus(value: IStruct2d): Poly {
|
||||
if (isEmpty) return this
|
||||
val vertices = ImmutableList.Builder<Vector2d>()
|
||||
val edges = ImmutableList.Builder<Edge>()
|
||||
|
||||
@ -139,6 +162,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
operator fun times(value: IStruct2d): Poly {
|
||||
if (isEmpty) return this
|
||||
val vertices = ImmutableList.Builder<Vector2d>()
|
||||
val edges = ImmutableList.Builder<Edge>()
|
||||
|
||||
@ -149,6 +173,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
operator fun times(value: Double): Poly {
|
||||
if (isEmpty) return this
|
||||
val vertices = ImmutableList.Builder<Vector2d>()
|
||||
val edges = ImmutableList.Builder<Edge>()
|
||||
|
||||
@ -159,7 +184,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
fun rotate(radians: Double): Poly {
|
||||
if (radians == 0.0)
|
||||
if (radians == 0.0 || isEmpty)
|
||||
return this
|
||||
|
||||
val sin = sin(radians)
|
||||
@ -181,6 +206,8 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
|
||||
// min / max
|
||||
fun project(normal: Vector2d): IStruct2d {
|
||||
check(!isEmpty) { "Poly is empty" }
|
||||
|
||||
var min = vertices.first().dot(normal)
|
||||
var max = min
|
||||
|
||||
@ -198,7 +225,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
* @param axis separate ONLY along specified axis, that said, if axis is positive Y and we have collision, and closest separation axis is to up right, we instead separate only up until we no longer collide.
|
||||
*/
|
||||
fun intersect(other: Poly, axis: Vector2d? = null, strictAxis: Boolean = false): Penetration? {
|
||||
if (!aabb.intersectWeak(other.aabb))
|
||||
if (isEmpty || !aabb.intersectWeak(other.aabb))
|
||||
return null
|
||||
|
||||
val normals = ObjectOpenHashSet<Vector2d>()
|
||||
@ -284,6 +311,8 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
fun render(client: StarboundClient = StarboundClient.current(), color: RGBAColor = RGBAColor.LIGHT_GREEN) {
|
||||
if (isEmpty) return
|
||||
|
||||
val program = client.programs.position
|
||||
val lines = program.builder.builder
|
||||
program.use()
|
||||
@ -307,7 +336,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Poly[aabb = $aabb; edges = $edges]"
|
||||
return "Poly[aabb = $aabb; edges = $edges; vertices = $vertices]"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@ -318,9 +347,30 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
|
||||
return vertices.hashCode()
|
||||
}
|
||||
|
||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
if (isLegacy) {
|
||||
stream.writeCollection(vertices) { writeStruct2f(it.toFloatVector()) }
|
||||
} else {
|
||||
stream.writeCollection(vertices) { writeStruct2d(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object : TypeAdapterFactory {
|
||||
val CODEC = StreamCodec.Impl({ read(it, false) }, { a, b -> b.write(a, false) })
|
||||
val LEGACY_CODEC = StreamCodec.Impl({ read(it, true) }, { a, b -> b.write(a, true) })
|
||||
|
||||
val EMPTY = Poly(ImmutableList.of(), ImmutableList.of())
|
||||
|
||||
private val identity = Matrix3f.identity()
|
||||
|
||||
fun read(stream: DataInputStream, isLegacy: Boolean): Poly {
|
||||
if (isLegacy) {
|
||||
return Poly(stream.readCollection { readVector2f().toDoubleVector() })
|
||||
} else {
|
||||
return Poly(stream.readCollection { readVector2d() })
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType === Poly::class.java) {
|
||||
return object : TypeAdapter<Poly>() {
|
||||
|
Loading…
Reference in New Issue
Block a user