massive refactor to export entities (buildings and resources)

- minor version bumps
- new Factorio version
- use both prototype name and type to identify a prototype
- kxs-ts-gen: use BrandTyping, but exclude some identifiers
- kxs-ts-gen: manually create zip dist, so it can run after the project compiles
- mod: use typed-factorio strict types
- mod: refactor event handlers to be a little more consistent and object-oriented
This commit is contained in:
Adam 2022-07-05 23:44:23 +02:00
parent 88982d818a
commit 90dc520d22
62 changed files with 2629 additions and 713 deletions

View file

@ -18,12 +18,14 @@ fun Configuration.asProvider() {
isCanBeConsumed = true
}
fun Configuration.asConsumer() {
isVisible = false
isCanBeResolved = true
isCanBeConsumed = false
}
fun Configuration.factorioModAttributes(objects: ObjectFactory): Configuration =
attributes {
attribute(USAGE_ATTRIBUTE, objects.named("factorio"))
@ -32,6 +34,7 @@ fun Configuration.factorioModAttributes(objects: ObjectFactory): Configuration =
attribute(BUNDLING_ATTRIBUTE, objects.named(Bundling.EMBEDDED))
}
fun Configuration.typescriptAttributes(objects: ObjectFactory): Configuration =
attributes {
attribute(USAGE_ATTRIBUTE, objects.named("typescript"))

View file

@ -6,8 +6,10 @@ import org.gradle.api.file.FileSystemOperations
import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.work.DisableCachingByDefault
@DisableCachingByDefault(because = "caching makes regenerating more difficult")
abstract class GenerateTypeScriptTask @Inject constructor(
private val fileOps: FileSystemOperations,
) : JavaExec() {

View file

@ -16,20 +16,22 @@ kvision = "5.11.0"
log4j2 = "2.17.2"
qosLogback = "1.2.11"
slf4j = "1.7.36"
okio = "3.1.0"
okio = "3.2.0"
scrimage = "4.0.31"
semver = "1.2.0"
kotlinx-serialization = "1.3.3"
kxs-charleskornKaml = "0.44.0"
kxs-mamoeYamlkt = "0.12.0"
kxs-kxsTsGen = "0.2.1"
kotlinJsWrappers = "1.0.0-pre.346"
kotlinJsWrappers = "1.0.0-pre.349" # https://search.maven.org/artifact/org.jetbrains.kotlin-wrappers/kotlin-wrappers-bom
kotlinx-cli = "0.3.4"
kotlinx-coroutines = "1.6.3"
kotlinx-datetime = "0.3.3"
kotlinx-html = "0.7.5"
kotlinx-nodejs = "0.0.7"
kotlinx-coroutines = "1.6.1"
kotlinx-cli = "0.3.4"
kotlinx-datetime = "0.3.3"
ktor = "2.0.3"
@ -39,20 +41,24 @@ simpleSyslogParser = "0.0.6"
kotkaStreams = "dev-SNAPSHOT"
## test ##
junit = "5.8.2"
kotest = "5.3.1"
mockk = "1.12.4"
## infra ##
factorio = "1.1.59"
factorio = "1.1.61"
confluent-kafkaConnect = "7.0.1"
grafana = "8.5.0"
prometheus = "v2.34.0"
## gradle plugins ##
gradleNodePlugin = "3.3.0" # https://github.com/node-gradle/gradle-node-plugin/releases
#########
[plugins]
#########
@ -89,8 +95,10 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "ko
kotlinx-serialization-properties = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-properties" }
kotlinx-serialization-protobuf = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-protobuf" }
kxs-charleskornKaml = { group = "com.charleskorn.kaml", name = "kaml", version.ref = "kxs-charleskornKaml" }
kxs-mamoeYamlkt = { group = "net.mamoe.yamlkt", name = "yamlkt", version.ref = "kxs-mamoeYamlkt" }
kxs-charleskornKaml = { group = "com.charleskorn.kaml", name = "kaml", version.ref = "kxs-charleskornKaml" }
kxs-mamoeYamlkt = { group = "net.mamoe.yamlkt", name = "yamlkt", version.ref = "kxs-mamoeYamlkt" }
#kxs-kxsTsGen = { group = "dev.adamko.kxstsgen", name = "kxs-ts-gen-core", version.ref = "kxs-kxsTsGen" }
kxs-kxsTsGen = { group = "com.github.adamko-dev.kotlinx-serialization-typescript-generator", name = "kxs-ts-gen-core", version.ref = "kxs-kxsTsGen" }
kotlinx-coroutines-bom = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-bom", version.ref = "kotlinx-coroutines" }

View file

@ -1,6 +1,9 @@
import dev.adamko.kafkatorio.gradle.asProvider
import dev.adamko.kafkatorio.gradle.typescriptAttributes
import dev.adamko.kafkatorio.task.GenerateTypeScriptTask
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@ -36,6 +39,7 @@ kotlin {
all {
languageSettings.apply {
optIn("kotlin.ExperimentalStdlibApi")
optIn("kotlin.ExperimentalUnsignedTypes")
optIn("kotlin.RequiresOptIn")
optIn("kotlin.js.ExperimentalJsExport")
optIn("kotlin.time.ExperimentalTime")
@ -51,7 +55,7 @@ kotlin {
implementation(project.dependencies.platform(libs.kotlinx.serialization.bom))
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)
api("dev.adamko.kxstsgen:kxs-ts-gen-core:0.1.3")
api(libs.kxs.kxsTsGen)
implementation(dependencies.platform(libs.okio.bom))
implementation(libs.okio.core)
@ -96,16 +100,34 @@ kotlin {
}
val kotlinJvmMainCompilation: NamedDomainObjectProvider<KotlinJvmCompilation> =
kotlin.jvm().compilations.named(KotlinCompilation.MAIN_COMPILATION_NAME)
val jvmJar: TaskProvider<Jar> = tasks.named<Jar>(kotlin.jvm().artifactsTaskName)
val mainCompilation: Provider<FileCollection> =
kotlin.jvm().compilations.named("main").map { it.runtimeDependencyFiles }
val kotlinMainRuntimeDependencies: Provider<FileCollection> =
kotlinJvmMainCompilation.map { it.runtimeDependencyFiles }
val kotlinMainCompileDependencies: Provider<FileCollection> =
kotlinJvmMainCompilation.map { it.compileDependencyFiles }
val kotlinMainCompileTask: Provider<KotlinCompile> =
kotlinJvmMainCompilation.map { it.compileKotlinTask }
val generateTypeScript by tasks.registering(GenerateTypeScriptTask::class) {
dependsOn(jvmJar)
// dependsOn(kotlinMainCompileTask)
// inputs.files(jvmJar.map { it.outputs.files })
// inputs.files(kotlinMainCompileTask.map { it.javaSources })
classpath(
jvmJar,
mainCompilation,
kotlinMainRuntimeDependencies,
// kotlinMainCompileTask.map { it.libraries }
// kotlinMainDependencies,
)
output.set(layout.buildDirectory.dir("generated/typescript"))
mainClass.set("dev.adamko.kafkatorio.events.schema.Kt2ts2Kt")
@ -122,16 +144,26 @@ val generateTypeScriptOutputFiles: Provider<FileTree> =
generateTypeScript.map { it.outputs.files.asFileTree }
val schemaTs by distributions.registering {
distributionBaseName.set(schemaTsDistributionName)
contents {
from(generateTypeScriptOutputFiles)
}
//val schemaTs by distributions.registering {
// distributionBaseName.set(schemaTsDistributionName)
// contents {
// from(generateTypeScriptOutputFiles)
// }
//}
//val schemaTsZipTask: TaskProvider<Zip> = tasks.named<Zip>("${schemaTs.name}DistZip")
//val schemaTsZipTaskArchiveFile: Provider<RegularFile> = schemaTsZipTask.flatMap { it.archiveFile }
val schemaTsZip by tasks.registering(Zip::class) {
group = "kt2ts"
archiveBaseName.set("schema-ts")
from(generateTypeScriptOutputFiles)
destinationDirectory.set(layout.buildDirectory.dir("distributions/kt2ts"))
}
val schemaTsZipTask: TaskProvider<Zip> = tasks.named<Zip>("${schemaTs.name}DistZip")
val schemaTsZipTaskArchiveFile: Provider<RegularFile> = schemaTsZipTask.flatMap { it.archiveFile }
val schemaTsZipTaskArchiveFile: Provider<RegularFile> = schemaTsZip.flatMap { it.archiveFile }
val typeScriptModelGenerated: Configuration by configurations.creating {
@ -139,4 +171,5 @@ val typeScriptModelGenerated: Configuration by configurations.creating {
typescriptAttributes(objects)
outgoing.artifact(schemaTsZipTaskArchiveFile)
// outgoing.artifact(schemaTsZipTaskArchiveFile)
}

View file

@ -5,6 +5,7 @@ import kotlin.math.roundToInt
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Red, green, blue and alpha values, all in range `[0, 1]` or all in range `[0, 255]` if any
* value is > 1.

View file

@ -0,0 +1,191 @@
package dev.adamko.kafkatorio.schema.common
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("kafkatorio.entity.FactorioEntityData")
sealed interface FactorioEntityData {
val protoId: PrototypeId
val status: Status?
val position: MapEntityPosition
/** Non-specific entity data */
@Serializable
@SerialName("kafkatorio.entity.FactorioEntityData.Standard")
data class Standard(
override val protoId: PrototypeId,
override val status: Status? = null,
override val position: MapEntityPosition,
val graphicsVariation: UByte? = null,
val health: Float? = null,
val isActive: Boolean? = null,
val isRotatable: Boolean? = null,
val lastUser: UInt? = null,
val localisedDescription: String? = null,
val localisedName: String? = null,
) : FactorioEntityData
@Serializable
@SerialName("kafkatorio.entity.FactorioEntityData.Resource")
data class Resource(
override val protoId: PrototypeId,
override val status: Status? = null,
override val position: MapEntityPosition,
val amount: UInt,
/** Count of initial resource units contained. `null` if resource is not infinite. */
val initialAmount: UInt? = null,
) : FactorioEntityData
@Serializable
@SerialName("kafkatorio.entity.FactorioEntityData.EntityStatus")
enum class Status {
CANT_DIVIDE_SEGMENTS,
CHARGING,
CLOSED_BY_CIRCUIT_NETWORK,
DISABLED,
DISABLED_BY_CONTROL_BEHAVIOR,
DISABLED_BY_SCRIPT,
DISCHARGING,
FLUID_INGREDIENT_SHORTAGE,
FULLY_CHARGED,
FULL_OUTPUT,
ITEM_INGREDIENT_SHORTAGE,
LAUNCHING_ROCKET,
LOW_INPUT_FLUID,
LOW_POWER,
LOW_TEMPERATURE,
MARKED_FOR_DECONSTRUCTION,
MISSING_REQUIRED_FLUID,
MISSING_SCIENCE_PACKS,
NETWORKS_CONNECTED,
NETWORKS_DISCONNECTED,
NORMAL,
NOT_CONNECTED_TO_RAIL,
NOT_PLUGGED_IN_ELECTRIC_NETWORK,
NO_AMMO,
NO_FUEL,
NO_INGREDIENTS,
NO_INPUT_FLUID,
NO_MINABLE_RESOURCES,
NO_MODULES_TO_TRANSMIT,
NO_POWER,
NO_RECIPE,
NO_RESEARCH_IN_PROGRESS,
OPENED_BY_CIRCUIT_NETWORK,
OUT_OF_LOGISTIC_NETWORK,
PREPARING_ROCKET_FOR_LAUNCH,
RECHARGING_AFTER_POWER_OUTAGE,
TURNED_OFF_DURING_DAYTIME,
WAITING_FOR_SOURCE_ITEMS,
WAITING_FOR_SPACE_IN_DESTINATION,
WAITING_FOR_TARGET_TO_BE_BUILT,
WAITING_FOR_TRAIN,
WAITING_TO_LAUNCH_ROCKET,
WORKING,
}
}
/*
enum entity_status {
working,
normal,
no_power,
low_power,
no_fuel,
disabled_by_control_behavior,
opened_by_circuit_network,
closed_by_circuit_network,
disabled_by_script,
marked_for_deconstruction,
not_plugged_in_electric_network,
networks_connected,
networks_disconnected,
charging,
discharging,
fully_charged,
out_of_logistic_network,
no_recipe,
no_ingredients,
no_input_fluid,
no_research_in_progress,
no_minable_resources,
low_input_fluid,
fluid_ingredient_shortage,
full_output,
item_ingredient_shortage,
missing_required_fluid,
missing_science_packs,
waiting_for_source_items,
waiting_for_space_in_destination,
preparing_rocket_for_launch,
waiting_to_launch_rocket,
launching_rocket,
no_modules_to_transmit,
recharging_after_power_outage,
waiting_for_target_to_be_built,
waiting_for_train,
no_ammo,
low_temperature,
disabled,
turned_off_during_daytime,
not_connected_to_rail,
cant_divide_segments,
}
*/
/*
AssemblingMachine
Beam
Car
Character
CharacterCorpse
CraftingMachine
ElectricEnergyInterface
ElectricPole
EntityGhost
EntityWithHealth
EntityWithOwner
FlyingText
Furnace
Gate
Generator
Ghost
HeatInterface
HighlightBox
InfinityContainer
InfinityPipe
Inserter
ItemEntity
LandMine
LinkedBelt
Loader
Market
MiningDrill
ProgrammableSpeaker
Pump
Radar
Rail
RailChainSignal
RailSignal
Reactor
ResourceEntity
RocketSilo
SmokeWithTrigger
SpiderVehicle
Splitter
TrainStop
TransportBeltConnectable
TransportBeltToGround
Turret
Unit
Vehicle
*/

View file

@ -1,10 +1,12 @@
package dev.adamko.kafkatorio.schema.common
import kotlin.jvm.JvmInline
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("kafkatorio.id.Tick")
@JvmInline
value class Tick(val value: UInt)
@ -17,6 +19,7 @@ val UInt.tick
* See [`LuaSurface.index`](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.index)
*/
@Serializable
@SerialName("kafkatorio.id.SurfaceIndex")
@JvmInline
value class SurfaceIndex(private val id: UInt) {
override fun toString(): String = "$id"
@ -24,6 +27,7 @@ value class SurfaceIndex(private val id: UInt) {
@Serializable
@SerialName("kafkatorio.id.ForceIndex")
@JvmInline
value class ForceIndex(private val id: UInt) {
override fun toString(): String = "$id"
@ -32,6 +36,7 @@ value class ForceIndex(private val id: UInt) {
/** The player's index in `LuaGameScript::players` */
@Serializable
@SerialName("kafkatorio.id.PlayerIndex")
@JvmInline
value class PlayerIndex(private val id: UInt) {
override fun toString(): String = "$id"
@ -40,6 +45,7 @@ value class PlayerIndex(private val id: UInt) {
/** > This is universally unique for every entity that has one, for the lifetime of a whole game. */
@Serializable
@SerialName("kafkatorio.id.UnitNumber")
@JvmInline
value class UnitNumber(private val id: UInt) {
override fun toString(): String = "$id"
@ -47,13 +53,7 @@ value class UnitNumber(private val id: UInt) {
@Serializable
@JvmInline
value class PrototypeName(private val id: String) {
override fun toString(): String = id
}
@Serializable
@SerialName("kafkatorio.id.EventName")
@JvmInline
value class EventName(private val name: String) {
override fun toString(): String = name
@ -62,23 +62,23 @@ value class EventName(private val name: String) {
interface EntityIdentifiers {
val unitNumber: UnitNumber?
val name: String
/** The prototype-type of the entity. */
val protoType: String
/** The 'prototype type' and 'prototype name' of the entity. */
val protoId: PrototypeId
}
// instant of EntityIdentifiers, because kxs needs a concrete instance
@Serializable
@SerialName("kafkatorio.id.EntityIdentifiersData")
data class EntityIdentifiersData(
override val unitNumber: UnitNumber? = null,
override val name: String,
override val protoType: String,
override val protoId: PrototypeId,
) : EntityIdentifiers
@JvmInline
@Serializable
@SerialName("kafkatorio.id.FactorioServerId")
@JvmInline
value class FactorioServerId(private val id: String) {
override fun toString() = id
}

View file

@ -8,13 +8,14 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("kafkatorio.prototype.FactorioPrototype")
sealed interface FactorioPrototype {
val name: PrototypeName
val protoId: PrototypeId
val mapColour: Colour
@SerialName("kafkatorio.prototype.MapTile")
@Serializable
data class MapTile(
override val name: PrototypeName,
override val protoId: PrototypeId,
override val mapColour: Colour,
val layer: UInt,
@ -29,7 +30,7 @@ sealed interface FactorioPrototype {
@SerialName("kafkatorio.prototype.Entity")
@Serializable
data class Entity(
override val name: PrototypeName,
override val protoId: PrototypeId,
override val mapColour: Colour,
val protoType: String,

View file

@ -12,16 +12,16 @@ const val MAP_CHUNK_SIZE = 32
data class MapTile(
val x: Int,
val y: Int,
val proto: PrototypeName,
val protoId: PrototypeId,
)
/**
* 2D map of x/y tile positions and prototype-name, optimised for JSON size.
*
* [protos] is a one-to-one map between a [PrototypeName] and [PrototypeKey].
* [protos] is a one-to-one map between a prototype[PrototypeId] and [PrototypeKey].
*
* [PrototypeKey] is an arbitrary integer value, which maps to one [PrototypeName] only for a
* [PrototypeKey] is an arbitrary integer value, which maps to one [PrototypeId] only for a
* specific instance of [MapTileDictionary].
*
* Each X/Y coordinate in [tilesXY] maps to a [PrototypeKey].
@ -30,9 +30,10 @@ data class MapTile(
data class MapTileDictionary(
/** Map an X,Y coordinate a prototype name */
val tilesXY: Map<String, Map<String, PrototypeKey>>,
val protos: Map<PrototypeName, PrototypeKey>,
val protos: Map<PrototypeId, PrototypeKey>,
) {
private val protosIndexToName: Map<PrototypeKey, PrototypeName> by lazy {
private val protosIndexToId: Map<PrototypeKey, PrototypeId> by lazy {
protos.entries.associate { (name, key) -> key to name }
}
@ -41,11 +42,11 @@ data class MapTileDictionary(
row.forEach { (yString, protoIndex) ->
val x = xString.toIntOrNull()
val y = yString.toIntOrNull()
val protoName = protosIndexToName[protoIndex]
val protoName = protosIndexToId[protoIndex]
if (x != null && y != null && protoName != null) {
add(MapTile(x, y, protoName))
} else if (protoName == null) {
println("warning: no prototype name found for index:$protoIndex. $protosIndexToName")
println("warning: no prototype name found for index:$protoIndex. $protosIndexToId")
}
}
}

View file

@ -0,0 +1,817 @@
package dev.adamko.kafkatorio.schema.common
import kotlin.jvm.JvmInline
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Both a [name] and [type] are required to unique identify a [prototype][FactorioPrototype].
*
*
* @property[name] The name of the prototype, which is unique per specific Prototype [type]
*/
// * Each subclass of [PrototypeId] is equivalent to a Prototype `type`.
//sealed interface PrototypeId {
//
// val name: String
//
// @Serializable
// @JvmInline
// @SerialName("kafkatorio.prototype.id.PrototypeIdMapTile")
// value class MapTile(override val name: String) : PrototypeId {
// override fun toString(): String = name
// }
//
// @Serializable
// @JvmInline
// @SerialName("kafkatorio.prototype.id.PrototypeIdEntity")
// value class Entity(override val name: String) : PrototypeId {
// override fun toString(): String = name
// }
//
// @Serializable
// @JvmInline
// @SerialName("kafkatorio.prototype.id.PrototypeIdResourceEntity")
// value class ResourceEntity(override val name: String) : PrototypeId {
// override fun toString(): String = name
// }
//
// @Serializable
// @JvmInline
// @SerialName("kafkatorio.prototype.id.PrototypeIdFluid")
// value class Fluid(override val name: String) : PrototypeId {
// override fun toString(): String = name
// }
//
// @Serializable
// @JvmInline
// @SerialName("kafkatorio.prototype.id.PrototypeIdItem")
// value class Item(override val name: String) : PrototypeId {
// override fun toString(): String = name
// }
//
//}
@Serializable
@JvmInline
@SerialName("kafkatorio.id.PrototypeId")
value class PrototypeId private constructor(
private val typeNameConcat: String,
) {
val type: Type get() = Type(typeNameConcat.substringBefore(delim, ""))
val name: Name get() = Name(typeNameConcat.substringAfter(delim, ""))
operator fun component1(): Type = type
operator fun component2(): Name = name
constructor(
type: String,
name: String,
) : this("${type}${delim}${name}")
/** Denotes the class of the Prototype */
@Serializable
@JvmInline
@SerialName("kafkatorio.id.PrototypeIdType")
value class Type(private val id: String) {
override fun toString(): String = id
}
/** A specific implementation of a [type]. Unique per prototype 'super class' */
@Serializable
@JvmInline
@SerialName("kafkatorio.id.PrototypeIdName")
value class Name(private val name: String) {
override fun toString(): String = name
}
companion object {
private const val delim = '/'
}
}
//@Suppress("unused")
//enum class PrototypeTypeId(
// val id: String
//) {
// AmbientSound("ambient-sound"),
// Animation("animation"),
// EditorController("editor-controller"),
// Font("font"),
// GodController("god-controller"),
// MapGenPresets("map-gen-presets"),
// MapSettings("map-settings"),
// MouseCursor("mouse-cursor"),
// Sound("sound"),
// SpectatorController("spectator-controller"),
// Sprite("sprite"),
// TileEffect("tile-effect"),
// TipsAndTricksItemCategory("tips-and-tricks-item-category"),
// TriggerTargetType("trigger-target-type"),
// WindSound("wind-sound"),
// Achievement("achievement"),
// BuildEntityAchievement("build-entity-achievement"),
// CombatRobotCountAchievement("combat-robot-count"),
// ConstructWithRobotsAchievement("construct-with-robots-achievement"),
// DeconstructWithRobotsAchievement("deconstruct-with-robots-achievement"),
// DeliverByRobotsAchievement("deliver-by-robots-achievement"),
// DontBuildEntityAchievement("dont-build-entity-achievement"),
// DontCraftManuallyAchievement("dont-craft-manually-achievement"),
// DontUseEntityInEnergyProductionAchievement("dont-use-entity-in-energy-production-achievement"),
// FinishTheGameAchievement("finish-the-game-achievement"),
// GroupAttackAchievement("group-attack-achievement"),
// KillAchievement("kill-achievement"),
// PlayerDamagedAchievement("player-damaged-achievement"),
// ProduceAchievement("produce-achievement"),
// ProducePerHourAchievement("produce-per-hour-achievement"),
// ResearchAchievement("research-achievement"),
// TrainPathAchievement("train-path-achievement"),
// AmmoCategory("ammo-category"),
// AutoplaceControl("autoplace-control"),
// CustomInput("custom-input"),
// DamageType("damage-type"),
// Decorative("optimized-decorative"),
// Entity("<abstract>"),
// Arrow("arrow"),
// ArtilleryFlare("artillery-flare"),
// ArtilleryProjectile("artillery-projectile"),
// Beam("beam"),
// CharacterCorpse("character-corpse"),
// Cliff("cliff"),
// Corpse("corpse"),
// RailRemnants("rail-remnants"),
// DeconstructibleTileProxy("deconstructible-tile-proxy"),
// EntityGhost("entity-ghost"),
// /** for migration, cannot be used */
// EntityParticle("particle"),
// /** for migration, cannot be used */
// LeafParticle("leaf-particle"),
// EntityWithHealth("<abstract>"),
// EntityWithOwner("<abstract>"),
// Accumulator("accumulator"),
// ArtilleryTurret("artillery-turret"),
// Beacon("beacon"),
// Boiler("boiler"),
// BurnerGenerator("burner-generator"),
// Character("character"),
// Combinator("<abstract>"),
// ArithmeticCombinator("arithmetic-combinator"),
// DeciderCombinator("decider-combinator"),
// ConstantCombinator("constant-combinator"),
// Container("container"),
// LogisticContainer("logistic-container"),
// InfinityContainer("infinity-container"),
// CraftingMachine("<abstract>"),
// AssemblingMachine("assembling-machine"),
// RocketSilo("rocket-silo"),
// Furnace("furnace"),
// ElectricEnergyInterface("electric-energy-interface"),
// ElectricPole("electric-pole"),
// EnemySpawner("unit-spawner"),
// FlyingRobot("<abstract>"),
// CombatRobot("combat-robot"),
// RobotWithLogisticInterface("<abstract>"),
// ConstructionRobot("construction-robot"),
// LogisticRobot("logistic-robot"),
// Gate("gate"),
// Generator("generator"),
// HeatInterface("heat-interface"),
// HeatPipe("heat-pipe"),
// Inserter("inserter"),
// Lab("lab"),
// Lamp("lamp"),
// LandMine("land-mine"),
// LinkedContainer("linked-container"),
// Market("market"),
// MiningDrill("mining-drill"),
// OffshorePump("offshore-pump"),
// Pipe("pipe"),
// InfinityPipe("infinity-pipe"),
// PipeToGround("pipe-to-ground"),
// PlayerPort("player-port"),
// PowerSwitch("power-switch"),
// ProgrammableSpeaker("programmable-speaker"),
// Pump("pump"),
// Radar("radar"),
// Rail("<abstract>"),
// CurvedRail("curved-rail"),
// StraightRail("straight-rail"),
// RailSignalBase("<abstract>"),
// RailChainSignal("rail-chain-signal"),
// RailSignal("rail-signal"),
// Reactor("reactor"),
// Roboport("roboport"),
// SimpleEntityWithOwner("simple-entity-with-owner"),
// SimpleEntityWithForce("simple-entity-with-force"),
// SolarPanel("solar-panel"),
// StorageTank("storage-tank"),
// TrainStop("train-stop"),
// TransportBeltConnectable("<abstract>"),
// LinkedBelt("linked-belt"),
// Loader1x1("loader-1x1"),
// Loader1x2("loader"),
// Splitter("splitter"),
// TransportBelt("transport-belt"),
// UndergroundBelt("underground-belt"),
// Turret("turret"),
// AmmoTurret("ammo-turret"),
// ElectricTurret("electric-turret"),
// FluidTurret("fluid-turret"),
// Unit("unit"),
// Vehicle("<abstract>"),
// Car("car"),
// RollingStock("<abstract>"),
// ArtilleryWagon("artillery-wagon"),
// CargoWagon("cargo-wagon"),
// FluidWagon("fluid-wagon"),
// Locomotive("locomotive"),
// SpiderVehicle("spider-vehicle"),
// Wall("wall"),
// Fish("fish"),
// SimpleEntity("simple-entity"),
// SpiderLeg("spider-leg"),
// Tree("tree"),
// Explosion("explosion"),
// FlameThrowerExplosion("flame-thrower-explosion"),
// FireFlame("fire"),
// FluidStream("stream"),
// FlyingText("flying-text"),
// HighlightBoxEntity("highlight-box"),
// ItemEntity("item-entity"),
// ItemRequestProxy("item-request-proxy"),
// ParticleSource("particle-source"),
// Projectile("projectile"),
// ResourceEntity("resource"),
// RocketSiloRocket("rocket-silo-rocket"),
// RocketSiloRocketShadow("rocket-silo-rocket-shadow"),
// Smoke("<abstract>"),
// /** for migration, cannot be used */
// SimpleSmoke("smoke"),
// SmokeWithTrigger("smoke-with-trigger"),
// SpeechBubble("speech-bubble"),
// Sticker("sticker"),
// TileGhost("tile-ghost"),
// Equipment("<abstract>"),
// ActiveDefenseEquipment("active-defense-equipment"),
// BatteryEquipment("battery-equipment"),
// BeltImmunityEquipment("belt-immunity-equipment"),
// EnergyShieldEquipment("energy-shield-equipment"),
// GeneratorEquipment("generator-equipment"),
// MovementBonusEquipment("movement-bonus-equipment"),
// NightVisionEquipment("night-vision-equipment"),
// RoboportEquipment("roboport-equipment"),
// SolarPanelEquipment("solar-panel-equipment"),
// EquipmentCategory("equipment-category"),
// EquipmentGrid("equipment-grid"),
// Fluid("fluid"),
// FuelCategory("fuel-category"),
// GuiStyle("gui-style"),
// Item("item"),
// AmmoItem("ammo"),
// Capsule("capsule"),
// Gun("gun"),
// ItemWithEntityData("item-with-entity-data"),
// ItemWithLabel("item-with-label"),
// ItemWithInventory("item-with-inventory"),
// BlueprintBook("blueprint-book"),
// ItemWithTags("item-with-tags"),
// SelectionTool("selection-tool"),
// BlueprintItem("blueprint"),
// CopyPasteTool("copy-paste-tool"),
// DeconstructionItem("deconstruction-item"),
// UpgradeItem("upgrade-item"),
// Module("module"),
// RailPlanner("rail-planner"),
// SpidertronRemote("spidertron-remote"),
// Tool("tool"),
// Armor("armor"),
// /** for migration, cannot be used */
// MiningTool("mining-tool"),
// RepairTool("repair-tool"),
// ItemGroup("item-group"),
// ItemSubGroup("item-subgroup"),
// ModuleCategory("module-category"),
// NamedNoiseExpression("noise-expression"),
// NoiseLayer("noise-layer"),
// Particle("optimized-particle"),
// Recipe("recipe"),
// RecipeCategory("recipe-category"),
// ResourceCategory("resource-category"),
// Shortcut("shortcut"),
// Technology("technology"),
// Tile("tile"),
// TipsAndTricksItem("tips-and-tricks-item"),
// TrivialSmoke("trivial-smoke"),
// Tutorial("tutorial"),
// UtilityConstants("utility-constants"),
// UtilitySounds("utility-sounds"),
// UtilitySprites("utility-sprites"),
// VirtualSignal("virtual-signal"),
// ;
//
// fun named(protoName: String): PrototypeId = PrototypeId(this, protoName)
//
// companion object {
// val entries: Set<PrototypeTypeId> = values().toSet()
// }
//
//}
//@Suppress("unused")
//sealed class PrototypeType(
// val id: String
//) {
//
// object AmbientSound : PrototypeType("ambient-sound")
// object Animation : PrototypeType("animation")
// object EditorController : PrototypeType("editor-controller")
// object Font : PrototypeType("font")
// object GodController : PrototypeType("god-controller")
// object MapGenPresets : PrototypeType("map-gen-presets")
// object MapSettings : PrototypeType("map-settings")
// object MouseCursor : PrototypeType("mouse-cursor")
// object Sound : PrototypeType("sound")
// object SpectatorController : PrototypeType("spectator-controller")
// object Sprite : PrototypeType("sprite")
// object TileEffect : PrototypeType("tile-effect")
// object TipsAndTricksItemCategory : PrototypeType("tips-and-tricks-item-category")
// object TriggerTargetType : PrototypeType("trigger-target-type")
// object WindSound : PrototypeType("wind-sound")
//
//
// object Achievement : PrototypeType("achievement")
// object BuildEntityAchievement : PrototypeType("build-entity-achievement")
// object CombatRobotCountAchievement : PrototypeType("combat-robot-count")
// object ConstructWithRobotsAchievement : PrototypeType("construct-with-robots-achievement")
// object DeconstructWithRobotsAchievement : PrototypeType("deconstruct-with-robots-achievement")
// object DeliverByRobotsAchievement : PrototypeType("deliver-by-robots-achievement")
// object DontBuildEntityAchievement : PrototypeType("dont-build-entity-achievement")
// object DontCraftManuallyAchievement : PrototypeType("dont-craft-manually-achievement")
// object DontUseEntityInEnergyProductionAchievement : PrototypeType("dont-use-entity-in-energy-production-achievement")
//
//
// object FinishTheGameAchievement : PrototypeType("finish-the-game-achievement")
// object GroupAttackAchievement : PrototypeType("group-attack-achievement")
// object KillAchievement : PrototypeType("kill-achievement")
// object PlayerDamagedAchievement : PrototypeType("player-damaged-achievement")
// object ProduceAchievement : PrototypeType("produce-achievement")
// object ProducePerHourAchievement : PrototypeType("produce-per-hour-achievement")
// object ResearchAchievement : PrototypeType("research-achievement")
// object TrainPathAchievement : PrototypeType("train-path-achievement")
// object AmmoCategory : PrototypeType("ammo-category")
// object AutoplaceControl : PrototypeType("autoplace-control")
// object CustomInput : PrototypeType("custom-input")
// object DamageType : PrototypeType("damage-type")
// object Decorative : PrototypeType("optimized-decorative")
//
//
// object Entity : PrototypeType("stract>")
// object Arrow : PrototypeType("arrow")
// object ArtilleryFlare : PrototypeType("artillery-flare")
// object ArtilleryProjectile : PrototypeType("artillery-projectile")
// object Beam : PrototypeType("beam")
// object CharacterCorpse : PrototypeType("character-corpse")
// object Cliff : PrototypeType("cliff")
// object Corpse : PrototypeType("corpse")
// object RailRemnants : PrototypeType("rail-remnants")
// object DeconstructibleTileProxy : PrototypeType("deconstructible-tile-proxy")
// object EntityGhost : PrototypeType("entity-ghost")
// /** for migration, cannot be used */
// object EntityParticle : PrototypeType("particle")
// /** for migration, cannot be used */
// object LeafParticle : PrototypeType("leaf-particle")
//
//
// object EntityWithOwner : PrototypeType("stract>")
// object Accumulator : PrototypeType("accumulator")
// object ArtilleryTurret : PrototypeType("artillery-turret")
// object Beacon : PrototypeType("beacon")
// object Boiler : PrototypeType("boiler")
// object BurnerGenerator : PrototypeType("burner-generator")
// object Character : PrototypeType("character")
//
//
// object Combinator : PrototypeType("stract>")
// object ArithmeticCombinator : PrototypeType("arithmetic-combinator")
// object DeciderCombinator : PrototypeType("decider-combinator")
// object ConstantCombinator : PrototypeType("constant-combinator")
// object Container : PrototypeType("container")
// object LogisticContainer : PrototypeType("logistic-container")
// object InfinityContainer : PrototypeType("infinity-container")
//
//
// object CraftingMachine : PrototypeType("stract>")
// object AssemblingMachine : PrototypeType("assembling-machine")
// object RocketSilo : PrototypeType("rocket-silo")
// object Furnace : PrototypeType("furnace")
// object ElectricEnergyInterface : PrototypeType("electric-energy-interface")
// object ElectricPole : PrototypeType("electric-pole")
// object EnemySpawner : PrototypeType("unit-spawner")
//
//
// object FlyingRobot : PrototypeType("stract>")
// object CombatRobot : PrototypeType("combat-robot") , PrototypeAbstract.FlyingRobot
//
//
// object RobotWithLogisticInterface : PrototypeType("stract>")
// object ConstructionRobot : PrototypeType("construction-robot")
// object LogisticRobot : PrototypeType("logistic-robot")
// object Gate : PrototypeType("gate")
// object Generator : PrototypeType("generator")
// object HeatInterface : PrototypeType("heat-interface")
// object HeatPipe : PrototypeType("heat-pipe")
// object Inserter : PrototypeType("inserter")
// object Lab : PrototypeType("lab")
// object Lamp : PrototypeType("lamp")
// object LandMine : PrototypeType("land-mine")
// object LinkedContainer : PrototypeType("linked-container")
// object Market : PrototypeType("market")
// object MiningDrill : PrototypeType("mining-drill")
// object OffshorePump : PrototypeType("offshore-pump")
// object Pipe : PrototypeType("pipe")
// object InfinityPipe : PrototypeType("infinity-pipe")
// object PipeToGround : PrototypeType("pipe-to-ground")
// object PlayerPort : PrototypeType("player-port")
// object PowerSwitch : PrototypeType("power-switch")
// object ProgrammableSpeaker : PrototypeType("programmable-speaker")
// object Pump : PrototypeType("pump")
// object Radar : PrototypeType("radar")
//
// object CurvedRail : PrototypeType("curved-rail"), PrototypeAbstract.Rail
// object StraightRail : PrototypeType("straight-rail"), PrototypeAbstract.Rail
//
// object RailSignalBase : PrototypeType("stract>")
// object RailChainSignal : PrototypeType("rail-chain-signal")
// object RailSignal : PrototypeType("rail-signal")
// object Reactor : PrototypeType("reactor")
// object Roboport : PrototypeType("roboport")
// object SimpleEntityWithOwner : PrototypeType("simple-entity-with-owner")
// object SimpleEntityWithForce : PrototypeType("simple-entity-with-force")
// object SolarPanel : PrototypeType("solar-panel")
// object StorageTank : PrototypeType("storage-tank")
// object TrainStop : PrototypeType("train-stop")
//
//
// object TransportBeltConnectable : PrototypeType("stract>")
// object LinkedBelt : PrototypeType("linked-belt")
// object Loader1x1 : PrototypeType("loader-1x1")
// object Loader1x2 : PrototypeType("loader")
// object Splitter : PrototypeType("splitter")
// object TransportBelt : PrototypeType("transport-belt")
// object UndergroundBelt : PrototypeType("underground-belt")
// object Turret : PrototypeType("turret")
// object AmmoTurret : PrototypeType("ammo-turret")
// object ElectricTurret : PrototypeType("electric-turret")
// object FluidTurret : PrototypeType("fluid-turret")
// object Unit : PrototypeType("unit")
//
//
// object Car : PrototypeType("car>"), PrototypeAbstract.Vehicle
// object SpiderVehicle : PrototypeType("spider-vehicle"), PrototypeAbstract.Vehicle
//
//
// object ArtilleryWagon : PrototypeType("artillery-wagon"), PrototypeAbstract.RollingStock
// object CargoWagon : PrototypeType("cargo-wagon"), PrototypeAbstract.RollingStock
// object FluidWagon : PrototypeType("fluid-wagon"), PrototypeAbstract.RollingStock
// object Locomotive : PrototypeType("locomotive"), PrototypeAbstract.RollingStock
//
// object Wall : PrototypeType("wall")
//
//
// object Fish : PrototypeType("fish"), PrototypeAbstract.EntityWithHealth
// object SimpleEntity : PrototypeType("simple-entity"), PrototypeAbstract.EntityWithHealth
// object SpiderLeg : PrototypeType("spider-leg"), PrototypeAbstract.EntityWithHealth
// object Tree : PrototypeType("tree"), PrototypeAbstract.EntityWithHealth
//
//
// object Explosion : PrototypeType("explosion"),
// object FlameThrowerExplosion : PrototypeType("flame-thrower-explosion"),
// object FireFlame : PrototypeType("fire"),
// object FluidStream : PrototypeType("stream"),
// object FlyingText : PrototypeType("flying-text"),
// object HighlightBoxEntity : PrototypeType("highlight-box"),
// object ItemEntity : PrototypeType("item-entity"),
// object ItemRequestProxy : PrototypeType("item-request-proxy"),
// object ParticleSource : PrototypeType("particle-source"),
// object Projectile : PrototypeType("projectile"),
// object ResourceEntity : PrototypeType("resource"),
// object RocketSiloRocket : PrototypeType("rocket-silo-rocket"),
// object RocketSiloRocketShadow : PrototypeType("rocket-silo-rocket-shadow"),
//
// /** for migration, cannot be used */
// object SimpleSmoke : PrototypeType("smoke"), PrototypeAbstract.Smoke
// object SmokeWithTrigger : PrototypeType("smoke-with-trigger"), PrototypeAbstract.Smoke
// object SpeechBubble : PrototypeType("speech-bubble"), PrototypeAbstract.Smoke
// object Sticker : PrototypeType("sticker"), PrototypeAbstract.Smoke
// object TileGhost : PrototypeType("tile-ghost"), PrototypeAbstract.Smoke
//
// object Equipment : PrototypeType("abstract>")
// object ActiveDefenseEquipment : PrototypeType("active-defense-equipment")
// object BatteryEquipment : PrototypeType("battery-equipment")
// object BeltImmunityEquipment : PrototypeType("belt-immunity-equipment")
// object EnergyShieldEquipment : PrototypeType("energy-shield-equipment")
// object GeneratorEquipment : PrototypeType("generator-equipment")
// object MovementBonusEquipment : PrototypeType("movement-bonus-equipment")
// object NightVisionEquipment : PrototypeType("night-vision-equipment")
// object RoboportEquipment : PrototypeType("roboport-equipment")
// object SolarPanelEquipment : PrototypeType("solar-panel-equipment")
// object EquipmentCategory : PrototypeType("equipment-category")
// object EquipmentGrid : PrototypeType("equipment-grid")
// object Fluid : PrototypeType("fluid")
// object FuelCategory : PrototypeType("fuel-category")
// object GuiStyle : PrototypeType("gui-style")
// object Item : PrototypeType("item")
// object AmmoItem : PrototypeType("ammo")
// object Capsule : PrototypeType("capsule")
// object Gun : PrototypeType("gun")
// object ItemWithEntityData : PrototypeType("item-with-entity-data")
// object ItemWithLabel : PrototypeType("item-with-label")
// object ItemWithInventory : PrototypeType("item-with-inventory")
// object BlueprintBook : PrototypeType("blueprint-book")
// object ItemWithTags : PrototypeType("item-with-tags")
// object SelectionTool : PrototypeType("selection-tool")
// object BlueprintItem : PrototypeType("blueprint")
// object CopyPasteTool : PrototypeType("copy-paste-tool")
// object DeconstructionItem : PrototypeType("deconstruction-item")
// object UpgradeItem : PrototypeType("upgrade-item")
// object Module : PrototypeType("module")
// object RailPlanner : PrototypeType("rail-planner")
// object SpidertronRemote : PrototypeType("spidertron-remote")
// object Tool : PrototypeType("tool")
// object Armor : PrototypeType("armor")
// /** for migration, cannot be used */
// object MiningTool : PrototypeType("mining-tool")
// object RepairTool : PrototypeType("repair-tool")
// object ItemGroup : PrototypeType("item-group")
// object ItemSubGroup : PrototypeType("item-subgroup")
// object ModuleCategory : PrototypeType("module-category")
// object NamedNoiseExpression : PrototypeType("noise-expression")
// object NoiseLayer : PrototypeType("noise-layer")
// object Particle : PrototypeType("optimized-particle")
// object Recipe : PrototypeType("recipe")
// object RecipeCategory : PrototypeType("recipe-category")
// object ResourceCategory : PrototypeType("resource-category")
// object Shortcut : PrototypeType("shortcut")
// object Technology : PrototypeType("technology")
// object Tile : PrototypeType("tile")
// object TipsAndTricksItem : PrototypeType("tips-and-tricks-item")
// object TrivialSmoke : PrototypeType("trivial-smoke")
// object Tutorial : PrototypeType("tutorial")
// object UtilityConstants : PrototypeType("utility-constants")
// object UtilitySounds : PrototypeType("utility-sounds")
// object UtilitySprites : PrototypeType("utility-sprites")
// object VirtualSignal : PrototypeType("virtual-signal")
//
//}
//
//@Suppress("unused")
//sealed interface PrototypeAbstract {
// //@formatter:off
// sealed interface PrototypeBase : PrototypeAbstract
// sealed interface Entity : PrototypeAbstract
// sealed interface EntityWithHealth : Entity
//
// sealed interface EntityWithOwner : EntityWithHealth
//
// sealed interface Combinator : EntityWithOwner
// sealed interface CraftingMachine : EntityWithOwner
// sealed interface FlyingRobot : EntityWithOwner
//
// sealed interface RobotWithLogisticInterface : FlyingRobot
//
// sealed interface Rail : EntityWithOwner
//
// sealed interface RailSignalBase : EntityWithOwner
// sealed interface TransportBeltConnectable : EntityWithOwner
// sealed interface Vehicle : EntityWithOwner
// sealed interface RollingStock : Vehicle
// sealed interface Smoke : Entity
// sealed interface Equipment : PrototypeAbstract
// //@formatter:on
//}
/*
* [[Prototype/AmbientSound]] '''ambient-sound'''
* [[Prototype/Animation]] '''animation'''
* [[Prototype/EditorController]] '''editor-controller'''
* [[Prototype/Font]] '''font'''
* [[Prototype/GodController]] '''god-controller'''
* [[Prototype/MapGenPresets]] '''map-gen-presets'''
* [[Prototype/MapSettings]] '''map-settings'''
* [[Prototype/MouseCursor]] '''mouse-cursor'''
* [[Prototype/Sound]] '''sound'''
* [[Prototype/SpectatorController]] '''spectator-controller'''
* [[Prototype/Sprite]] '''sprite'''
* [[Prototype/TileEffect]] '''tile-effect'''
* [[Prototype/TipsAndTricksItemCategory]] '''tips-and-tricks-item-category'''
* [[Prototype/TriggerTargetType]] '''trigger-target-type'''
* [[Prototype/WindSound]] '''wind-sound'''
* [[PrototypeBase]] <abstract>
</div><div class="prototype-tree">
* [[Prototype/Achievement]] '''achievement'''
** [[Prototype/BuildEntityAchievement]] '''build-entity-achievement'''
** [[Prototype/CombatRobotCountAchievement]] '''combat-robot-count'''
** [[Prototype/ConstructWithRobotsAchievement]] '''construct-with-robots-achievement'''
** [[Prototype/DeconstructWithRobotsAchievement]] '''deconstruct-with-robots-achievement'''
** [[Prototype/DeliverByRobotsAchievement]] '''deliver-by-robots-achievement'''
** [[Prototype/DontBuildEntityAchievement]] '''dont-build-entity-achievement'''
** [[Prototype/DontCraftManuallyAchievement]] '''dont-craft-manually-achievement'''
** [[Prototype/DontUseEntityInEnergyProductionAchievement]] '''dont-use-entity-in-energy-production-achievement'''
** [[Prototype/FinishTheGameAchievement]] '''finish-the-game-achievement'''
** [[Prototype/GroupAttackAchievement]] '''group-attack-achievement'''
** [[Prototype/KillAchievement]] '''kill-achievement'''
** [[Prototype/PlayerDamagedAchievement]] '''player-damaged-achievement'''
** [[Prototype/ProduceAchievement]] '''produce-achievement'''
** [[Prototype/ProducePerHourAchievement]] '''produce-per-hour-achievement'''
** [[Prototype/ResearchAchievement]] '''research-achievement'''
** [[Prototype/TrainPathAchievement]] '''train-path-achievement'''
* [[Prototype/AmmoCategory]] '''ammo-category'''
* [[Prototype/AutoplaceControl]] '''autoplace-control'''
* [[Prototype/CustomInput]] '''custom-input'''
* [[Prototype/DamageType]] '''damage-type'''
* [[Prototype/Decorative]] '''optimized-decorative'''
* [[Prototype/Entity]] <abstract>
** [[Prototype/Arrow]] '''arrow'''
** [[Prototype/ArtilleryFlare]] '''artillery-flare'''
** [[Prototype/ArtilleryProjectile]] '''artillery-projectile'''
** [[Prototype/Beam]] '''beam'''
** [[Prototype/CharacterCorpse]] '''character-corpse'''
** [[Prototype/Cliff]] '''cliff'''
** [[Prototype/Corpse]] '''corpse'''
*** [[Prototype/RailRemnants]] '''rail-remnants'''
** [[Prototype/DeconstructibleTileProxy]] '''deconstructible-tile-proxy'''
** [[Prototype/EntityGhost]] '''entity-ghost'''
** [[Prototype/EntityParticle]] '''particle''' (for migration, cannot be used)
*** [[Prototype/LeafParticle]] '''leaf-particle''' (for migration, cannot be used)
** [[Prototype/EntityWithHealth]] <abstract>
*** [[Prototype/EntityWithOwner]] <abstract>
**** [[Prototype/Accumulator]] '''accumulator'''
**** [[Prototype/ArtilleryTurret]] '''artillery-turret'''
**** [[Prototype/Beacon]] '''beacon'''
**** [[Prototype/Boiler]] '''boiler'''
**** [[Prototype/BurnerGenerator]] '''burner-generator'''
**** [[Prototype/Character]] '''character'''
**** [[Prototype/Combinator]] <abstract>
***** [[Prototype/ArithmeticCombinator]] '''arithmetic-combinator'''
***** [[Prototype/DeciderCombinator]] '''decider-combinator'''
**** [[Prototype/ConstantCombinator]] '''constant-combinator'''
**** [[Prototype/Container]] '''container'''
***** [[Prototype/LogisticContainer]] '''logistic-container'''
****** [[Prototype/InfinityContainer]] '''infinity-container'''
**** [[Prototype/CraftingMachine]] <abstract>
***** [[Prototype/AssemblingMachine]] '''assembling-machine'''
****** [[Prototype/RocketSilo]] '''rocket-silo'''
***** [[Prototype/Furnace]] '''furnace'''
**** [[Prototype/ElectricEnergyInterface]] '''electric-energy-interface'''
**** [[Prototype/ElectricPole]] '''electric-pole'''
**** [[Prototype/EnemySpawner]] '''unit-spawner'''
**** [[Prototype/FlyingRobot]] <abstract>
***** [[Prototype/CombatRobot]] '''combat-robot'''
***** [[Prototype/RobotWithLogisticInterface]] <abstract>
****** [[Prototype/ConstructionRobot]] '''construction-robot'''
****** [[Prototype/LogisticRobot]] '''logistic-robot'''
**** [[Prototype/Gate]] '''gate'''
**** [[Prototype/Generator]] '''generator'''
**** [[Prototype/HeatInterface]] '''heat-interface'''
**** [[Prototype/HeatPipe]] '''heat-pipe'''
**** [[Prototype/Inserter]] '''inserter'''
**** [[Prototype/Lab]] '''lab'''
**** [[Prototype/Lamp]] '''lamp'''
**** [[Prototype/LandMine]] '''land-mine'''
**** [[Prototype/LinkedContainer]] '''linked-container'''
**** [[Prototype/Market]] '''market'''
**** [[Prototype/MiningDrill]] '''mining-drill'''
**** [[Prototype/OffshorePump]] '''offshore-pump'''
**** [[Prototype/Pipe]] '''pipe'''
***** [[Prototype/InfinityPipe]] '''infinity-pipe'''
**** [[Prototype/PipeToGround]] '''pipe-to-ground'''
**** [[Prototype/PlayerPort]] '''player-port'''
**** [[Prototype/PowerSwitch]] '''power-switch'''
**** [[Prototype/ProgrammableSpeaker]] '''programmable-speaker'''
**** [[Prototype/Pump]] '''pump'''
**** [[Prototype/Radar]] '''radar'''
**** [[Prototype/Rail]] <abstract>
***** [[Prototype/CurvedRail]] '''curved-rail'''
***** [[Prototype/StraightRail]] '''straight-rail'''
**** [[Prototype/RailSignalBase]] <abstract>
***** [[Prototype/RailChainSignal]] '''rail-chain-signal'''
***** [[Prototype/RailSignal]] '''rail-signal'''
**** [[Prototype/Reactor]] '''reactor'''
**** [[Prototype/Roboport]] '''roboport'''
**** [[Prototype/SimpleEntityWithOwner]] '''simple-entity-with-owner'''
***** [[Prototype/SimpleEntityWithForce]] '''simple-entity-with-force'''
**** [[Prototype/SolarPanel]] '''solar-panel'''
**** [[Prototype/StorageTank]] '''storage-tank'''
**** [[Prototype/TrainStop]] '''train-stop'''
**** [[Prototype/TransportBeltConnectable]] <abstract>
***** [[Prototype/LinkedBelt]] '''linked-belt'''
***** [[Prototype/Loader1x1]] '''loader-1x1'''
***** [[Prototype/Loader1x2]] '''loader'''
***** [[Prototype/Splitter]] '''splitter'''
***** [[Prototype/TransportBelt]] '''transport-belt'''
***** [[Prototype/UndergroundBelt]] '''underground-belt'''
**** [[Prototype/Turret]] '''turret'''
***** [[Prototype/AmmoTurret]] '''ammo-turret'''
***** [[Prototype/ElectricTurret]] '''electric-turret'''
***** [[Prototype/FluidTurret]] '''fluid-turret'''
**** [[Prototype/Unit]] '''unit'''
**** [[Prototype/Vehicle]] <abstract>
***** [[Prototype/Car]] '''car'''
***** [[Prototype/RollingStock]] <abstract>
****** [[Prototype/ArtilleryWagon]] '''artillery-wagon'''
****** [[Prototype/CargoWagon]] '''cargo-wagon'''
****** [[Prototype/FluidWagon]] '''fluid-wagon'''
****** [[Prototype/Locomotive]] '''locomotive'''
***** [[Prototype/SpiderVehicle]] '''spider-vehicle'''
**** [[Prototype/Wall]] '''wall'''
*** [[Prototype/Fish]] '''fish'''
*** [[Prototype/SimpleEntity]] '''simple-entity'''
*** [[Prototype/SpiderLeg]] '''spider-leg'''
*** [[Prototype/Tree]] '''tree'''
** [[Prototype/Explosion]] '''explosion'''
*** [[Prototype/FlameThrowerExplosion]] '''flame-thrower-explosion'''
** [[Prototype/FireFlame]] '''fire'''
** [[Prototype/FluidStream]] '''stream'''
** [[Prototype/FlyingText]] '''flying-text'''
** [[Prototype/HighlightBoxEntity]] '''highlight-box'''
** [[Prototype/ItemEntity]] '''item-entity'''
** [[Prototype/ItemRequestProxy]] '''item-request-proxy'''
** [[Prototype/ParticleSource]] '''particle-source'''
** [[Prototype/Projectile]] '''projectile'''
** [[Prototype/ResourceEntity]] '''resource'''
** [[Prototype/RocketSiloRocket]] '''rocket-silo-rocket'''
** [[Prototype/RocketSiloRocketShadow]] '''rocket-silo-rocket-shadow'''
** [[Prototype/Smoke]] <abstract>
*** [[Prototype/SimpleSmoke]] '''smoke''' (for migration, cannot be used)
*** [[Prototype/SmokeWithTrigger]] '''smoke-with-trigger'''
** [[Prototype/SpeechBubble]] '''speech-bubble'''
** [[Prototype/Sticker]] '''sticker'''
** [[Prototype/TileGhost]] '''tile-ghost'''
* [[Prototype/Equipment]] <abstract>
** [[Prototype/ActiveDefenseEquipment]] '''active-defense-equipment'''
** [[Prototype/BatteryEquipment]] '''battery-equipment'''
** [[Prototype/BeltImmunityEquipment]] '''belt-immunity-equipment'''
** [[Prototype/EnergyShieldEquipment]] '''energy-shield-equipment'''
** [[Prototype/GeneratorEquipment]] '''generator-equipment'''
** [[Prototype/MovementBonusEquipment]] '''movement-bonus-equipment'''
** [[Prototype/NightVisionEquipment]] '''night-vision-equipment'''
** [[Prototype/RoboportEquipment]] '''roboport-equipment'''
** [[Prototype/SolarPanelEquipment]] '''solar-panel-equipment'''
* [[Prototype/EquipmentCategory]] '''equipment-category'''
* [[Prototype/EquipmentGrid]] '''equipment-grid'''
* [[Prototype/Fluid]] '''fluid'''
* [[Prototype/FuelCategory]] '''fuel-category'''
* [[Prototype/GuiStyle]] '''gui-style'''
* [[Prototype/Item]] '''item'''
** [[Prototype/AmmoItem]] '''ammo'''
** [[Prototype/Capsule]] '''capsule'''
** [[Prototype/Gun]] '''gun'''
** [[Prototype/ItemWithEntityData]] '''item-with-entity-data'''
** [[Prototype/ItemWithLabel]] '''item-with-label'''
*** [[Prototype/ItemWithInventory]] '''item-with-inventory'''
**** [[Prototype/BlueprintBook]] '''blueprint-book'''
*** [[Prototype/ItemWithTags]] '''item-with-tags'''
*** [[Prototype/SelectionTool]] '''selection-tool'''
**** [[Prototype/BlueprintItem]] '''blueprint'''
**** [[Prototype/CopyPasteTool]] '''copy-paste-tool'''
**** [[Prototype/DeconstructionItem]] '''deconstruction-item'''
**** [[Prototype/UpgradeItem]] '''upgrade-item'''
** [[Prototype/Module]] '''module'''
** [[Prototype/RailPlanner]] '''rail-planner'''
** [[Prototype/SpidertronRemote]] '''spidertron-remote'''
** [[Prototype/Tool]] '''tool'''
*** [[Prototype/Armor]] '''armor'''
*** [[Prototype/MiningTool]] '''mining-tool''' (for migration, cannot be used)
*** [[Prototype/RepairTool]] '''repair-tool'''
* [[Prototype/ItemGroup]] '''item-group'''
* [[Prototype/ItemSubGroup]] '''item-subgroup'''
* [[Prototype/ModuleCategory]] '''module-category'''
* [[Prototype/NamedNoiseExpression]] '''noise-expression'''
* [[Prototype/NoiseLayer]] '''noise-layer'''
* [[Prototype/Particle]] '''optimized-particle'''
* [[Prototype/Recipe]] '''recipe'''
* [[Prototype/RecipeCategory]] '''recipe-category'''
* [[Prototype/ResourceCategory]] '''resource-category'''
* [[Prototype/Shortcut]] '''shortcut'''
* [[Prototype/Technology]] '''technology'''
* [[Prototype/Tile]] '''tile'''
* [[Prototype/TipsAndTricksItem]] '''tips-and-tricks-item'''
* [[Prototype/TrivialSmoke]] '''trivial-smoke'''
* [[Prototype/Tutorial]] '''tutorial'''
* [[Prototype/UtilityConstants]] '''utility-constants'''
* [[Prototype/UtilitySounds]] '''utility-sounds'''
* [[Prototype/UtilitySprites]] '''utility-sprites'''
* [[Prototype/VirtualSignal]] '''virtual-signal'''
*/

View file

@ -6,16 +6,11 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("kafkatorio.resource.MinedProduct")
data class MinedProduct(
/** `item` or `fluid`. */
val type: Type,
sealed interface MinedProduct {
/** Prototype name of the result. */
@SerialName("name")
val resultPrototype: PrototypeName,
//Amount of the item or fluid to give. If not specified, amount_min, amount_max and probability must all be specified.
val amount: Double?,
val resultProtoId: PrototypeId
//Amount of the item or fluid to give. If not specified, amount_min, amount_max and probability must all be specified.
val amount: Double?
//Minimal amount of the item or fluid to give. Has no effect when amount is specified.
//amount_min : Uint or double?,
@ -26,17 +21,24 @@ data class MinedProduct(
//A value in range [0, 1]. Item or fluid is only given with this probability; otherwise no product is produced.
//probability : Double?,
//How much of this product is a catalyst.
//How much of this product is a catalyst.
//catalyst_amount : Uint or double?,
) {
@Serializable
@SerialName("kafkatorio.resource.MinedProductType")
enum class Type {
@SerialName("item")
ITEM,
@SerialName("fluid")
FLUID,
}
@SerialName("kafkatorio.resource.MinedProduct.MinedProductFluid")
data class Fluid(
override val amount: Double? = null,
override val resultProtoId: PrototypeId,
) : MinedProduct
@Serializable
@SerialName("kafkatorio.resource.MinedProduct.MinedProductItem")
data class Item(
override val amount: Double? = null,
override val resultProtoId: PrototypeId,
) : MinedProduct
}

View file

@ -4,12 +4,13 @@ import dev.adamko.kafkatorio.schema.common.Colour
import dev.adamko.kafkatorio.schema.common.EntityIdentifiers
import dev.adamko.kafkatorio.schema.common.EntityIdentifiersData
import dev.adamko.kafkatorio.schema.common.EventName
import dev.adamko.kafkatorio.schema.common.FactorioEntityData
import dev.adamko.kafkatorio.schema.common.ForceIndex
import dev.adamko.kafkatorio.schema.common.MapChunkPosition
import dev.adamko.kafkatorio.schema.common.MapEntityPosition
import dev.adamko.kafkatorio.schema.common.MapTileDictionary
import dev.adamko.kafkatorio.schema.common.PlayerIndex
import dev.adamko.kafkatorio.schema.common.PrototypeName
import dev.adamko.kafkatorio.schema.common.PrototypeId
import dev.adamko.kafkatorio.schema.common.SurfaceIndex
import dev.adamko.kafkatorio.schema.common.Tick
import dev.adamko.kafkatorio.schema.common.UnitNumber
@ -23,6 +24,8 @@ import kotlinx.serialization.Serializable
sealed class KafkatorioKeyedPacketData : KafkatorioPacketData() {
abstract val key: KafkatorioKeyedPacketKey
/** A list of all events that have been aggregated into this packet. */
// Maybe change to be Map<EventName, Tick>, where Tick is the most recent event?
// A complete event timeline isn't necessary. The most recent tick of an event is good enough, and requires less space.
abstract val events: Map<EventName, List<Tick>>?
}
@ -40,8 +43,7 @@ sealed class KafkatorioKeyedPacketKey
data class EntityUpdateKey(
/** While normally optional, here a [unitNumber] is required for caching */
override val unitNumber: UnitNumber,
override val name: String,
override val protoType: String,
override val protoId: PrototypeId,
) : EntityIdentifiers, KafkatorioKeyedPacketKey()
@ -60,7 +62,6 @@ data class EntityUpdate(
val lastUser: UInt? = null,
val localisedDescription: String? = null,
val localisedName: String? = null,
val prototype: PrototypeName? = null,
) : KafkatorioKeyedPacketData()
@ -68,17 +69,18 @@ data class EntityUpdate(
@Serializable
@SerialName("kafkatorio.packet.keyed.MapChunkUpdateKey")
data class MapChunkUpdateKey(
@SerialName("kafkatorio.packet.keyed.MapChunkTileUpdateKey")
data class MapChunkTileUpdateKey(
// val index: PlayerIndex, // maybe add player/robot key, to allow metrics like 'player x mined y tiles'
val chunkPosition: MapChunkPosition,
val surfaceIndex: SurfaceIndex,
) : KafkatorioKeyedPacketKey()
@Serializable
@SerialName("kafkatorio.packet.keyed.MapChunkUpdate")
data class MapChunkUpdate(
override val key: MapChunkUpdateKey,
@SerialName("kafkatorio.packet.keyed.MapChunkTileUpdate")
data class MapChunkTileUpdate(
override val key: MapChunkTileUpdateKey,
override val events: Map<EventName, List<Tick>>? = null,
@ -93,6 +95,7 @@ data class MapChunkUpdate(
/* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
@Serializable
@SerialName("kafkatorio.packet.keyed.PlayerUpdateKey")
data class PlayerUpdateKey(
@ -134,4 +137,32 @@ data class PlayerUpdate(
) : KafkatorioKeyedPacketData()
/* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
@Serializable
@SerialName("kafkatorio.packet.keyed.MapChunkEntityUpdateKey")
data class MapChunkEntityUpdateKey(
val protoId: PrototypeId,
val surfaceIndex: SurfaceIndex,
val chunkPosition: MapChunkPosition,
) : KafkatorioKeyedPacketKey()
/**
* @property[distinctEntities] Distinct entities. The key is arbitrary and is only used
* for de-duplicating updates. It should only contain data that can be obtained from
* [the value][FactorioEntityData].
*/
@Serializable
@SerialName("kafkatorio.packet.keyed.MapChunkEntityUpdate")
data class MapChunkEntityUpdate(
override val key: MapChunkEntityUpdateKey,
override val events: Map<EventName, List<Tick>>? = null,
val distinctEntities: Map<String, FactorioEntityData>? = null,
) : KafkatorioKeyedPacketData()
/* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */

View file

@ -4,12 +4,12 @@ import dev.adamko.kafkatorio.library.jsonMapperKafkatorio
import dev.adamko.kafkatorio.schema.common.EventName
import dev.adamko.kafkatorio.schema.common.MapChunkPosition
import dev.adamko.kafkatorio.schema.common.MapTileDictionary
import dev.adamko.kafkatorio.schema.common.PrototypeName
import dev.adamko.kafkatorio.schema.common.PrototypeId
import dev.adamko.kafkatorio.schema.common.SurfaceIndex
import dev.adamko.kafkatorio.schema.common.tick
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kafkatorio.schema.packets.MapChunkUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkUpdateKey
import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdateKey
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import kotlinx.serialization.decodeFromString
@ -59,8 +59,8 @@ class MapChunkUpdateTest : FunSpec({
val expected = KafkatorioPacket(
tick = 36u.tick,
modVersion = "0.4.0",
data = MapChunkUpdate(
key = MapChunkUpdateKey(
data = MapChunkTileUpdate(
key = MapChunkTileUpdateKey(
MapChunkPosition(-6, -3),
SurfaceIndex(1u),
),
@ -78,9 +78,9 @@ class MapChunkUpdateTest : FunSpec({
),
),
protos = mapOf(
PrototypeName("water") to MapTileDictionary.PrototypeKey(1),
PrototypeName("grass-4") to MapTileDictionary.PrototypeKey(2),
PrototypeName("grass-3") to MapTileDictionary.PrototypeKey(32),
PrototypeId("tile", "water") to MapTileDictionary.PrototypeKey(1),
PrototypeId("tile", "grass-4") to MapTileDictionary.PrototypeKey(2),
PrototypeId("tile", "grass-3") to MapTileDictionary.PrototypeKey(32),
)
)
)

View file

@ -3,7 +3,7 @@ package dev.adamko.kafkatorio.events.schema
import dev.adamko.kafkatorio.library.jsonMapperKafkatorio
import dev.adamko.kafkatorio.schema.common.Colour
import dev.adamko.kafkatorio.schema.common.FactorioPrototype
import dev.adamko.kafkatorio.schema.common.PrototypeName
import dev.adamko.kafkatorio.schema.common.PrototypeId
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate
import io.kotest.core.spec.style.BehaviorSpec
@ -12,6 +12,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import kotlinx.serialization.decodeFromString
class PrototypesTest : BehaviorSpec({
Given("Prototypes Json") {
// language=JSON
@ -669,7 +670,7 @@ class PrototypesTest : BehaviorSpec({
.prototypes
.forOne {
it.shouldBeInstanceOf<FactorioPrototype.MapTile>()
it.name shouldBe PrototypeName("tutorial-grid")
it.protoId shouldBe PrototypeId.MapTile("tutorial-grid")
it.order shouldBe "z[other]-c[tutorial]-a[tutorial-grid]"
it.layer shouldBe 55u
it.collisionMasks shouldBe listOf("ground-tile")

View file

@ -7,6 +7,7 @@ import dev.adamko.kafkatorio.schema.common.EventName
import dev.adamko.kafkatorio.schema.common.ForceIndex
import dev.adamko.kafkatorio.schema.common.MapEntityPosition
import dev.adamko.kafkatorio.schema.common.PlayerIndex
import dev.adamko.kafkatorio.schema.common.PrototypeId
import dev.adamko.kafkatorio.schema.common.SurfaceIndex
import dev.adamko.kafkatorio.schema.common.Tick
import dev.adamko.kafkatorio.schema.common.UnitNumber
@ -190,8 +191,7 @@ class UpdatePacketsTest : FunSpec({
colour = null,
diedCause = EntityIdentifiersData(
unitNumber = UnitNumber(66u),
name = "small-biter",
protoType = "unit",
protoId = PrototypeId("unit/small-biter"),
),
disconnectReason = null,
events = mapOf(

View file

@ -20,22 +20,22 @@ class MapTileDictionaryTest : FunSpec({
"44" to mapOf("1" to MapTileDictionary.PrototypeKey(44)),
),
protos = mapOf(
PrototypeName("water") to MapTileDictionary.PrototypeKey(1),
PrototypeName("grass-4") to MapTileDictionary.PrototypeKey(2),
PrototypeName("grass-3") to MapTileDictionary.PrototypeKey(32),
PrototypeId.MapTile("water") to MapTileDictionary.PrototypeKey(1),
PrototypeId.MapTile("grass-4") to MapTileDictionary.PrototypeKey(2),
PrototypeId.MapTile("grass-3") to MapTileDictionary.PrototypeKey(32),
PrototypeName("not-used") to MapTileDictionary.PrototypeKey(99),
PrototypeId.MapTile("not-used") to MapTileDictionary.PrototypeKey(99),
)
)
test("toMapTileList") {
actual.toMapTileList() shouldContainAll listOf(
MapTile(-192, -65, PrototypeName("grass-4")),
MapTile(-192, -65, PrototypeId.MapTile("grass-4")),
MapTile(161, -96, PrototypeName("water")),
MapTile(161, -95, PrototypeName("grass-4")),
MapTile(161, 94, PrototypeName("grass-4")),
MapTile(161, 93, PrototypeName("grass-3")),
MapTile(161, -96, PrototypeId.MapTile("water")),
MapTile(161, -95, PrototypeId.MapTile("grass-4")),
MapTile(161, 94, PrototypeId.MapTile("grass-4")),
MapTile(161, 93, PrototypeId.MapTile("grass-3")),
)
}
}

View file

@ -1,11 +1,16 @@
package dev.adamko.kafkatorio.events.schema
import dev.adamko.kafkatorio.schema.common.PlayerIndex
import dev.adamko.kafkatorio.schema.common.SurfaceIndex
import dev.adamko.kafkatorio.schema.common.UnitNumber
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kxstsgen.KxsTsConfig
import dev.adamko.kxstsgen.KxsTsConfig.TypeAliasTypingConfig.BrandTyping
import dev.adamko.kxstsgen.KxsTsGenerator
import dev.adamko.kxstsgen.core.TsDeclaration.TsTypeAlias
import dev.adamko.kxstsgen.core.TsElementId
import dev.adamko.kxstsgen.core.TsLiteral
import dev.adamko.kxstsgen.core.TsSourceCodeGenerator
import dev.adamko.kxstsgen.core.TsTypeRef
import kotlin.io.path.Path
import kotlin.io.path.createDirectories
@ -13,14 +18,24 @@ import kotlinx.serialization.builtins.serializer
import org.intellij.lang.annotations.Language
@ExperimentalUnsignedTypes
fun main(args: Array<String>) {
val config = KxsTsConfig(
// typeAliasTyping = BrandTyping
typeAliasTyping = BrandTyping
)
val gen2 = KxsTsGenerator(config)
val gen2 = KxsTsGenerator(
config = config,
sourceCodeGenerator = MySourceCodeGenerator(config),
)
// gen2.serializerDescriptorOverrides += mapOf(
////@formatter:off
// SurfaceIndex.serializer() to emptySet(),
// PlayerIndex.serializer() to emptySet(),
// UnitNumber.serializer() to emptySet(),
////@formatter:on
// )
gen2.descriptorOverrides += mapOf(
//@formatter:off
@ -32,6 +47,10 @@ fun main(args: Array<String>) {
UByte .serializer().descriptor to TsTypeAlias(TsElementId("kotlin.UByte" ), TsTypeRef.Literal(TsLiteral.Custom("uint8" ), false)),
UShort.serializer().descriptor to TsTypeAlias(TsElementId("kotlin.UShort"), TsTypeRef.Literal(TsLiteral.Custom("uint16"), false)),
ULong .serializer().descriptor to TsTypeAlias(TsElementId("kotlin.ULong" ), TsTypeRef.Literal(TsLiteral.Custom("uint64"), false)),
PlayerIndex.serializer().descriptor to TsTypeAlias(TsElementId("PlayerIndex" ), TsTypeRef.Literal(TsLiteral.Custom("PlayerIndex"), false)),
SurfaceIndex.serializer().descriptor to TsTypeAlias(TsElementId("SurfaceIndex" ), TsTypeRef.Literal(TsLiteral.Custom("SurfaceIndex"), false)),
UnitNumber.serializer().descriptor to TsTypeAlias(TsElementId("UnitNumber" ), TsTypeRef.Literal(TsLiteral.Custom("UnitNumber"), false)),
//@formatter:on
)
@ -60,3 +79,26 @@ val kt2tsHeader = """
|// Generated by kxs-ts-gen - do not edit this file manually
|
""".trimMargin()
class MySourceCodeGenerator(
config: KxsTsConfig,
) : TsSourceCodeGenerator.Default(config) {
override fun generateTypeAlias(element: TsTypeAlias): String {
return if (element.id.name in listOf("PlayerIndex", "SurfaceIndex", "UnitNumber")) {
""
} else if (
(element.typeRef as? TsTypeRef.Literal)?.element is TsLiteral.Custom
|| element.id.name == "Tick"
) {
val aliasedRef = generateTypeReference(element.typeRef)
"""
|export type ${element.id.name} = ${aliasedRef};
""".trimMargin()
} else {
super.generateTypeAlias(element)
}
}
}

View file

@ -101,7 +101,7 @@ val installEventsTsSchema by tasks.registering(Sync::class) {
}
) {
// drop the first directory inside the zip
eachFile { relativePath = dropDirectory() }
// eachFile { relativePath = dropDirectory() }
includeEmptyDirs = false
}
into(outputDir)

View file

@ -75,9 +75,9 @@
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
},
"typed-factorio": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/typed-factorio/-/typed-factorio-1.3.0.tgz",
"integrity": "sha512-qBHKjE+m9Im7vDhsGvl0uwaSFzJ5asqyfw414gF0owZwmSax2CIjdLx367qUgdD4flsB0n9HecBaZ0Yzt9zMPQ=="
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/typed-factorio/-/typed-factorio-1.3.2.tgz",
"integrity": "sha512-sAVIDvSSBPWL7trI94SPd2Suq9dLjXmXL1WFKJIYJpFQocNwJUb0ydGRBsvJjZhVAyEZYJC0lHlDWH47RWP/tw=="
},
"typescript": {
"version": "4.7.4",

View file

@ -9,15 +9,19 @@
"engines": {
"node": "~14.19.1"
},
"devDependencies": {},
"devDependencies": {
},
"dependencies": {
"lua-types": "^2.11.0",
"typescript-to-lua": "1.6.1",
"typed-factorio": "1.3.0",
"typed-factorio": "1.3.2",
"typescript": "4.7.4"
},
"license": "UNLICENSED",
"peerDependencies": {},
"optionalDependencies": {},
"bundledDependencies": []
"peerDependencies": {
},
"optionalDependencies": {
},
"bundledDependencies": [
]
}

View file

@ -3,7 +3,7 @@ local ____global_2Dinit = require("main.global-init")
local initGlobal = ____global_2Dinit.initGlobal
local ____prototypeUpdates = require("main.events.prototypeUpdates")
local emitPrototypes = ____prototypeUpdates.emitPrototypes
require("main.events.registerHandlers")
require("main.events.registerEventListeners")
require("main.events.mapChunkUpdates")
require("main.events.playerUpdates")
require("main.events.entityUpdates")

View file

@ -9,7 +9,8 @@ do
KafkatorioPacketData.Type.PrototypesUpdate = "kafkatorio.packet.instant.PrototypesUpdate"
KafkatorioPacketData.Type.SurfaceUpdate = "kafkatorio.packet.instant.SurfaceUpdate"
KafkatorioPacketData.Type.EntityUpdate = "kafkatorio.packet.keyed.EntityUpdate"
KafkatorioPacketData.Type.MapChunkUpdate = "kafkatorio.packet.keyed.MapChunkUpdate"
KafkatorioPacketData.Type.MapChunkEntityUpdate = "kafkatorio.packet.keyed.MapChunkEntityUpdate"
KafkatorioPacketData.Type.MapChunkTileUpdate = "kafkatorio.packet.keyed.MapChunkTileUpdate"
KafkatorioPacketData.Type.PlayerUpdate = "kafkatorio.packet.keyed.PlayerUpdate"
KafkatorioPacketData.Type.Error = "kafkatorio.packet.KafkatorioPacketData.Error"
end
@ -20,7 +21,62 @@ do
FactorioPrototype.Type.Entity = "kafkatorio.prototype.Entity"
FactorioPrototype.Type.MapTile = "kafkatorio.prototype.MapTile"
end
____exports.MinedProductType = MinedProductType or ({})
____exports.MinedProductType.item = "item"
____exports.MinedProductType.fluid = "fluid"
____exports.FactorioEntityData = {}
local FactorioEntityData = ____exports.FactorioEntityData
do
FactorioEntityData.Type = Type or ({})
FactorioEntityData.Type.Resource = "kafkatorio.entity.FactorioEntityData.Resource"
FactorioEntityData.Type.Standard = "kafkatorio.entity.FactorioEntityData.Standard"
end
____exports.EntityStatus = EntityStatus or ({})
____exports.EntityStatus.CANT_DIVIDE_SEGMENTS = "CANT_DIVIDE_SEGMENTS"
____exports.EntityStatus.CHARGING = "CHARGING"
____exports.EntityStatus.CLOSED_BY_CIRCUIT_NETWORK = "CLOSED_BY_CIRCUIT_NETWORK"
____exports.EntityStatus.DISABLED = "DISABLED"
____exports.EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR = "DISABLED_BY_CONTROL_BEHAVIOR"
____exports.EntityStatus.DISABLED_BY_SCRIPT = "DISABLED_BY_SCRIPT"
____exports.EntityStatus.DISCHARGING = "DISCHARGING"
____exports.EntityStatus.FLUID_INGREDIENT_SHORTAGE = "FLUID_INGREDIENT_SHORTAGE"
____exports.EntityStatus.FULLY_CHARGED = "FULLY_CHARGED"
____exports.EntityStatus.FULL_OUTPUT = "FULL_OUTPUT"
____exports.EntityStatus.ITEM_INGREDIENT_SHORTAGE = "ITEM_INGREDIENT_SHORTAGE"
____exports.EntityStatus.LAUNCHING_ROCKET = "LAUNCHING_ROCKET"
____exports.EntityStatus.LOW_INPUT_FLUID = "LOW_INPUT_FLUID"
____exports.EntityStatus.LOW_POWER = "LOW_POWER"
____exports.EntityStatus.LOW_TEMPERATURE = "LOW_TEMPERATURE"
____exports.EntityStatus.MARKED_FOR_DECONSTRUCTION = "MARKED_FOR_DECONSTRUCTION"
____exports.EntityStatus.MISSING_REQUIRED_FLUID = "MISSING_REQUIRED_FLUID"
____exports.EntityStatus.MISSING_SCIENCE_PACKS = "MISSING_SCIENCE_PACKS"
____exports.EntityStatus.NETWORKS_CONNECTED = "NETWORKS_CONNECTED"
____exports.EntityStatus.NETWORKS_DISCONNECTED = "NETWORKS_DISCONNECTED"
____exports.EntityStatus.NORMAL = "NORMAL"
____exports.EntityStatus.NOT_CONNECTED_TO_RAIL = "NOT_CONNECTED_TO_RAIL"
____exports.EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK = "NOT_PLUGGED_IN_ELECTRIC_NETWORK"
____exports.EntityStatus.NO_AMMO = "NO_AMMO"
____exports.EntityStatus.NO_FUEL = "NO_FUEL"
____exports.EntityStatus.NO_INGREDIENTS = "NO_INGREDIENTS"
____exports.EntityStatus.NO_INPUT_FLUID = "NO_INPUT_FLUID"
____exports.EntityStatus.NO_MINABLE_RESOURCES = "NO_MINABLE_RESOURCES"
____exports.EntityStatus.NO_MODULES_TO_TRANSMIT = "NO_MODULES_TO_TRANSMIT"
____exports.EntityStatus.NO_POWER = "NO_POWER"
____exports.EntityStatus.NO_RECIPE = "NO_RECIPE"
____exports.EntityStatus.NO_RESEARCH_IN_PROGRESS = "NO_RESEARCH_IN_PROGRESS"
____exports.EntityStatus.OPENED_BY_CIRCUIT_NETWORK = "OPENED_BY_CIRCUIT_NETWORK"
____exports.EntityStatus.OUT_OF_LOGISTIC_NETWORK = "OUT_OF_LOGISTIC_NETWORK"
____exports.EntityStatus.PREPARING_ROCKET_FOR_LAUNCH = "PREPARING_ROCKET_FOR_LAUNCH"
____exports.EntityStatus.RECHARGING_AFTER_POWER_OUTAGE = "RECHARGING_AFTER_POWER_OUTAGE"
____exports.EntityStatus.TURNED_OFF_DURING_DAYTIME = "TURNED_OFF_DURING_DAYTIME"
____exports.EntityStatus.WAITING_FOR_SOURCE_ITEMS = "WAITING_FOR_SOURCE_ITEMS"
____exports.EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION = "WAITING_FOR_SPACE_IN_DESTINATION"
____exports.EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT = "WAITING_FOR_TARGET_TO_BE_BUILT"
____exports.EntityStatus.WAITING_FOR_TRAIN = "WAITING_FOR_TRAIN"
____exports.EntityStatus.WAITING_TO_LAUNCH_ROCKET = "WAITING_TO_LAUNCH_ROCKET"
____exports.EntityStatus.WORKING = "WORKING"
____exports.MinedProduct = {}
local MinedProduct = ____exports.MinedProduct
do
MinedProduct.Type = Type or ({})
MinedProduct.Type.MinedProductFluid = "kafkatorio.resource.MinedProduct.MinedProductFluid"
MinedProduct.Type.MinedProductItem = "kafkatorio.resource.MinedProduct.MinedProductItem"
end
return ____exports

View file

@ -1,7 +1,22 @@
local ____exports = {}
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local EntityStatus = ____kafkatorio_2Dschema.EntityStatus
local FactorioEntityData = ____kafkatorio_2Dschema.FactorioEntityData
____exports.Converters = {}
local Converters = ____exports.Converters
do
local convertEntityStatus, EntityStatusRecord
function convertEntityStatus(status)
if status == nil then
return nil
else
return EntityStatusRecord[status]
end
end
function Converters.prototypeId(____type, name)
return (____type .. "/") .. name
end
local floor = math.floor
local mapEventIdToName = {}
for eventName, eventId in pairs(defines.events) do
mapEventIdToName[eventId] = eventName
@ -36,5 +51,86 @@ do
end
return masks
end
function Converters.convertResourceEntity(entity)
if entity.type ~= "resource" then
return nil
else
return {
type = FactorioEntityData.Type.Resource,
protoId = Converters.prototypeId(entity.type, entity.name),
status = convertEntityStatus(entity.status),
amount = entity.amount,
initialAmount = entity.initial_amount,
position = Converters.convertMapTablePosition(entity.position)
}
end
end
function Converters.prototypeIdEntity(entity)
return Converters.prototypeId(entity.type, entity.name)
end
function Converters.tilePositionToChunkPosition(mapPosition)
return {
floor(mapPosition.x / 32),
floor(mapPosition.y)
}
end
function Converters.mapPositionToChunkPosition(mapPosition)
return {
floor(mapPosition.x / 32),
floor(mapPosition.y)
}
end
function Converters.entityStatus(status)
if status == nil then
return nil
else
return EntityStatusRecord[status]
end
end
EntityStatusRecord = {
[defines.entity_status.cant_divide_segments] = EntityStatus.CANT_DIVIDE_SEGMENTS,
[defines.entity_status.charging] = EntityStatus.CHARGING,
[defines.entity_status.closed_by_circuit_network] = EntityStatus.CLOSED_BY_CIRCUIT_NETWORK,
[defines.entity_status.disabled] = EntityStatus.DISABLED,
[defines.entity_status.disabled_by_control_behavior] = EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR,
[defines.entity_status.disabled_by_script] = EntityStatus.DISABLED_BY_SCRIPT,
[defines.entity_status.discharging] = EntityStatus.DISCHARGING,
[defines.entity_status.fluid_ingredient_shortage] = EntityStatus.FLUID_INGREDIENT_SHORTAGE,
[defines.entity_status.fully_charged] = EntityStatus.FULLY_CHARGED,
[defines.entity_status.full_output] = EntityStatus.FULL_OUTPUT,
[defines.entity_status.item_ingredient_shortage] = EntityStatus.ITEM_INGREDIENT_SHORTAGE,
[defines.entity_status.launching_rocket] = EntityStatus.LAUNCHING_ROCKET,
[defines.entity_status.low_input_fluid] = EntityStatus.LOW_INPUT_FLUID,
[defines.entity_status.low_power] = EntityStatus.LOW_POWER,
[defines.entity_status.low_temperature] = EntityStatus.LOW_TEMPERATURE,
[defines.entity_status.marked_for_deconstruction] = EntityStatus.MARKED_FOR_DECONSTRUCTION,
[defines.entity_status.missing_required_fluid] = EntityStatus.MISSING_REQUIRED_FLUID,
[defines.entity_status.missing_science_packs] = EntityStatus.MISSING_SCIENCE_PACKS,
[defines.entity_status.networks_connected] = EntityStatus.NETWORKS_CONNECTED,
[defines.entity_status.networks_disconnected] = EntityStatus.NETWORKS_DISCONNECTED,
[defines.entity_status.normal] = EntityStatus.NORMAL,
[defines.entity_status.not_connected_to_rail] = EntityStatus.NOT_CONNECTED_TO_RAIL,
[defines.entity_status.not_plugged_in_electric_network] = EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK,
[defines.entity_status.no_ammo] = EntityStatus.NO_AMMO,
[defines.entity_status.no_fuel] = EntityStatus.NO_FUEL,
[defines.entity_status.no_ingredients] = EntityStatus.NO_INGREDIENTS,
[defines.entity_status.no_input_fluid] = EntityStatus.NO_INPUT_FLUID,
[defines.entity_status.no_minable_resources] = EntityStatus.NO_MINABLE_RESOURCES,
[defines.entity_status.no_modules_to_transmit] = EntityStatus.NO_MODULES_TO_TRANSMIT,
[defines.entity_status.no_power] = EntityStatus.NO_POWER,
[defines.entity_status.no_recipe] = EntityStatus.NO_RECIPE,
[defines.entity_status.no_research_in_progress] = EntityStatus.NO_RESEARCH_IN_PROGRESS,
[defines.entity_status.opened_by_circuit_network] = EntityStatus.OPENED_BY_CIRCUIT_NETWORK,
[defines.entity_status.out_of_logistic_network] = EntityStatus.OUT_OF_LOGISTIC_NETWORK,
[defines.entity_status.preparing_rocket_for_launch] = EntityStatus.PREPARING_ROCKET_FOR_LAUNCH,
[defines.entity_status.recharging_after_power_outage] = EntityStatus.RECHARGING_AFTER_POWER_OUTAGE,
[defines.entity_status.turned_off_during_daytime] = EntityStatus.TURNED_OFF_DURING_DAYTIME,
[defines.entity_status.waiting_for_source_items] = EntityStatus.WAITING_FOR_SOURCE_ITEMS,
[defines.entity_status.waiting_for_space_in_destination] = EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION,
[defines.entity_status.waiting_for_target_to_be_built] = EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT,
[defines.entity_status.waiting_for_train] = EntityStatus.WAITING_FOR_TRAIN,
[defines.entity_status.waiting_to_launch_rocket] = EntityStatus.WAITING_TO_LAUNCH_ROCKET,
[defines.entity_status.working] = EntityStatus.WORKING
}
end
return ____exports

View file

@ -1,105 +1,203 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local handleBuiltEntityEvent, handleMinedEntityEvent
local ____converters = require("main.events.converters")
local Converters = ____converters.Converters
local ____EventDataCache = require("main.cache.EventDataCache")
local EventDataCache = ____EventDataCache.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local FactorioEntityData = ____kafkatorio_2Dschema.FactorioEntityData
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
function handleBuiltEntityEvent(event)
local eventName = Converters.eventNameString(event.name)
if event.created_entity.unit_number == nil then
____exports.EntityUpdatesHandler = __TS__Class()
local EntityUpdatesHandler = ____exports.EntityUpdatesHandler
EntityUpdatesHandler.name = "EntityUpdatesHandler"
function EntityUpdatesHandler.prototype.____constructor(self)
end
function EntityUpdatesHandler.prototype.handleMinedTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
local key = {name = event.created_entity.name, protoType = event.created_entity.prototype.name, unitNumber = event.created_entity.unit_number}
EventDataCache:debounce(
key,
KafkatorioPacketData.Type.EntityUpdate,
local tilesByChunk = {}
for ____, tile in ipairs(event.tiles) do
local chunkPosition = Converters.tilePositionToChunkPosition(tile.position)
tilesByChunk[chunkPosition] = tile
end
for chunkPos, tiles in pairs(tilesByChunk) do
local entities = {}
for tile in pairs(tiles) do
local resources = ____exports.EntityUpdatesHandler:getTileResourceEntities(surface, chunkPos)
for ____, resource in ipairs(resources) do
entities[#entities + 1] = resource
end
end
____exports.EntityUpdatesHandler:throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick,
event.surface_index,
{chunkPos[1], chunkPos[2]},
entities
)
end
end
function EntityUpdatesHandler.prototype.handleMinedEntityEvent(self, event)
____exports.EntityUpdatesHandler:throttleEntityUpdate(event, event.entity)
end
function EntityUpdatesHandler.prototype.handleBuiltEntityEvent(self, event)
____exports.EntityUpdatesHandler:throttleEntityUpdate(event, event.created_entity)
end
function EntityUpdatesHandler.prototype.handleChunkGeneratedEvent(self, event)
local entities = ____exports.EntityUpdatesHandler:getAreaResourceEntities(event.surface, event.area)
____exports.EntityUpdatesHandler:throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick,
event.surface.index,
{event.position.x, event.position.y},
entities
)
end
function EntityUpdatesHandler.throttleEntityUpdate(self, event, entity)
if entity.unit_number == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local entityUpdateKey = {
protoId = Converters.prototypeIdEntity(entity),
surfaceIndex = entity.surface.index,
chunkPosition = Converters.mapPositionToChunkPosition(entity.position)
}
local ____FactorioEntityData_Type_Standard_2 = FactorioEntityData.Type.Standard
local ____Converters_prototypeIdEntity_result_3 = Converters.prototypeIdEntity(entity)
local ____entity_graphics_variation_4 = entity.graphics_variation
local ____entity_health_5 = entity.health
local ____entity_active_6 = entity.active
local ____entity_rotatable_7 = entity.rotatable
local ____entity_last_user_index_0 = entity.last_user
if ____entity_last_user_index_0 ~= nil then
____entity_last_user_index_0 = ____entity_last_user_index_0.index
end
local entityUpdate = {
type = ____FactorioEntityData_Type_Standard_2,
protoId = ____Converters_prototypeIdEntity_result_3,
graphicsVariation = ____entity_graphics_variation_4,
health = ____entity_health_5,
isActive = ____entity_active_6,
isRotatable = ____entity_rotatable_7,
lastUser = ____entity_last_user_index_0,
localisedDescription = nil,
localisedName = nil,
position = {entity.position.x, entity.position.y},
status = Converters.entityStatus(entity.status)
}
local entityKey = tostring(entity.unit_number)
EventDataCache:throttle(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
function(data)
if data.events == nil then
data.events = {}
end
local ____data_events_0, ____eventName_1 = data.events, eventName
if ____data_events_0[____eventName_1] == nil then
____data_events_0[____eventName_1] = {}
local ____data_events_8, ____eventName_9 = data.events, eventName
if ____data_events_8[____eventName_9] == nil then
____data_events_8[____eventName_9] = {}
end
local ____data_events_eventName_2 = data.events[eventName]
____data_events_eventName_2[#____data_events_eventName_2 + 1] = event.tick
data.chunkPosition = Converters.convertMapTablePosition(event.created_entity.position)
data.graphicsVariation = event.created_entity.graphics_variation
data.health = event.created_entity.health
data.isActive = event.created_entity.active
data.isRotatable = event.created_entity.rotatable
local ____data_5 = data
local ____event_created_entity_last_user_index_3 = event.created_entity.last_user
if ____event_created_entity_last_user_index_3 ~= nil then
____event_created_entity_last_user_index_3 = ____event_created_entity_last_user_index_3.index
local ____data_events_eventName_10 = data.events[eventName]
____data_events_eventName_10[#____data_events_eventName_10 + 1] = event.tick
if data.distinctEntities == nil then
data.distinctEntities = {}
end
____data_5.lastUser = ____event_created_entity_last_user_index_3
data.prototype = event.created_entity.prototype.name
data.distinctEntities[entityKey] = entityUpdate
end
)
end
function handleMinedEntityEvent(event)
local eventName = Converters.eventNameString(event.name)
if event.entity.unit_number == nil then
return
function EntityUpdatesHandler.throttleResourceEntitiesUpdate(self, eventName, eventTick, surfaceIndex, chunkPosition, entities)
local entitiesByProtoId = {}
for ____, entity in ipairs(entities) do
local ____entitiesByProtoId_11, ____entity_protoId_12 = entitiesByProtoId, entity.protoId
if ____entitiesByProtoId_11[____entity_protoId_12] == nil then
____entitiesByProtoId_11[____entity_protoId_12] = {}
end
local ____entitiesByProtoId_entity_protoId_13 = entitiesByProtoId[entity.protoId]
____entitiesByProtoId_entity_protoId_13[#____entitiesByProtoId_entity_protoId_13 + 1] = entity
end
local key = {name = event.entity.name, protoType = event.entity.prototype.name, unitNumber = event.entity.unit_number}
EventDataCache:debounce(
key,
KafkatorioPacketData.Type.EntityUpdate,
function(data)
if data.events == nil then
data.events = {}
for protoId, resourceEntities in pairs(entitiesByProtoId) do
local entityUpdateKey = {protoId = protoId, surfaceIndex = surfaceIndex, chunkPosition = {chunkPosition[1], chunkPosition[2]}}
EventDataCache:throttle(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
function(data)
if data.events == nil then
data.events = {}
end
local ____data_events_14, ____eventName_15 = data.events, eventName
if ____data_events_14[____eventName_15] == nil then
____data_events_14[____eventName_15] = {}
end
local ____data_events_eventName_16 = data.events[eventName]
____data_events_eventName_16[#____data_events_eventName_16 + 1] = eventTick
for ____, entity in ipairs(resourceEntities) do
local x, y = table.unpack(entity.position)
local entityKey = (tostring(x) .. ",") .. tostring(y)
if data.distinctEntities == nil then
data.distinctEntities = {}
end
data.distinctEntities[entityKey] = entity
end
end
local ____data_events_6, ____eventName_7 = data.events, eventName
if ____data_events_6[____eventName_7] == nil then
____data_events_6[____eventName_7] = {}
end
local ____data_events_eventName_8 = data.events[eventName]
____data_events_eventName_8[#____data_events_eventName_8 + 1] = event.tick
data.chunkPosition = Converters.convertMapTablePosition(event.entity.position)
data.graphicsVariation = event.entity.graphics_variation
data.health = event.entity.health
data.isActive = event.entity.active
data.isRotatable = event.entity.rotatable
local ____data_11 = data
local ____event_entity_last_user_index_9 = event.entity.last_user
if ____event_entity_last_user_index_9 ~= nil then
____event_entity_last_user_index_9 = ____event_entity_last_user_index_9.index
end
____data_11.lastUser = ____event_entity_last_user_index_9
data.prototype = event.entity.prototype.name
end
)
)
end
end
function EntityUpdatesHandler.getTileResourceEntities(self, surface, mapPosition)
local rawEntities = surface.find_entities_filtered({position = mapPosition, collision_mask = "resource-layer"})
local resources = {}
for ____, rawEntity in ipairs(rawEntities) do
local entity = Converters.convertResourceEntity(rawEntity)
if entity ~= nil then
resources[#resources + 1] = entity
end
end
return resources
end
function EntityUpdatesHandler.getAreaResourceEntities(self, surface, area)
local rawEntities = surface.find_entities_filtered({area = area, collision_mask = "resource-layer"})
local resources = {}
for ____, rawEntity in ipairs(rawEntities) do
local entity = Converters.convertResourceEntity(rawEntity)
if entity ~= nil then
resources[#resources + 1] = entity
end
end
return resources
end
local EntityUpdates = __TS__New(____exports.EntityUpdatesHandler)
____exports.default = EntityUpdates
script.on_event(
defines.events.on_built_entity,
function(e)
log("on_built_entity " .. tostring(e.tick))
handleBuiltEntityEvent(e)
EntityUpdates:handleBuiltEntityEvent(e)
end
)
script.on_event(
defines.events.on_robot_built_entity,
function(e)
log("on_robot_built_entity " .. tostring(e.tick))
handleBuiltEntityEvent(e)
EntityUpdates:handleBuiltEntityEvent(e)
end
)
script.on_event(
defines.events.on_player_mined_entity,
function(e)
log("on_player_mined_entity " .. tostring(e.tick))
handleMinedEntityEvent(e)
EntityUpdates:handleMinedEntityEvent(e)
end
)
script.on_event(
defines.events.on_robot_mined_entity,
function(e)
log("on_robot_mined_entity " .. tostring(e.tick))
handleMinedEntityEvent(e)
EntityUpdates:handleMinedEntityEvent(e)
end
)
return ____exports

View file

@ -1,9 +1,9 @@
local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local Map = ____lualib.Map
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local mapTilesUpdateDebounce, getTiles, getSurface, groupTiles, handleBuiltTileEvent, handleMinedTileEvent, Type
local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType
local ____converters = require("main.events.converters")
@ -12,65 +12,16 @@ local ____EventDataCache = require("main.cache.EventDataCache")
local EventUpdatesManager = ____EventDataCache.default
local ____kafkatorio_2Dschema = require("generated.kafkatorio-schema")
local KafkatorioPacketData = ____kafkatorio_2Dschema.KafkatorioPacketData
function mapTilesUpdateDebounce(surface, chunkPosition, tiles, event, updater, expirationDurationTicks)
if surface == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition}
EventUpdatesManager:debounce(
key,
Type.MapChunkUpdate,
function(data)
if data.tileDictionary == nil then
data.tileDictionary = {tilesXY = {}, protos = {}}
end
local protosCount = table_size(data.tileDictionary.protos)
for ____, tile in ipairs(tiles) do
local ____data_tileDictionary_protos_1, ____tile_name_2 = data.tileDictionary.protos, tile.name
if ____data_tileDictionary_protos_1[____tile_name_2] == nil then
local ____protosCount_0 = protosCount
protosCount = ____protosCount_0 + 1
____data_tileDictionary_protos_1[____tile_name_2] = ____protosCount_0
end
local protoKey = data.tileDictionary.protos[tile.name]
local xString = tostring(tile.position.x)
local yString = tostring(tile.position.y)
local ____data_tileDictionary_tilesXY_3, ____xString_4 = data.tileDictionary.tilesXY, xString
if ____data_tileDictionary_tilesXY_3[____xString_4] == nil then
____data_tileDictionary_tilesXY_3[____xString_4] = {}
end
data.tileDictionary.tilesXY[xString][yString] = protoKey
end
if data.events == nil then
data.events = {}
end
local ____data_events_5, ____eventName_6 = data.events, eventName
if ____data_events_5[____eventName_6] == nil then
____data_events_5[____eventName_6] = {}
end
local ____data_events_eventName_7 = data.events[eventName]
____data_events_eventName_7[#____data_events_eventName_7 + 1] = event.tick
if updater ~= nil then
updater(data)
end
end,
expirationDurationTicks
)
local ____entityUpdates = require("main.events.entityUpdates")
local EntityUpdates = ____entityUpdates.default
local Type = KafkatorioPacketData.Type
local MapChunkUpdateHandler = __TS__Class()
MapChunkUpdateHandler.name = "MapChunkUpdateHandler"
function MapChunkUpdateHandler.prototype.____constructor(self)
end
function getTiles(surface, area)
local ____surface_find_tiles_filtered_result_8 = surface
if ____surface_find_tiles_filtered_result_8 ~= nil then
____surface_find_tiles_filtered_result_8 = ____surface_find_tiles_filtered_result_8.find_tiles_filtered({area = area})
end
return ____surface_find_tiles_filtered_result_8 or ({})
end
function getSurface(surfaceIndex)
return game.surfaces[surfaceIndex]
end
function ____exports.handleChunkGeneratedEvent(event, expirationDurationTicks)
local tiles = getTiles(event.surface, event.area)
mapTilesUpdateDebounce(
function MapChunkUpdateHandler.prototype.handleChunkGeneratedEvent(self, event, expirationDurationTicks)
local tiles = event.surface.find_tiles_filtered({area = event.area})
MapChunkUpdateHandler:mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
@ -79,38 +30,19 @@ function ____exports.handleChunkGeneratedEvent(event, expirationDurationTicks)
expirationDurationTicks
)
end
function groupTiles(tiles)
local mapChunkPositionToTiles = __TS__New(Map)
for ____, tile in ipairs(tiles) do
local chunkPosition = {
math.floor(tile.position.x / 32),
math.floor(tile.position.y / 32)
}
if not mapChunkPositionToTiles:has(chunkPosition) then
mapChunkPositionToTiles:set(chunkPosition, {})
end
local ____mapChunkPositionToTiles_get_result_push_result_10 = mapChunkPositionToTiles:get(chunkPosition)
if ____mapChunkPositionToTiles_get_result_push_result_10 ~= nil then
local ____temp_11 = mapChunkPositionToTiles:get(chunkPosition)
____temp_11[#____temp_11 + 1] = tile
____mapChunkPositionToTiles_get_result_push_result_10 = nil
end
end
return mapChunkPositionToTiles
end
function handleBuiltTileEvent(event)
local surface = getSurface(event.surface_index)
function MapChunkUpdateHandler.prototype.handleBuiltTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
local eventName = Converters.eventNameString(event.name)
log("[handleBuiltTileEvent] undefined surface " .. eventName)
return
end
local tiles = Converters.convertPlacedTiles(event.tile, event.tiles)
local groupedTiles = groupTiles(tiles)
local groupedTiles = MapChunkUpdateHandler:groupTiles(tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(
MapChunkUpdateHandler:mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
@ -121,12 +53,15 @@ function handleBuiltTileEvent(event)
return
end
if isEventType(event, defines.events.on_robot_built_tile) then
local ____data_15 = data
local ____event_robot_unit_number_13 = event.robot
if ____event_robot_unit_number_13 ~= nil then
____event_robot_unit_number_13 = ____event_robot_unit_number_13.unit_number
local ____data_2 = data
local ____event_robot_unit_number_0 = event.robot
if ____event_robot_unit_number_0 ~= nil then
____event_robot_unit_number_0 = ____event_robot_unit_number_0.unit_number
end
____data_15.robot = {unitNumber = ____event_robot_unit_number_13 or nil, name = event.robot.name, protoType = event.robot.type}
____data_2.robot = {
unitNumber = ____event_robot_unit_number_0 or nil,
protoId = Converters.prototypeId(event.robot.type, event.robot.name)
}
return
end
local exhaustiveCheck = event
@ -134,19 +69,19 @@ function handleBuiltTileEvent(event)
)
end
end
function handleMinedTileEvent(event)
local surface = getSurface(event.surface_index)
function MapChunkUpdateHandler.prototype.handleMinedTileEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
local eventName = Converters.eventNameString(event.name)
log("[handleMinedTileEvent] undefined surface " .. eventName)
return
end
local tiles = Converters.convertRemovedTiles(surface, event.tiles)
local groupedTiles = groupTiles(tiles)
local groupedTiles = MapChunkUpdateHandler:groupTiles(tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(
MapChunkUpdateHandler:mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
@ -157,12 +92,15 @@ function handleMinedTileEvent(event)
return
end
if isEventType(event, defines.events.on_robot_mined_tile) then
local ____data_18 = data
local ____event_robot_unit_number_16 = event.robot
if ____event_robot_unit_number_16 ~= nil then
____event_robot_unit_number_16 = ____event_robot_unit_number_16.unit_number
local ____data_5 = data
local ____event_robot_unit_number_3 = event.robot
if ____event_robot_unit_number_3 ~= nil then
____event_robot_unit_number_3 = ____event_robot_unit_number_3.unit_number
end
____data_18.robot = {unitNumber = ____event_robot_unit_number_16 or nil, name = event.robot.name, protoType = event.robot.type}
____data_5.robot = {
unitNumber = ____event_robot_unit_number_3 or nil,
protoId = Converters.prototypeId(event.robot.type, event.robot.name)
}
return
end
local exhaustiveCheck = event
@ -170,78 +108,155 @@ function handleMinedTileEvent(event)
)
end
end
Type = KafkatorioPacketData.Type
function MapChunkUpdateHandler.prototype.handleScriptSetTilesEvent(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
local groupedTiles = MapChunkUpdateHandler:groupTiles(event.tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
MapChunkUpdateHandler:mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
end
end
function MapChunkUpdateHandler.prototype.handlePreChunkDeleted(self, event)
local surface = game.get_surface(event.surface_index)
if surface == nil then
return
end
for ____, position in ipairs(event.positions) do
local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}}
EventUpdatesManager:debounce(
key,
Type.MapChunkTileUpdate,
function(data)
data.isDeleted = true
return true
end,
0
)
end
end
function MapChunkUpdateHandler.mapTilesUpdateDebounce(self, surface, chunkPosition, tiles, event, updater, expirationDurationTicks)
if surface == nil then
return
end
local eventName = Converters.eventNameString(event.name)
local key = {surfaceIndex = surface.index, chunkPosition = chunkPosition}
EventUpdatesManager:debounce(
key,
Type.MapChunkTileUpdate,
function(data)
if data.tileDictionary == nil then
data.tileDictionary = {tilesXY = {}, protos = {}}
end
local protosCount = table_size(data.tileDictionary.protos)
for ____, tile in ipairs(tiles) do
local protoId = Converters.prototypeId("tile", tile.name)
if data.tileDictionary.protos[protoId] == nil then
end
local ____data_tileDictionary_protos_7, ____protoId_8 = data.tileDictionary.protos, protoId
if ____data_tileDictionary_protos_7[____protoId_8] == nil then
local ____protosCount_6 = protosCount
protosCount = ____protosCount_6 + 1
____data_tileDictionary_protos_7[____protoId_8] = ____protosCount_6
end
local protoKey = data.tileDictionary.protos[protoId]
local xString = tostring(tile.position.x)
local yString = tostring(tile.position.y)
local ____data_tileDictionary_tilesXY_9, ____xString_10 = data.tileDictionary.tilesXY, xString
if ____data_tileDictionary_tilesXY_9[____xString_10] == nil then
____data_tileDictionary_tilesXY_9[____xString_10] = {}
end
data.tileDictionary.tilesXY[xString][yString] = protoKey
end
if data.events == nil then
data.events = {}
end
local ____data_events_11, ____eventName_12 = data.events, eventName
if ____data_events_11[____eventName_12] == nil then
____data_events_11[____eventName_12] = {}
end
local ____data_events_eventName_13 = data.events[eventName]
____data_events_eventName_13[#____data_events_eventName_13 + 1] = event.tick
if updater ~= nil then
updater(data)
end
end,
expirationDurationTicks
)
end
function MapChunkUpdateHandler.groupTiles(self, tiles)
local mapChunkPositionToTiles = __TS__New(Map)
for ____, tile in ipairs(tiles) do
local chunkPosition = {
math.floor(tile.position.x / 32),
math.floor(tile.position.y / 32)
}
if not mapChunkPositionToTiles:has(chunkPosition) then
mapChunkPositionToTiles:set(chunkPosition, {})
end
local ____mapChunkPositionToTiles_get_result_push_result_14 = mapChunkPositionToTiles:get(chunkPosition)
if ____mapChunkPositionToTiles_get_result_push_result_14 ~= nil then
local ____temp_15 = mapChunkPositionToTiles:get(chunkPosition)
____temp_15[#____temp_15 + 1] = tile
____mapChunkPositionToTiles_get_result_push_result_14 = nil
end
end
return mapChunkPositionToTiles
end
local MapChunkUpdate = __TS__New(MapChunkUpdateHandler)
____exports.default = MapChunkUpdate
script.on_event(
defines.events.on_chunk_generated,
function(e)
log("on_chunk_generated " .. tostring(e.tick))
____exports.handleChunkGeneratedEvent(e)
MapChunkUpdate:handleChunkGeneratedEvent(e)
EntityUpdates:handleChunkGeneratedEvent(e)
end
)
script.on_event(
defines.events.script_raised_set_tiles,
function(event)
log("script_raised_set_tiles " .. tostring(event.tick))
local surface = getSurface(event.surface_index)
if surface == nil then
return
end
local groupedTiles = groupTiles(event.tiles)
for ____, ____value in __TS__Iterator(groupedTiles) do
local chunkPos = ____value[1]
local tiles = ____value[2]
mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
end
MapChunkUpdate:handleScriptSetTilesEvent(event)
end
)
script.on_event(
defines.events.on_player_built_tile,
function(e)
log("on_player_built_tile " .. tostring(e.tick))
handleBuiltTileEvent(e)
MapChunkUpdate:handleBuiltTileEvent(e)
end
)
script.on_event(
defines.events.on_robot_built_tile,
function(e)
log("on_robot_built_tile " .. tostring(e.tick))
handleBuiltTileEvent(e)
MapChunkUpdate:handleBuiltTileEvent(e)
end
)
script.on_event(
defines.events.on_player_mined_tile,
function(e)
log("on_player_mined_tile " .. tostring(e.tick))
handleMinedTileEvent(e)
MapChunkUpdate:handleMinedTileEvent(e)
EntityUpdates:handleMinedTileEvent(e)
end
)
script.on_event(
defines.events.on_robot_mined_tile,
function(e)
log("on_robot_mined_tile " .. tostring(e.tick))
handleMinedTileEvent(e)
MapChunkUpdate:handleMinedTileEvent(e)
end
)
script.on_event(
defines.events.on_pre_chunk_deleted,
function(e)
log("on_pre_chunk_deleted " .. tostring(e.tick))
local surface = getSurface(e.surface_index)
if surface == nil then
return
end
for ____, position in ipairs(e.positions) do
local key = {surfaceIndex = surface.index, chunkPosition = {position.x, position.y}}
EventUpdatesManager:debounce(
key,
Type.MapChunkUpdate,
function(data)
data.isDeleted = true
return true
end,
0
)
end
MapChunkUpdate:handlePreChunkDeleted(e)
end
)
return ____exports

View file

@ -115,7 +115,10 @@ script.on_event(
data.ticksToRespawn = player.ticks_to_respawn or nil
playerOnlineInfo(player, data)
if e.cause ~= nil then
data.diedCause = {unitNumber = e.cause.unit_number or nil, name = e.cause.name, protoType = e.cause.type}
data.diedCause = {
unitNumber = e.cause.unit_number or nil,
protoId = Converters.prototypeId(e.cause.type, e.cause.name)
}
end
end
)

View file

@ -22,7 +22,7 @@ function getMapTilePrototypes()
for ____, tile in pairs(game.tile_prototypes) do
tiles[#tiles + 1] = {
type = FactorioPrototype.Type.MapTile,
name = tile.name,
protoId = Converters.prototypeId("tile", tile.name),
order = tile.order,
layer = tile.layer,
collisionMasks = Converters.convertCollisionMaskToNames(tile.collision_mask),

View file

@ -5,12 +5,12 @@ local ____eventTypeCheck = require("main.events.eventTypeCheck")
local isEventType = ____eventTypeCheck.isEventType
local ____converters = require("main.events.converters")
local Converters = ____converters.Converters
local ____mapChunkUpdates = require("main.events.mapChunkUpdates")
local handleChunkGeneratedEvent = ____mapChunkUpdates.handleChunkGeneratedEvent
local ____surfaceUpdates = require("main.events.surfaceUpdates")
local handleSurfaceUpdate = ____surfaceUpdates.handleSurfaceUpdate
local ____EventDataCache = require("main.cache.EventDataCache")
local EventCacheService = ____EventDataCache.default
local ____mapChunkUpdates = require("main.events.mapChunkUpdates")
local MapChunkUpdate = ____mapChunkUpdates.default
script.on_event(
defines.events.on_tick,
function(event)
@ -38,7 +38,7 @@ script.on_event(
if isEventType(event, defines.events.on_chunk_generated) then
local eName = Converters.eventNameString(event.name)
log((((("[on_tick:" .. tostring(event.tick)) .. "] dequed event ") .. eName) .. ", delay ") .. tostring(i))
handleChunkGeneratedEvent(event, i)
MapChunkUpdate:handleChunkGeneratedEvent(event, i)
i = i + 1
end
end

View file

@ -34,7 +34,7 @@ end
function KafkatorioSettingsConfig.cacheDurationSettingName(self, ____type)
return "kafkatorio:cache-expiration-seconds_" .. __TS__StringReplaceAll(____type, ".", "-")
end
KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds = {["kafkatorio.packet.keyed.PlayerUpdate"] = 0.5, ["kafkatorio.packet.keyed.MapChunkUpdate"] = 30, ["kafkatorio.packet.keyed.EntityUpdate"] = 5}
KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds = {["kafkatorio.packet.keyed.PlayerUpdate"] = 0.5, ["kafkatorio.packet.keyed.MapChunkTileUpdate"] = 30, ["kafkatorio.packet.keyed.MapChunkEntityUpdate"] = 30, ["kafkatorio.packet.keyed.EntityUpdate"] = 5}
local KafkatorioSettings = __TS__New(____exports.KafkatorioSettingsConfig)
____exports.default = KafkatorioSettings
return ____exports

View file

@ -1,14 +1,16 @@
[mod-setting-name]
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-PlayerUpdate=Player update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkUpdate=Map update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkTileUpdate=Map tile update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkEntityUpdate=Entity update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-EntityUpdate=Entity update rate (seconds)
[mod-setting-description]
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-PlayerUpdate=How frequently Player updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (0.25 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkUpdate=How frequently Map updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkTileUpdate=How frequently Map updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkEntityUpdate=How frequently entity updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (30 seconds) is probably fine!
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-EntityUpdate=How frequently Entity updates are sent to the Kafkatorio server, in seconds. Lower means updates are sent more frequently, which may cause network congestion or reduce performance. The default (5 seconds) is probably fine!

View file

@ -1,7 +1,7 @@
import {initGlobal} from "./main/global-init";
import {emitPrototypes} from "./main/events/prototypeUpdates";
require("./main/events/registerHandlers")
require("./main/events/registerEventListeners")
require("./main/events/mapChunkUpdates")
require("./main/events/playerUpdates")
require("./main/events/entityUpdates")

View file

@ -14,7 +14,8 @@ export type KafkatorioPacketData =
| KafkatorioPacketData.ConsoleCommandUpdate
| KafkatorioPacketData.EntityUpdate
| KafkatorioPacketData.Error
| KafkatorioPacketData.MapChunkUpdate
| KafkatorioPacketData.MapChunkEntityUpdate
| KafkatorioPacketData.MapChunkTileUpdate
| KafkatorioPacketData.PlayerUpdate
| KafkatorioPacketData.PrototypesUpdate
| KafkatorioPacketData.SurfaceUpdate;
@ -27,7 +28,8 @@ export namespace KafkatorioPacketData {
PrototypesUpdate = "kafkatorio.packet.instant.PrototypesUpdate",
SurfaceUpdate = "kafkatorio.packet.instant.SurfaceUpdate",
EntityUpdate = "kafkatorio.packet.keyed.EntityUpdate",
MapChunkUpdate = "kafkatorio.packet.keyed.MapChunkUpdate",
MapChunkEntityUpdate = "kafkatorio.packet.keyed.MapChunkEntityUpdate",
MapChunkTileUpdate = "kafkatorio.packet.keyed.MapChunkTileUpdate",
PlayerUpdate = "kafkatorio.packet.keyed.PlayerUpdate",
Error = "kafkatorio.packet.KafkatorioPacketData.Error",
}
@ -77,12 +79,18 @@ export namespace KafkatorioPacketData {
lastUser?: UInt | null;
localisedDescription?: string | null;
localisedName?: string | null;
prototype?: PrototypeName | null;
}
export interface MapChunkUpdate {
type: KafkatorioPacketData.Type.MapChunkUpdate;
key: MapChunkUpdateKey;
export interface MapChunkEntityUpdate {
type: KafkatorioPacketData.Type.MapChunkEntityUpdate;
key: MapChunkEntityUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
distinctEntities?: { [key: string]: FactorioEntityData } | null;
}
export interface MapChunkTileUpdate {
type: KafkatorioPacketData.Type.MapChunkTileUpdate;
key: MapChunkTileUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
player?: PlayerIndex | null;
robot?: EntityIdentifiersData | null;
@ -132,16 +140,11 @@ export interface ConfigurationUpdateGameData {
newVersion?: string | null;
}
export type PlayerIndex = UInt;
export type SurfaceIndex = UInt;
export type Double = double;
export interface EntityUpdateKey {
unitNumber: UnitNumber;
name: string;
protoType: string;
protoId: PrototypeId;
}
export type MapEntityPosition = [
@ -153,32 +156,33 @@ export type UByte = uint8;
export type Float = float;
export type PrototypeName = string;
export interface MapChunkEntityUpdateKey {
protoId: PrototypeId;
surfaceIndex: SurfaceIndex;
chunkPosition: MapChunkPosition;
}
export interface MapChunkUpdateKey {
export interface MapChunkTileUpdateKey {
chunkPosition: MapChunkPosition;
surfaceIndex: SurfaceIndex;
}
export interface EntityIdentifiersData {
unitNumber?: UnitNumber | null;
name: string;
protoType: string;
protoId: PrototypeId;
}
export type ForceIndex = UInt;
export type ForceIndex = UInt & { __ForceIndex__: void };
export interface MapTileDictionary {
tilesXY: { [key: string]: { [key: string]: PrototypeKey } };
protos: { [key: PrototypeName]: PrototypeKey };
protos: { [key: PrototypeId]: PrototypeKey };
}
export interface PlayerUpdateKey {
index: PlayerIndex;
}
export type UnitNumber = UInt;
export type Colour = [
red: Float,
green: Float,
@ -206,7 +210,7 @@ export namespace FactorioPrototype {
export interface Entity {
type: FactorioPrototype.Type.Entity;
name: PrototypeName;
protoId: PrototypeId;
mapColour: Colour;
protoType: string;
objectName: string;
@ -220,7 +224,7 @@ export namespace FactorioPrototype {
export interface MapTile {
type: FactorioPrototype.Type.MapTile;
name: PrototypeName;
protoId: PrototypeId;
mapColour: Colour;
layer: UInt;
collisionMasks: List;
@ -229,7 +233,9 @@ export namespace FactorioPrototype {
}
}
export type EventName = string;
export type PrototypeId = string & { __PrototypeId__: void };
export type EventName = string & { __EventName__: void };
export type Byte = int8;
@ -238,22 +244,114 @@ export type MapChunkPosition = [
y: Int,
];
export type FactorioEntityData =
| FactorioEntityData.Resource
| FactorioEntityData.Standard;
export namespace FactorioEntityData {
export enum Type {
Resource = "kafkatorio.entity.FactorioEntityData.Resource",
Standard = "kafkatorio.entity.FactorioEntityData.Standard",
}
export interface Resource {
type: FactorioEntityData.Type.Resource;
protoId: PrototypeId;
status?: EntityStatus | null;
position: MapEntityPosition;
amount: UInt;
initialAmount?: UInt | null;
}
export interface Standard {
type: FactorioEntityData.Type.Standard;
protoId: PrototypeId;
status?: EntityStatus | null;
position: MapEntityPosition;
graphicsVariation?: UByte | null;
health?: Float | null;
isActive?: boolean | null;
isRotatable?: boolean | null;
lastUser?: UInt | null;
localisedDescription?: string | null;
localisedName?: string | null;
}
}
export interface MiningProperties {
canBeMined: boolean;
products: MinedProduct[] | null;
}
export type List = any;
export type List = any & { __List__: void };
export type PrototypeKey = Int;
export interface MinedProduct {
type: MinedProductType;
name: PrototypeName;
amount: Double | null;
export enum EntityStatus {
CANT_DIVIDE_SEGMENTS = "CANT_DIVIDE_SEGMENTS",
CHARGING = "CHARGING",
CLOSED_BY_CIRCUIT_NETWORK = "CLOSED_BY_CIRCUIT_NETWORK",
DISABLED = "DISABLED",
DISABLED_BY_CONTROL_BEHAVIOR = "DISABLED_BY_CONTROL_BEHAVIOR",
DISABLED_BY_SCRIPT = "DISABLED_BY_SCRIPT",
DISCHARGING = "DISCHARGING",
FLUID_INGREDIENT_SHORTAGE = "FLUID_INGREDIENT_SHORTAGE",
FULLY_CHARGED = "FULLY_CHARGED",
FULL_OUTPUT = "FULL_OUTPUT",
ITEM_INGREDIENT_SHORTAGE = "ITEM_INGREDIENT_SHORTAGE",
LAUNCHING_ROCKET = "LAUNCHING_ROCKET",
LOW_INPUT_FLUID = "LOW_INPUT_FLUID",
LOW_POWER = "LOW_POWER",
LOW_TEMPERATURE = "LOW_TEMPERATURE",
MARKED_FOR_DECONSTRUCTION = "MARKED_FOR_DECONSTRUCTION",
MISSING_REQUIRED_FLUID = "MISSING_REQUIRED_FLUID",
MISSING_SCIENCE_PACKS = "MISSING_SCIENCE_PACKS",
NETWORKS_CONNECTED = "NETWORKS_CONNECTED",
NETWORKS_DISCONNECTED = "NETWORKS_DISCONNECTED",
NORMAL = "NORMAL",
NOT_CONNECTED_TO_RAIL = "NOT_CONNECTED_TO_RAIL",
NOT_PLUGGED_IN_ELECTRIC_NETWORK = "NOT_PLUGGED_IN_ELECTRIC_NETWORK",
NO_AMMO = "NO_AMMO",
NO_FUEL = "NO_FUEL",
NO_INGREDIENTS = "NO_INGREDIENTS",
NO_INPUT_FLUID = "NO_INPUT_FLUID",
NO_MINABLE_RESOURCES = "NO_MINABLE_RESOURCES",
NO_MODULES_TO_TRANSMIT = "NO_MODULES_TO_TRANSMIT",
NO_POWER = "NO_POWER",
NO_RECIPE = "NO_RECIPE",
NO_RESEARCH_IN_PROGRESS = "NO_RESEARCH_IN_PROGRESS",
OPENED_BY_CIRCUIT_NETWORK = "OPENED_BY_CIRCUIT_NETWORK",
OUT_OF_LOGISTIC_NETWORK = "OUT_OF_LOGISTIC_NETWORK",
PREPARING_ROCKET_FOR_LAUNCH = "PREPARING_ROCKET_FOR_LAUNCH",
RECHARGING_AFTER_POWER_OUTAGE = "RECHARGING_AFTER_POWER_OUTAGE",
TURNED_OFF_DURING_DAYTIME = "TURNED_OFF_DURING_DAYTIME",
WAITING_FOR_SOURCE_ITEMS = "WAITING_FOR_SOURCE_ITEMS",
WAITING_FOR_SPACE_IN_DESTINATION = "WAITING_FOR_SPACE_IN_DESTINATION",
WAITING_FOR_TARGET_TO_BE_BUILT = "WAITING_FOR_TARGET_TO_BE_BUILT",
WAITING_FOR_TRAIN = "WAITING_FOR_TRAIN",
WAITING_TO_LAUNCH_ROCKET = "WAITING_TO_LAUNCH_ROCKET",
WORKING = "WORKING",
}
export enum MinedProductType {
item = "item",
fluid = "fluid",
export type PrototypeKey = Int & { __PrototypeKey__: void };
export type MinedProduct =
| MinedProduct.MinedProductFluid
| MinedProduct.MinedProductItem;
export namespace MinedProduct {
export enum Type {
MinedProductFluid = "kafkatorio.resource.MinedProduct.MinedProductFluid",
MinedProductItem = "kafkatorio.resource.MinedProduct.MinedProductItem",
}
export interface MinedProductFluid {
type: MinedProduct.Type.MinedProductFluid;
amount?: Double | null;
resultProtoId: PrototypeId;
}
export interface MinedProductItem {
type: MinedProduct.Type.MinedProductItem;
amount?: Double | null;
resultProtoId: PrototypeId;
}
}

View file

@ -101,7 +101,7 @@ export namespace EventUpdates {
let countExpired = 0
let countTotal = 0
const expiredData: Array<KafkatorioKeyedPacketData> = []
for (let [key, entry] of global.cache) {
for (const [key, entry] of global.cache) {
countTotal++
if (this.isExpired(entry)) {
countExpired++

View file

@ -23,7 +23,7 @@ script.on_event(
const update: KafkatorioPacketData.ConsoleCommandUpdate = {
type: Type.ConsoleCommandUpdate,
authorPlayerIndex: event.player_index ?? null,
authorPlayerIndex: event.player_index ?? null,
command: event.command,
parameters: event.parameters,
}

View file

@ -1,16 +1,25 @@
import {EventName} from "../types";
import {Colour, MapChunkPosition} from "../../generated/kafkatorio-schema";
import {DefinedEventName} from "../types";
import {
Colour,
EntityStatus,
EventName,
FactorioEntityData,
MapChunkPosition,
PrototypeId,
} from "../../generated/kafkatorio-schema";
export namespace Converters {
const mapEventIdToName = new LuaTable<defines.Events, EventName>()
import floor = math.floor;
const mapEventIdToName = new LuaTable<defines.Events, DefinedEventName>()
for (const [eventName, eventId] of pairs(defines.events)) {
mapEventIdToName.set(eventId, eventName)
}
export function eventNameString(event: defines.Events): EventName {
return mapEventIdToName.get(event)
return mapEventIdToName.get(event) as EventName
}
@ -60,10 +69,115 @@ export namespace Converters {
export function convertCollisionMaskToNames(cm: CollisionMask): string[] {
let masks: string[] = []
for (let [name] of pairs(cm)) {
for (const [name] of pairs(cm)) {
masks.push(name)
}
return masks
}
export function convertResourceEntity(
entity: LuaEntity
): FactorioEntityData.Resource | null {
if (entity.type != "resource") {
return null
} else {
return {
type: FactorioEntityData.Type.Resource,
protoId: prototypeId(entity.type, entity.name),
status: convertEntityStatus(entity.status),
amount: entity.amount,
initialAmount: entity.initial_amount,
position: convertMapTablePosition(entity.position),
}
}
}
function convertEntityStatus(
status: defines.entity_status | undefined
): EntityStatus | null {
if (status == undefined) {
return null
} else {
return EntityStatusRecord[status]
}
}
export function prototypeIdEntity(entity: LuaEntity): PrototypeId {
return prototypeId(entity.type, entity.name)
}
export function prototypeId(type: string, name: string): PrototypeId {
return `${type}/${name}` as PrototypeId
}
export function tilePositionToChunkPosition(mapPosition: TilePositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
}
export function mapPositionToChunkPosition(mapPosition: MapPositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
}
export function entityStatus(status: defines.entity_status | undefined): EntityStatus | undefined {
if (status == undefined) {
return undefined
} else {
return EntityStatusRecord[status]
}
}
const EntityStatusRecord: Record<defines.entity_status, EntityStatus> = {
//@formatter:off
[ defines.entity_status.cant_divide_segments ]: EntityStatus.CANT_DIVIDE_SEGMENTS,
[ defines.entity_status.charging ]: EntityStatus.CHARGING,
[ defines.entity_status.closed_by_circuit_network ]: EntityStatus.CLOSED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.disabled ]: EntityStatus.DISABLED,
[ defines.entity_status.disabled_by_control_behavior ]: EntityStatus.DISABLED_BY_CONTROL_BEHAVIOR,
[ defines.entity_status.disabled_by_script ]: EntityStatus.DISABLED_BY_SCRIPT,
[ defines.entity_status.discharging ]: EntityStatus.DISCHARGING,
[ defines.entity_status.fluid_ingredient_shortage ]: EntityStatus.FLUID_INGREDIENT_SHORTAGE,
[ defines.entity_status.fully_charged ]: EntityStatus.FULLY_CHARGED,
[ defines.entity_status.full_output ]: EntityStatus.FULL_OUTPUT,
[ defines.entity_status.item_ingredient_shortage ]: EntityStatus.ITEM_INGREDIENT_SHORTAGE,
[ defines.entity_status.launching_rocket ]: EntityStatus.LAUNCHING_ROCKET,
[ defines.entity_status.low_input_fluid ]: EntityStatus.LOW_INPUT_FLUID,
[ defines.entity_status.low_power ]: EntityStatus.LOW_POWER,
[ defines.entity_status.low_temperature ]: EntityStatus.LOW_TEMPERATURE,
[ defines.entity_status.marked_for_deconstruction ]: EntityStatus.MARKED_FOR_DECONSTRUCTION,
[ defines.entity_status.missing_required_fluid ]: EntityStatus.MISSING_REQUIRED_FLUID,
[ defines.entity_status.missing_science_packs ]: EntityStatus.MISSING_SCIENCE_PACKS,
[ defines.entity_status.networks_connected ]: EntityStatus.NETWORKS_CONNECTED,
[ defines.entity_status.networks_disconnected ]: EntityStatus.NETWORKS_DISCONNECTED,
[ defines.entity_status.normal ]: EntityStatus.NORMAL,
[ defines.entity_status.not_connected_to_rail ]: EntityStatus.NOT_CONNECTED_TO_RAIL,
[ defines.entity_status.not_plugged_in_electric_network ]: EntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK,
[ defines.entity_status.no_ammo ]: EntityStatus.NO_AMMO,
[ defines.entity_status.no_fuel ]: EntityStatus.NO_FUEL,
[ defines.entity_status.no_ingredients ]: EntityStatus.NO_INGREDIENTS,
[ defines.entity_status.no_input_fluid ]: EntityStatus.NO_INPUT_FLUID,
[ defines.entity_status.no_minable_resources ]: EntityStatus.NO_MINABLE_RESOURCES,
[ defines.entity_status.no_modules_to_transmit ]: EntityStatus.NO_MODULES_TO_TRANSMIT,
[ defines.entity_status.no_power ]: EntityStatus.NO_POWER,
[ defines.entity_status.no_recipe ]: EntityStatus.NO_RECIPE,
[ defines.entity_status.no_research_in_progress ]: EntityStatus.NO_RESEARCH_IN_PROGRESS,
[ defines.entity_status.opened_by_circuit_network ]: EntityStatus.OPENED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.out_of_logistic_network ]: EntityStatus.OUT_OF_LOGISTIC_NETWORK,
[ defines.entity_status.preparing_rocket_for_launch ]: EntityStatus.PREPARING_ROCKET_FOR_LAUNCH,
[ defines.entity_status.recharging_after_power_outage ]: EntityStatus.RECHARGING_AFTER_POWER_OUTAGE,
[ defines.entity_status.turned_off_during_daytime ]: EntityStatus.TURNED_OFF_DURING_DAYTIME,
[ defines.entity_status.waiting_for_source_items ]: EntityStatus.WAITING_FOR_SOURCE_ITEMS,
[ defines.entity_status.waiting_for_space_in_destination ]: EntityStatus.WAITING_FOR_SPACE_IN_DESTINATION,
[ defines.entity_status.waiting_for_target_to_be_built ]: EntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT,
[ defines.entity_status.waiting_for_train ]: EntityStatus.WAITING_FOR_TRAIN,
[ defines.entity_status.waiting_to_launch_rocket ]: EntityStatus.WAITING_TO_LAUNCH_ROCKET,
[ defines.entity_status.working ]: EntityStatus.WORKING,
//@formatter:on
}
}

View file

@ -1,97 +1,276 @@
import {Converters} from "./converters";
import EventDataCache from "../cache/EventDataCache";
import {
EntityUpdateKey,
KafkatorioPacketData
EventName,
FactorioEntityData,
KafkatorioPacketData,
MapChunkPosition,
PrototypeId,
Tick,
} from "../../generated/kafkatorio-schema";
export class EntityUpdatesHandler {
handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == null) {
return
}
const tilesByChunk: LuaTable<MapChunkPosition, OldTileAndPosition> = new LuaTable<MapChunkPosition, OldTileAndPosition>()
for (const tile of event.tiles) {
const chunkPosition = Converters.tilePositionToChunkPosition(tile.position)
tilesByChunk.set(chunkPosition, tile)
}
for (const [chunkPos, tiles] of pairs(tilesByChunk)) {
const entities: FactorioEntityData.Resource[] = []
for (const tile in tiles) {
const resources: FactorioEntityData.Resource[] = EntityUpdatesHandler.getTileResourceEntities(surface, chunkPos)
for (const resource of resources) {
entities[entities.length] = resource
}
}
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface_index,
[chunkPos[0], chunkPos[1]],
entities,
)
}
}
handleMinedEntityEvent(
event: OnPlayerMinedEntityEvent | OnRobotMinedEntityEvent,
) {
EntityUpdatesHandler.throttleEntityUpdate(event, event.entity)
}
handleBuiltEntityEvent(
event: OnBuiltEntityEvent | OnRobotBuiltEntityEvent,
) {
EntityUpdatesHandler.throttleEntityUpdate(event, event.created_entity)
}
handleChunkGeneratedEvent(event: OnChunkGeneratedEvent) {
const entities = EntityUpdatesHandler.getAreaResourceEntities(event.surface, event.area)
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface.index,
[event.position.x, event.position.y],
entities,
)
}
private static throttleEntityUpdate(event: EntityUpdateEvent, entity: LuaEntity) {
if (entity.unit_number == undefined) {
// ignore for now - I'm not sure how to track entities without a unit_number
return
}
const eventName = Converters.eventNameString(event.name)
const entityUpdateKey: KafkatorioPacketData.MapChunkEntityUpdate["key"] = {
protoId: Converters.prototypeIdEntity(entity),
surfaceIndex: entity.surface.index,
chunkPosition: Converters.mapPositionToChunkPosition(entity.position)
}
const entityUpdate: FactorioEntityData.Standard = {
type: FactorioEntityData.Type.Standard,
protoId: Converters.prototypeIdEntity(entity),
graphicsVariation: entity.graphics_variation,
health: entity.health,
isActive: entity.active,
isRotatable: entity.rotatable,
lastUser: entity.last_user?.index,
localisedDescription: null,
localisedName: null,
position: [entity.position.x, entity.position.y],
status: Converters.entityStatus(entity.status),
}
const entityKey: string = `${entity.unit_number}`
EventDataCache.throttle<KafkatorioPacketData.MapChunkEntityUpdate>(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entityUpdate
}
)
}
private static throttleResourceEntitiesUpdate(
eventName: EventName,
eventTick: Tick,
surfaceIndex: SurfaceIndex,
chunkPosition: MapChunkPosition,
entities: FactorioEntityData.Resource[],
) {
// first group by prototype ID
const entitiesByProtoId: { [protoId: PrototypeId]: FactorioEntityData.Resource[] } = {}
for (const entity of entities) {
entitiesByProtoId[entity.protoId] ??= []
entitiesByProtoId[entity.protoId].push(entity)
}
for (const [protoId, resourceEntities] of pairs(entitiesByProtoId)) {
// create an update for each prototype
const entityUpdateKey: KafkatorioPacketData.MapChunkEntityUpdate["key"] = {
protoId: protoId,
surfaceIndex: surfaceIndex,
chunkPosition: [chunkPosition[0], chunkPosition[1]],
}
EventDataCache.throttle<KafkatorioPacketData.MapChunkEntityUpdate>(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(eventTick)
for (const entity of resourceEntities) {
const [x, y] = entity.position
const entityKey: string = `${x},${y}`
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entity
}
}
)
}
}
/** Get all resources on a tile-position */
private static getTileResourceEntities(
surface: LuaSurface,
mapPosition: MapPosition,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
position: mapPosition,
collision_mask: "resource-layer",
}
)
const resources: FactorioEntityData.Resource[] = []
for (const rawEntity of rawEntities) {
const entity = Converters.convertResourceEntity(rawEntity)
if (entity != null) {
resources[resources.length] = entity
}
}
return resources
}
/** Get all resources within a map-area */
private static getAreaResourceEntities(
surface: LuaSurface,
area: BoundingBoxTable,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
area: area,
collision_mask: "resource-layer",
}
)
const resources: FactorioEntityData.Resource[] = []
for (const rawEntity of rawEntities) {
const entity = Converters.convertResourceEntity(rawEntity)
if (entity != null) {
resources[resources.length] = entity
}
}
return resources
}
}
const EntityUpdates = new EntityUpdatesHandler()
export default EntityUpdates
script.on_event(defines.events.on_built_entity, (e: OnBuiltEntityEvent) => {
log(`on_built_entity ${e.tick}`)
handleBuiltEntityEvent(e)
EntityUpdates.handleBuiltEntityEvent(e)
})
script.on_event(defines.events.on_robot_built_entity, (e: OnRobotBuiltEntityEvent) => {
log(`on_robot_built_entity ${e.tick}`)
handleBuiltEntityEvent(e)
EntityUpdates.handleBuiltEntityEvent(e)
})
function handleBuiltEntityEvent(event: OnBuiltEntityEvent | OnRobotBuiltEntityEvent) {
const eventName = Converters.eventNameString(event.name)
if (event.created_entity.unit_number == undefined) {
return
}
const key: EntityUpdateKey = {
name: event.created_entity.name,
protoType: event.created_entity.prototype.name,
unitNumber: event.created_entity.unit_number
}
EventDataCache.debounce<KafkatorioPacketData.EntityUpdate>(
key,
KafkatorioPacketData.Type.EntityUpdate,
data => {
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
data.chunkPosition = Converters.convertMapTablePosition(event.created_entity.position)
data.graphicsVariation = event.created_entity.graphics_variation
data.health = event.created_entity.health
data.isActive = event.created_entity.active
data.isRotatable = event.created_entity.rotatable
data.lastUser = event.created_entity.last_user?.index
data.prototype = event.created_entity.prototype.name
}
)
}
script.on_event(defines.events.on_player_mined_entity, (e: OnPlayerMinedEntityEvent) => {
log(`on_player_mined_entity ${e.tick}`)
handleMinedEntityEvent(e)
EntityUpdates.handleMinedEntityEvent(e)
})
script.on_event(defines.events.on_robot_mined_entity, (e: OnRobotMinedEntityEvent) => {
log(`on_robot_mined_entity ${e.tick}`)
handleMinedEntityEvent(e)
EntityUpdates.handleMinedEntityEvent(e)
})
function handleMinedEntityEvent(event: OnPlayerMinedEntityEvent | OnRobotMinedEntityEvent) {
const eventName = Converters.eventNameString(event.name)
type EntityUpdateEvent =
| OnBuiltEntityEvent
if (event.entity.unit_number == undefined) {
return
}
| OnEntityClonedEvent
| OnEntityDamagedEvent
| OnEntityDestroyedEvent
| OnEntityDiedEvent
| OnEntityLogisticSlotChangedEvent
| OnEntityRenamedEvent
| OnEntitySettingsPastedEvent
| OnEntitySpawnedEvent
const key: EntityUpdateKey = {
name: event.entity.name,
protoType: event.entity.prototype.name,
unitNumber: event.entity.unit_number
}
| OnPlayerMinedEntityEvent
| OnPlayerRepairedEntityEvent
| OnPlayerRotatedEntityEvent
EventDataCache.debounce<KafkatorioPacketData.EntityUpdate>(
key,
KafkatorioPacketData.Type.EntityUpdate,
data => {
| OnRobotMinedEntityEvent
| OnRobotBuiltEntityEvent
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
| OnPostEntityDiedEvent
| OnPreEntitySettingsPastedEvent
data.chunkPosition = Converters.convertMapTablePosition(event.entity.position)
data.graphicsVariation = event.entity.graphics_variation
data.health = event.entity.health
data.isActive = event.entity.active
data.isRotatable = event.entity.rotatable
data.lastUser = event.entity.last_user?.index
data.prototype = event.entity.prototype.name
}
)
}
| OnSelectedEntityChangedEvent
| OnTriggerCreatedEntityEvent

View file

@ -3,129 +3,270 @@ import {Converters} from "./converters";
import EventUpdatesManager, {EventUpdates} from "../cache/EventDataCache";
import {
KafkatorioPacketData,
MapChunkPosition
MapChunkPosition, PrototypeId,
PrototypeKey,
} from "../../generated/kafkatorio-schema";
import EntityUpdates from "./entityUpdates";
import CacheKey = EventUpdates.PacketKey;
import Type = KafkatorioPacketData.Type;
type MapChunkUpdater = (data: KafkatorioPacketData.MapChunkUpdate) => void
type MapChunkTileUpdater = (data: KafkatorioPacketData.MapChunkTileUpdate) => void
type MapTileUpdateEvent =
| OnChunkGeneratedEvent
export type MapTileChangeEvent =
| OnPlayerBuiltTileEvent
| OnPlayerMinedEntityEvent
| OnPlayerMinedTileEvent
| OnRobotBuiltTileEvent
| OnRobotMinedEntityEvent
| OnRobotMinedTileEvent
| ScriptRaisedSetTilesEvent
function mapTilesUpdateDebounce(
surface: LuaSurface | undefined,
chunkPosition: MapChunkPosition,
tiles: TileRead[],
event: MapTileUpdateEvent,
updater?: MapChunkUpdater,
expirationDurationTicks?: uint,
) {
export type MapChunkUpdateEvent =
| OnChunkChartedEvent
| OnChunkDeletedEvent
| OnChunkGeneratedEvent
| OnPreChunkDeletedEvent
if (surface == undefined) {
return
class MapChunkUpdateHandler {
handleChunkGeneratedEvent(
event: OnChunkGeneratedEvent,
expirationDurationTicks?: uint,
) {
const tiles = event.surface.find_tiles_filtered({area: event.area})
MapChunkUpdateHandler.mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
event,
undefined,
expirationDurationTicks,
)
}
const eventName = Converters.eventNameString(event.name)
const key: KafkatorioPacketData.MapChunkUpdate["key"] = {
surfaceIndex: surface.index,
chunkPosition: chunkPosition,
handleBuiltTileEvent(event: OnPlayerBuiltTileEvent | OnRobotBuiltTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleBuiltTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertPlacedTiles(event.tile, event.tiles)
const groupedTiles = MapChunkUpdateHandler.groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
MapChunkUpdateHandler.mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_built_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_built_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
protoId: Converters.prototypeId(event.robot.type, event.robot.name),
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkUpdate>(
key,
Type.MapChunkUpdate,
data => {
// tile dictionary update
if (data.tileDictionary == undefined) {
data.tileDictionary = {
tilesXY: {},
protos: {},
}
}
handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleMinedTileEvent] undefined surface ${eventName}`)
return
}
let protosCount: uint = table_size(data.tileDictionary.protos)
for (const tile of tiles) {
data.tileDictionary.protos[tile.name] ??= protosCount++
const tiles: TileRead[] = Converters.convertRemovedTiles(surface, event.tiles)
const groupedTiles = MapChunkUpdateHandler.groupTiles(tiles)
const protoKey = data.tileDictionary.protos[tile.name]
for (const [chunkPos, tiles] of groupedTiles) {
const xString = `${tile.position.x}`
const yString = `${tile.position.y}`
data.tileDictionary.tilesXY[xString] ??= {}
data.tileDictionary.tilesXY[xString][yString] = protoKey
}
// events count update
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
// apply mutator
if (updater != undefined) {
updater(data)
}
},
expirationDurationTicks,
)
}
MapChunkUpdateHandler.mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_mined_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_mined_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
protoId: Converters.prototypeId(event.robot.type, event.robot.name),
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
function getTiles(
surface: LuaSurface | undefined,
area: BoundingBoxTable
): LuaTile[] {
return surface?.find_tiles_filtered({
area: area,
// collision_mask: ["ground-tile", "water-tile"]
handleScriptSetTilesEvent(event: ScriptRaisedSetTilesEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
return
}
const groupedTiles = MapChunkUpdateHandler.groupTiles(event.tiles)
for (const [chunkPos, tiles] of groupedTiles) {
MapChunkUpdateHandler.mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
}
}
handlePreChunkDeleted(event: OnPreChunkDeletedEvent) {
const surface = game.get_surface(event.surface_index)
if (surface == undefined) {
return
}
for (const position of event.positions) {
const key: CacheKey<KafkatorioPacketData.MapChunkTileUpdate> = {
surfaceIndex: surface.index,
chunkPosition: [position.x, position.y],
}
) ?? []
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key,
Type.MapChunkTileUpdate,
data => data.isDeleted = true,
0, // deletion is important - emit ASAP
)
}
}
private static mapTilesUpdateDebounce(
surface: LuaSurface | undefined,
chunkPosition: MapChunkPosition,
tiles: TileRead[],
event: MapChunkUpdateEvent | MapTileChangeEvent,
updater?: MapChunkTileUpdater,
expirationDurationTicks?: uint,
) {
if (surface == undefined) {
return
}
const eventName = Converters.eventNameString(event.name)
const key: KafkatorioPacketData.MapChunkTileUpdate["key"] = {
surfaceIndex: surface.index,
chunkPosition: chunkPosition,
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
key,
Type.MapChunkTileUpdate,
data => {
// tile dictionary update
if (data.tileDictionary == undefined) {
data.tileDictionary = {
tilesXY: {},
protos: {},
}
}
let protosCount: uint = table_size(data.tileDictionary.protos)
for (const tile of tiles) {
const protoId = Converters.prototypeId("tile", tile.name)
if (data.tileDictionary.protos[protoId] == null) {
}
data.tileDictionary.protos[protoId] ??= protosCount++ as PrototypeKey
const protoKey = data.tileDictionary.protos[protoId]
const xString = `${tile.position.x}`
const yString = `${tile.position.y}`
data.tileDictionary.tilesXY[xString] ??= {}
data.tileDictionary.tilesXY[xString][yString] = protoKey
}
// events count update
data.events ??= {}
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
// apply mutator
if (updater != undefined) {
updater(data)
}
},
expirationDurationTicks,
)
}
private static groupTiles(tiles: TileRead[]): Map<MapChunkPosition, TileRead[]> {
const mapChunkPositionToTiles = new Map<MapChunkPosition, TileRead[]>()
for (const tile of tiles) {
const chunkPosition: MapChunkPosition = [
math.floor((tile.position.x / 32)),
math.floor((tile.position.y / 32)),
]
if (!mapChunkPositionToTiles.has(chunkPosition)) {
mapChunkPositionToTiles.set(chunkPosition, [])
}
mapChunkPositionToTiles.get(chunkPosition)?.push(tile)
}
return mapChunkPositionToTiles
}
}
function getSurface(surfaceIndex: uint): LuaSurface | undefined {
return game.surfaces[surfaceIndex]
}
const MapChunkUpdate = new MapChunkUpdateHandler()
export default MapChunkUpdate
script.on_event(
defines.events.on_chunk_generated,
(e: OnChunkGeneratedEvent) => {
log(`on_chunk_generated ${e.tick}`)
handleChunkGeneratedEvent(e)
MapChunkUpdate.handleChunkGeneratedEvent(e)
EntityUpdates.handleChunkGeneratedEvent(e)
}
)
export function handleChunkGeneratedEvent(
event: OnChunkGeneratedEvent,
expirationDurationTicks?: uint,
) {
let tiles = getTiles(event.surface, event.area)
mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
tiles,
event,
undefined,
expirationDurationTicks,
)
}
// script.on_event(
// defines.events.on_chunk_charted,
// (e: OnChunkChartedEvent) => {
@ -144,169 +285,38 @@ export function handleChunkGeneratedEvent(
// )
script.on_event(
defines.events.script_raised_set_tiles,
(event: ScriptRaisedSetTilesEvent) => {
log(`script_raised_set_tiles ${event.tick}`)
const surface = getSurface(event.surface_index)
if (surface == undefined) {
return
}
const groupedTiles = groupTiles(event.tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(surface, chunkPos, tiles, event)
}
}
)
function groupTiles(tiles: TileRead[]): Map<MapChunkPosition, TileRead[]> {
const mapChunkPositionToTiles = new Map<MapChunkPosition, TileRead[]>()
for (const tile of tiles) {
const chunkPosition: MapChunkPosition = [
math.floor((tile.position.x / 32)),
math.floor((tile.position.y / 32)),
]
if (!mapChunkPositionToTiles.has(chunkPosition)) {
mapChunkPositionToTiles.set(chunkPosition, [])
}
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1256
mapChunkPositionToTiles.get(chunkPosition)?.push(tile)
// const tiles = mapChunkPositionToTiles.get(chunkPosition)
// if (tiles != null) {
// tiles.push(tile)
// }
}
return mapChunkPositionToTiles
}
script.on_event(defines.events.script_raised_set_tiles, (event: ScriptRaisedSetTilesEvent) => {
log(`script_raised_set_tiles ${event.tick}`)
MapChunkUpdate.handleScriptSetTilesEvent(event)
})
script.on_event(defines.events.on_player_built_tile, (e: OnPlayerBuiltTileEvent) => {
log(`on_player_built_tile ${e.tick}`)
handleBuiltTileEvent(e)
MapChunkUpdate.handleBuiltTileEvent(e)
})
script.on_event(defines.events.on_robot_built_tile, (e: OnRobotBuiltTileEvent) => {
log(`on_robot_built_tile ${e.tick}`)
handleBuiltTileEvent(e)
MapChunkUpdate.handleBuiltTileEvent(e)
})
function handleBuiltTileEvent(event: OnPlayerBuiltTileEvent | OnRobotBuiltTileEvent) {
const surface = getSurface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleBuiltTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertPlacedTiles(event.tile, event.tiles)
const groupedTiles = groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_built_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_built_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
name: event.robot.name,
protoType: event.robot.type,
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
script.on_event(defines.events.on_player_mined_tile, (e: OnPlayerMinedTileEvent) => {
log(`on_player_mined_tile ${e.tick}`)
handleMinedTileEvent(e)
MapChunkUpdate.handleMinedTileEvent(e)
EntityUpdates.handleMinedTileEvent(e)
})
script.on_event(defines.events.on_robot_mined_tile, (e: OnRobotMinedTileEvent) => {
log(`on_robot_mined_tile ${e.tick}`)
handleMinedTileEvent(e)
MapChunkUpdate.handleMinedTileEvent(e)
})
function handleMinedTileEvent(event: OnPlayerMinedTileEvent | OnRobotMinedTileEvent) {
const surface = getSurface(event.surface_index)
if (surface == undefined) {
const eventName = Converters.eventNameString(event.name)
log(`[handleMinedTileEvent] undefined surface ${eventName}`)
return
}
const tiles: TileRead[] = Converters.convertRemovedTiles(surface, event.tiles)
const groupedTiles = groupTiles(tiles)
for (const [chunkPos, tiles] of groupedTiles) {
mapTilesUpdateDebounce(
surface,
chunkPos,
tiles,
event,
(data => {
if (isEventType(event, defines.events.on_player_mined_tile)) {
data.player = event.player_index
return
}
if (isEventType(event, defines.events.on_robot_mined_tile)) {
data.robot = {
unitNumber: event.robot?.unit_number ?? null,
name: event.robot.name,
protoType: event.robot.type,
}
return
}
// this line will not compile if the previous 'if' statements are not exhaustive
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
const exhaustiveCheck: never = event
}
)
)
}
}
script.on_event(
defines.events.on_pre_chunk_deleted,
(e: OnPreChunkDeletedEvent) => {
log(`on_pre_chunk_deleted ${e.tick}`)
const surface = getSurface(e.surface_index)
if (surface == undefined) {
return
}
for (const position of e.positions) {
const key: CacheKey<KafkatorioPacketData.MapChunkUpdate> = {
surfaceIndex: surface.index,
chunkPosition: [position.x, position.y],
}
EventUpdatesManager.debounce<KafkatorioPacketData.MapChunkUpdate>(
key,
Type.MapChunkUpdate,
data => data.isDeleted = true,
0, // deletion is important - emit ASAP
)
}
}
)
script.on_event(defines.events.on_pre_chunk_deleted, (e: OnPreChunkDeletedEvent) => {
log(`on_pre_chunk_deleted ${e.tick}`)
MapChunkUpdate.handlePreChunkDeleted(e)
})

View file

@ -1,11 +1,8 @@
import {Converters} from "./converters";
import EventUpdatesManager from "../cache/EventDataCache";
import {
KafkatorioPacketData,
PlayerUpdateKey
} from "../../generated/kafkatorio-schema";
import Type = KafkatorioPacketData.Type;
import {ForceIndex, KafkatorioPacketData, PlayerUpdateKey} from "../../generated/kafkatorio-schema";
import packetEmitter from "../PacketEmitter";
import Type = KafkatorioPacketData.Type;
type PlayerUpdater = (player: LuaPlayer, data: KafkatorioPacketData.PlayerUpdate) => void
@ -36,7 +33,7 @@ function playerUpdateThrottle(
const eventName = Converters.eventNameString(event.name)
EventUpdatesManager.throttle<KafkatorioPacketData.PlayerUpdate>(
{index: playerIndex},
{index: playerIndex },
Type.PlayerUpdate,
data => {
const player = game.players[playerIndex]
@ -88,7 +85,7 @@ script.on_event(
data.characterUnitNumber = player.character?.unit_number ?? null
data.chatColour = Converters.mapColour(player.chat_color)
data.colour = Converters.mapColour(player.color)
data.forceIndex = player.force.index
data.forceIndex = player.force.index as ForceIndex
data.name = player.name
data.isShowOnMap = player.show_on_map
data.isSpectator = player.spectator
@ -139,8 +136,7 @@ script.on_event(
if (e.cause != undefined) {
data.diedCause = {
unitNumber: e.cause.unit_number ?? null,
name: e.cause.name,
protoType: e.cause.type
protoId: Converters.prototypeId(e.cause.type, e.cause.name),
}
}
}
@ -189,7 +185,7 @@ script.on_event(
log(`on_pre_player_left_game ${event.tick} ${event.name}`)
const playerUpdateKey: PlayerUpdateKey = {
index: event.player_index
index: event.player_index ,
}
const playerUpdate: KafkatorioPacketData.PlayerUpdate = {

View file

@ -26,11 +26,11 @@ function prototypeUpdates(): FactorioPrototype[] {
function getMapTilePrototypes(): FactorioPrototype.MapTile[] {
let tiles: FactorioPrototype.MapTile[] = []
for (let [, tile] of game.tile_prototypes) {
for (const [, tile] of game.tile_prototypes) {
tiles.push({
type: FactorioPrototype.Type.MapTile,
name: tile.name,
protoId: Converters.prototypeId("tile", tile.name),
order: tile.order,
layer: tile.layer,
collisionMasks: Converters.convertCollisionMaskToNames(tile.collision_mask),
@ -44,7 +44,7 @@ function getMapTilePrototypes(): FactorioPrototype.MapTile[] {
// function getEntityPrototypes(): FactorioPrototype.MapTile[] {
// let tiles: FactorioPrototype.MapTile[] = []
// for (let [, tile] of game.tile_prototypes) {
// for (const [, tile] of game.tile_prototypes) {
// tiles.push({
// type: FactorioPrototype.Type.MapTile,
//

View file

@ -1,9 +1,9 @@
import {Queue} from "../queue/queue";
import {isEventType} from "./eventTypeCheck";
import {Converters} from "./converters";
import {handleChunkGeneratedEvent} from "./mapChunkUpdates";
import {handleSurfaceUpdate} from "./surfaceUpdates";
import EventCacheService from "../cache/EventDataCache";
import MapChunkUpdate from "./mapChunkUpdates";
script.on_event(
@ -35,7 +35,7 @@ script.on_event(
let eName = Converters.eventNameString(event.name)
log(`[on_tick:${event.tick}] dequed event ${eName}, delay ${i}`)
handleChunkGeneratedEvent(event, i)
MapChunkUpdate.handleChunkGeneratedEvent(event, i)
i += 1
}
}

View file

@ -0,0 +1,99 @@
// import {isEventType} from "./eventTypeCheck";
// import {Converters} from "./converters";
// import EventUpdatesManager, {EventUpdates} from "../cache/EventDataCache";
// import {
// KafkatorioPacketData,
// MapChunkPosition,
// PrototypeKey,
// } from "../../generated/kafkatorio-schema";
// import CacheKey = EventUpdates.PacketKey;
// import Type = KafkatorioPacketData.Type;
//
//
// export class ResourceEntityUpdatesHandler {
//
//
// handleChunkGeneratedEvent(
// event: OnChunkGeneratedEvent,
// expirationDurationTicks?: uint,
// ) {
// const resourceEntities = this.getAreaResourceEntities(event.surface, event.area)
//
// mapTilesUpdateDebounce(
// event.surface,
// Converters.convertMapTablePosition(event.position),
// tiles,
// event,
// undefined,
// expirationDurationTicks,
// )
// }
//
//
// private resourceEntityUpdatesThrottle(
// surface: LuaSurface | undefined,
// chunkPosition: MapChunkPosition,
// tiles: TileRead[],
// event: MapTileUpdateEvent,
// updater?: MapChunkUpdater,
// expirationDurationTicks?: uint,
// ) {
//
// if (surface == undefined) {
// return
// }
//
// const eventName = Converters.eventNameString(event.name)
//
// const key: KafkatorioPacketData.ResourceEntitiesUpdate["key"] = {
// surfaceIndex: surface.index as SurfaceIndex,
// chunkPosition: chunkPosition,
// }
//
// EventUpdatesManager.throttle<KafkatorioPacketData.ResourceEntitiesUpdate>(
// key,
// Type.MapChunkUpdate,
// data => {
//
// // tile dictionary update
// if (data.tileDictionary == undefined) {
// data.tileDictionary = {
// tilesXY: {},
// protos: {},
// }
// }
//
// let protosCount: uint = table_size(data.tileDictionary.protos)
// for (const tile of tiles) {
// const tileName = tile.name as PrototypeIdMapTile
// const protoKey = protosCount++ as PrototypeKey
// data.tileDictionary.protos[tileName] ??= protoKey
//
// const xString = `${tile.position.x}`
// const yString = `${tile.position.y}`
//
// data.tileDictionary.tilesXY[xString] ??= {}
// data.tileDictionary.tilesXY[xString][yString] = protoKey
// }
//
// // events count update
// data.events ??= {}
// data.events[eventName] ??= []
// data.events[eventName].push(event.tick)
//
// // apply mutator
// if (updater != undefined) {
// updater(data)
// }
// },
// expirationDurationTicks,
// )
// }
//
// }
//
//
//
// const ResourceEntityUpdates = new ResourceEntityUpdatesHandler()
//
// export default ResourceEntityUpdates

View file

@ -13,7 +13,7 @@ export function handleSurfaceUpdate(
name: surface.name,
index: surface.index,
daytime: surface.daytime
daytime: surface.daytime,
}
PacketEmitter.emitInstantPacket(update)

View file

@ -6,7 +6,8 @@ export class KafkatorioSettingsConfig {
private static eventCacheExpirationDefaultSeconds: Record<KafkatorioKeyedPacketTypes, double> = {
"kafkatorio.packet.keyed.PlayerUpdate": 0.5,
"kafkatorio.packet.keyed.MapChunkUpdate": 30,
"kafkatorio.packet.keyed.MapChunkTileUpdate": 30,
"kafkatorio.packet.keyed.MapChunkEntityUpdate": 30,
"kafkatorio.packet.keyed.EntityUpdate": 5,
}
@ -21,7 +22,7 @@ export class KafkatorioSettingsConfig {
public initialiseSettings(data: Data): void {
for (const [type,] of pairs(KafkatorioSettingsConfig.eventCacheExpirationDefaultSeconds)) {
data.extend(
[ KafkatorioSettingsConfig.createDefaultCacheDuration(type)]
[KafkatorioSettingsConfig.createDefaultCacheDuration(type)]
)
}
}

View file

@ -1,6 +1,6 @@
import {KafkatorioPacketData} from "../generated/kafkatorio-schema";
export type EventName = keyof typeof defines.events
export type DefinedEventName = keyof typeof defines.events
/** Get all keys of `TYPE` that are not nullable. */

View file

@ -10,7 +10,8 @@
"types": [
"typescript-to-lua/language-extensions",
"lua-types/5.2",
"typed-factorio/runtime"
"typed-factorio/runtime",
"typed-factorio/strict-index-types"
],
"plugins": [
{
@ -32,8 +33,7 @@
"traceResolution": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true
"noFallthroughCasesInSwitch": true
},
"tstl": {
"luaTarget": "5.2",

View file

@ -2,11 +2,13 @@ package dev.adamko.kafkatorio.server.processor
import com.github.palindromicity.syslog.dsl.SyslogFieldKeys
import dev.adamko.kafkatorio.server.config.ApplicationProperties
import dev.adamko.kafkatorio.server.processor.topology.broadcastToWebsocket
import dev.adamko.kafkatorio.server.processor.topology.colourMapChunks
import dev.adamko.kafkatorio.server.processor.topology.*
import dev.adamko.kafkatorio.server.processor.topology.factorioServerPacketStream
import dev.adamko.kafkatorio.server.processor.topology.saveMapTiles
import dev.adamko.kafkatorio.server.processor.topology.splitFactorioServerPacketStream
import dev.adamko.kafkatorio.server.socket.SyslogSocketServer
import dev.adamko.kafkatorio.server.web.websocket.WebmapWebsocketServer
import dev.adamko.kafkatorio.server.web.websocket.broadcastToWebsocket
import java.util.Properties
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

View file

@ -1,7 +1,7 @@
package dev.adamko.kafkatorio.server.processor.admin
import dev.adamko.kafkatorio.server.processor.allTopics
import dev.adamko.kafkatorio.server.config.ApplicationProperties
import dev.adamko.kafkatorio.server.processor.allTopics
import java.util.concurrent.TimeUnit
import mu.KotlinLogging
import org.apache.kafka.clients.admin.Admin

View file

@ -1,9 +1,9 @@
package dev.adamko.kafkatorio.server.processor
import dev.adamko.kafkatorio.server.socket.SyslogSocketServer
import dev.adamko.kafkatorio.server.processor.admin.KafkatorioKafkaAdmin
import dev.adamko.kafkatorio.server.config.ApplicationProperties
import dev.adamko.kafkatorio.server.processor.admin.KafkatorioKafkaAdmin
import dev.adamko.kafkatorio.server.socket.SyslogSocketServer
import dev.adamko.kafkatorio.server.web.websocket.WebmapWebsocketServer
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
@ -27,11 +27,11 @@ suspend fun eventsProcessor(
val topology = KafkatorioTopology(appProps, wsServer, syslogServer)
launch {
topology.start()
launch {
topology.start()
println("launched KafkatorioTopology")
println("launched KafkatorioTopology")
awaitCancellation()
}
awaitCancellation()
}
}

View file

@ -5,7 +5,8 @@ import dev.adamko.kafkatorio.schema.packets.ConsoleChatUpdate
import dev.adamko.kafkatorio.schema.packets.ConsoleCommandUpdate
import dev.adamko.kafkatorio.schema.packets.EntityUpdate
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacketData
import dev.adamko.kafkatorio.schema.packets.MapChunkUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkEntityUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate
import dev.adamko.kafkatorio.schema.packets.PlayerUpdate
import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate
import dev.adamko.kafkatorio.schema.packets.SurfaceUpdate
@ -54,9 +55,10 @@ val KafkatorioPacketData.topicName: String
is PrototypesUpdate -> packetTopicNames.getValue(PrototypesUpdate::class)
is SurfaceUpdate -> packetTopicNames.getValue(SurfaceUpdate::class)
is EntityUpdate -> packetTopicNames.getValue(EntityUpdate::class)
is MapChunkUpdate -> packetTopicNames.getValue(MapChunkUpdate::class)
is MapChunkTileUpdate -> packetTopicNames.getValue(MapChunkTileUpdate::class)
is PlayerUpdate -> packetTopicNames.getValue(PlayerUpdate::class)
is KafkatorioPacketData.Error -> packetTopicNames.getValue(KafkatorioPacketData.Error::class)
is MapChunkEntityUpdate -> packetTopicNames.getValue(MapChunkEntityUpdate::class)
}
@ -67,12 +69,14 @@ private val packetTopicNames: Map<KClass<out KafkatorioPacketData>, String> = ma
PrototypesUpdate::class to "prototypes",
SurfaceUpdate::class to "surface",
EntityUpdate::class to "entity",
MapChunkUpdate::class to "map-chunk",
MapChunkTileUpdate::class to "map-chunk-tile",
PlayerUpdate::class to "player",
MapChunkEntityUpdate::class to "map-chunk-entity",
KafkatorioPacketData.Error::class to "error",
).mapValues { (_, v) ->
"$DOMAIN.packet.$v"
}
val KClass<out KafkatorioPacketData>.topicName: String
get() = packetTopicNames.getValue(this)

View file

@ -1,8 +1,5 @@
package dev.adamko.kafkatorio.server.processor.topology
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_032_STATE
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_STATE
import dev.adamko.kafkatorio.server.processor.DebounceProcessor.Companion.addDebounceProcessor
import dev.adamko.kafkatorio.library.kxsBinary
import dev.adamko.kafkatorio.schema.common.ChunkSize
import dev.adamko.kafkatorio.schema.common.ColourHex
@ -10,8 +7,11 @@ import dev.adamko.kafkatorio.schema.common.FactorioServerId
import dev.adamko.kafkatorio.schema.common.ServerMapChunkId
import dev.adamko.kafkatorio.schema.common.leftTopTile
import dev.adamko.kafkatorio.schema.common.toMapChunkPosition
import dev.adamko.kafkatorio.schema.packets.MapChunkUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate
import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate
import dev.adamko.kafkatorio.server.processor.DebounceProcessor.Companion.addDebounceProcessor
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_032_STATE
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_STATE
import dev.adamko.kotka.extensions.consumedAs
import dev.adamko.kotka.extensions.groupedAs
import dev.adamko.kotka.extensions.materializedAs
@ -39,10 +39,10 @@ import org.apache.kafka.streams.kstream.KTable
private const val pid: String = "colourMapChunks"
/** Add colour to each [MapChunkUpdate] */
/** Add colour to each [MapChunkTileUpdate] */
fun colourMapChunks(builder: StreamsBuilder): Topology {
val mapChunksStream: KStream<FactorioServerId, MapChunkUpdate> = builder.streamPacketData()
val mapChunksStream: KStream<FactorioServerId, MapChunkTileUpdate> = builder.streamPacketData()
val protosStream: KStream<FactorioServerId, PrototypesUpdate> = builder.streamPacketData()
val protosTable: KTable<FactorioServerId, TileColourDict> =
@ -78,7 +78,7 @@ fun colourMapChunks(builder: StreamsBuilder): Topology {
ChunkSize.CHUNK_032 to ChunkSize.CHUNK_256,
ChunkSize.CHUNK_256 to ChunkSize.CHUNK_512,
).forEach { (from, to) ->
).forEach { (from, to) ->
chunkTilesColoured
.filter("$pid.combine-chunks.filter-$from-to-$to") { key, _ ->
key.chunkSize == from
@ -98,12 +98,12 @@ fun colourMapChunks(builder: StreamsBuilder): Topology {
}
private fun KStream<FactorioServerId, MapChunkUpdate>.convertToServerMapChunkTiles()
private fun KStream<FactorioServerId, MapChunkTileUpdate>.convertToServerMapChunkTiles()
: KStream<ServerMapChunkId, ServerMapChunkTiles<TileProtoHashCode>> {
val pid = "$pid.convertToMapTiles"
return map("$pid.map") { serverId: FactorioServerId, update: MapChunkUpdate ->
return map("$pid.map") { serverId: FactorioServerId, update: MapChunkTileUpdate ->
val mapTileList = update.tileDictionary?.toMapTileList() ?: emptyList()
@ -112,7 +112,7 @@ private fun KStream<FactorioServerId, MapChunkUpdate>.convertToServerMapChunkTil
}
if (invalidMapTilesList.isNotEmpty()) {
println("WARNING [reduceMapTilesToTable] MapChunkUpdate contained out-of-bounds tiles $invalidMapTilesList")
println("WARNING [reduceMapTilesToTable] MapChunkTileUpdate contained out-of-bounds tiles $invalidMapTilesList")
}
val validMapTilesToProtoHashCode =
@ -232,7 +232,7 @@ fun KTable<ServerMapChunkId, ServerMapChunkTiles<ColourHex>>.streamMapChunkColou
/**
* Group [MapChunkUpdate]s by the Chunk position.
* Group [MapChunkTileUpdate]s by the Chunk position.
*
* They should already be grouped, but do it again to make sure, and to filter out empty updates.
*/

View file

@ -1,12 +1,12 @@
package dev.adamko.kafkatorio.server.processor.topology
import dev.adamko.kafkatorio.server.processor.TOPIC_SRC_SERVER_LOG
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.schema.common.FactorioServerId
import dev.adamko.kafkatorio.schema.common.Tick
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacketData
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.server.processor.TOPIC_SRC_SERVER_LOG
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kotka.extensions.component1
import dev.adamko.kotka.extensions.component2
import dev.adamko.kotka.extensions.consumedAs
@ -35,7 +35,7 @@ fun factorioServerPacketStream(
val packet = runCatching {
jsonMapper.decodeFromString<KafkatorioPacket>(value)
}.getOrElse { e ->
KafkatorioPacket(
KafkatorioPacket(
modVersion = "unknown",
tick = Tick(0u),
data = KafkatorioPacketData.Error(

View file

@ -1,10 +1,10 @@
package dev.adamko.kafkatorio.server.processor.topology
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.schema.common.FactorioServerId
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacketData
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kotka.extensions.consumedAs
import dev.adamko.kotka.extensions.streams.filter
import dev.adamko.kotka.extensions.streams.mapValues

View file

@ -22,8 +22,8 @@ import org.apache.kafka.streams.kstream.KTable
@JvmInline
@Serializable
value class TileProtoHashCode private constructor(val code: Int) {
constructor(tile: MapTile) : this(tile.proto.hashCode())
constructor(tile: FactorioPrototype.MapTile) : this(tile.name.hashCode())
constructor(tile: MapTile) : this(tile.protoId.hashCode())
constructor(tile: FactorioPrototype.MapTile) : this(tile.protoId.hashCode())
}

View file

@ -3,15 +3,15 @@ package dev.adamko.kafkatorio.server.processor.topology
import com.sksamuel.scrimage.ImmutableImage
import com.sksamuel.scrimage.ScaleMethod
import com.sksamuel.scrimage.nio.PngWriter
import dev.adamko.kafkatorio.server.processor.TOPIC_BROADCAST_TO_WEBSOCKET
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_STATE
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.library.kxsBinary
import dev.adamko.kafkatorio.schema.common.ColourHex
import dev.adamko.kafkatorio.schema.common.FactorioServerId
import dev.adamko.kafkatorio.schema.common.ServerMapChunkId
import dev.adamko.kafkatorio.schema.common.TilePngFilename
import dev.adamko.kafkatorio.schema.packets.EventServerPacket
import dev.adamko.kafkatorio.server.config.jsonMapper
import dev.adamko.kafkatorio.server.processor.TOPIC_BROADCAST_TO_WEBSOCKET
import dev.adamko.kafkatorio.server.processor.TOPIC_MAP_CHUNK_COLOURED_STATE
import dev.adamko.kotka.extensions.consumedAs
import dev.adamko.kotka.extensions.producedAs
import dev.adamko.kotka.extensions.streams.map
@ -32,7 +32,7 @@ private const val WEB_MAP_TILE_SIZE_PX = 256
/**
* @param[serverDataDir] [dev.adamko.kafkatorio.processor.config.ApplicationProperties.serverDataDir]
* @param[serverDataDir] [dev.adamko.kafkatorio.server.config.ApplicationProperties.serverDataDir]
*/
fun saveMapTiles(
builder: StreamsBuilder,

View file

@ -1,13 +1,14 @@
package dev.adamko.kafkatorio.server.web.websocket
package dev.adamko.kafkatorio.server.processor.topology
import dev.adamko.kafkatorio.library.jsonMapperKafkatorio
import dev.adamko.kafkatorio.server.processor.TOPIC_BROADCAST_TO_WEBSOCKET
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kafkatorio.schema.common.FactorioServerId
import dev.adamko.kafkatorio.schema.packets.EventServerPacket
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacket
import dev.adamko.kafkatorio.schema.packets.PlayerUpdate
import dev.adamko.kafkatorio.schema.packets.SurfaceUpdate
import dev.adamko.kafkatorio.server.processor.TOPIC_BROADCAST_TO_WEBSOCKET
import dev.adamko.kafkatorio.server.processor.topicName
import dev.adamko.kafkatorio.server.web.websocket.WebmapWebsocketServer
import dev.adamko.kotka.extensions.consumedAs
import dev.adamko.kotka.extensions.producedAs
import dev.adamko.kotka.extensions.stream

View file

@ -59,12 +59,14 @@ class SyslogSocketServer(
launch {
val channel = socket.openReadChannel()
try {
while (socket.socketContext.isActive) {
while (socket.socketContext.isActive && !channel.isClosedForRead) {
val line = runCatching {
channel.readUTF8Line()
}.fold(
onSuccess = { line ->
log("received '$line' from $socket")
if (line != null) {
log("received '$line' from ${socket.description()}")
}
line
},
onFailure = { e ->

View file

@ -10,9 +10,7 @@ import io.ktor.http.content.EntityTagVersion
import io.ktor.http.content.versions
import io.ktor.resources.Resource
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.application
import io.ktor.server.application.call
import io.ktor.server.application.log
import io.ktor.server.http.content.LastModifiedVersion
import io.ktor.server.resources.get
import io.ktor.server.response.respond

View file

@ -41,7 +41,9 @@ val deployModToLocalClient by tasks.registering(Copy::class) {
description = "Copy the mod to the Factorio client"
group = FactorioMod.TASK_GROUP
onlyIf { clientModsDirectory.orNull?.asFile?.exists() == true }
val clientModsDirectory123 = clientModsDirectory
onlyIf { clientModsDirectory123.orNull?.asFile?.exists() == true }
from(factorioMod)
into(clientModsDirectory)
@ -61,9 +63,12 @@ val clientLaunch by tasks.registering(Exec::class) {
description = "Run local Factorio Steam game client"
group = FactorioMod.TASK_GROUP
val steamExe123 = steamExe
val clientModsDirectory444 = clientModsDirectory
onlyIf(!serviceOf<ExecOperations>().isFactorioRunning())
onlyIf { steamExe.orNull?.asFile?.exists() == true }
onlyIf { clientModsDirectory.orNull?.asFile?.exists() == true }
onlyIf { steamExe123.orNull?.asFile?.exists() == true }
onlyIf { clientModsDirectory444.orNull?.asFile?.exists() == true }
dependsOn(deployModToLocalClient)
mustRunAfter(clientKill, ":modules:infra-factorio-server:processRun")

View file

@ -1,4 +1,4 @@
# Do not edit manually. This file was created with task 'dockerEnv'
COMPOSE_PROJECT_NAME=kafkatorio
FACTORIO_VERSION=1.1.59
FACTORIO_VERSION=1.1.61
KAFKATORIO_VERSION=0.6.1

View file

@ -19,7 +19,7 @@ services:
logging:
driver: syslog
options:
syslog-address: "tcp://host.docker.internal:12092"
syslog-address: "tcp://host.docker.internal:12034"
syslog-format: "rfc5424"
# environment:

View file

@ -6,7 +6,7 @@ import dev.adamko.kafkatorio.schema.packets.ConsoleCommandUpdate
import dev.adamko.kafkatorio.schema.packets.EntityUpdate
import dev.adamko.kafkatorio.schema.packets.EventServerPacket
import dev.adamko.kafkatorio.schema.packets.KafkatorioPacketData
import dev.adamko.kafkatorio.schema.packets.MapChunkUpdate
import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate
import dev.adamko.kafkatorio.schema.packets.PlayerUpdate
import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate
import dev.adamko.kafkatorio.schema.packets.SurfaceUpdate
@ -84,7 +84,7 @@ class WebsocketService(
is PrototypesUpdate,
is SurfaceUpdate,
is EntityUpdate,
is MapChunkUpdate,
is MapChunkTileUpdate,
is KafkatorioPacketData.Error -> {
// to be continued...
}