Render buildings and resources

- create LuaJsonList to handle empty Lua JSON lists being sent as empty objects
- fix exporting of resources and entities
- split up map tiles into different layer directories
- add 'chunk size' to MapChunkPosition
- move tests to correct packages
- bump tstl
- decrease tstl logging
- remove some defined, but unused, kafka topics
- syslog, rest, websocket servers & web-map: decrease logging
- webmap: add additional layers for buildings and resources
This commit is contained in:
Adam 2022-07-16 23:10:32 +02:00
parent a805d2ad63
commit b507081eac
72 changed files with 2167 additions and 1068 deletions

View file

@ -2,15 +2,17 @@
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-PlayerUpdate=Player 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-MapChunkEntityUpdate=Map entity update rate (seconds)
kafkatorio:cache-expiration-seconds_kafkatorio-packet-keyed-MapChunkResourceUpdate=Map resource 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-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-MapChunkTileUpdate=How frequently map-tile 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 map-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-MapChunkResourceUpdate=How frequently map-resource 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

@ -15,6 +15,7 @@ export type KafkatorioPacketData =
| KafkatorioPacketData.EntityUpdate
| KafkatorioPacketData.Error
| KafkatorioPacketData.MapChunkEntityUpdate
| KafkatorioPacketData.MapChunkResourceUpdate
| KafkatorioPacketData.MapChunkTileUpdate
| KafkatorioPacketData.PlayerUpdate
| KafkatorioPacketData.PrototypesUpdate
@ -29,6 +30,7 @@ export namespace KafkatorioPacketData {
SurfaceUpdate = "kafkatorio.packet.instant.SurfaceUpdate",
EntityUpdate = "kafkatorio.packet.keyed.EntityUpdate",
MapChunkEntityUpdate = "kafkatorio.packet.keyed.MapChunkEntityUpdate",
MapChunkResourceUpdate = "kafkatorio.packet.keyed.MapChunkResourceUpdate",
MapChunkTileUpdate = "kafkatorio.packet.keyed.MapChunkTileUpdate",
PlayerUpdate = "kafkatorio.packet.keyed.PlayerUpdate",
Error = "kafkatorio.packet.KafkatorioPacketData.Error",
@ -74,8 +76,8 @@ export namespace KafkatorioPacketData {
chunkPosition?: MapEntityPosition | null;
graphicsVariation?: UByte | null;
health?: Float | null;
isActive?: boolean | null;
isRotatable?: boolean | null;
isActive?: boolean;
isRotatable?: boolean;
lastUser?: UInt | null;
localisedDescription?: string | null;
localisedName?: string | null;
@ -85,7 +87,14 @@ export namespace KafkatorioPacketData {
type: KafkatorioPacketData.Type.MapChunkEntityUpdate;
key: MapChunkEntityUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
distinctEntities?: { [key: string]: FactorioEntityData } | null;
entitiesXY: FactorioEntityUpdateEntityDictionary;
}
export interface MapChunkResourceUpdate {
type: KafkatorioPacketData.Type.MapChunkResourceUpdate;
key: MapChunkResourceUpdateKey;
events?: { [key: EventName]: Tick[] } | null;
resourcesXY: FactorioEntityUpdateResourceDictionary;
}
export interface MapChunkTileUpdate {
@ -96,7 +105,7 @@ export namespace KafkatorioPacketData {
robot?: EntityIdentifiersData | null;
force?: ForceIndex | null;
tileDictionary?: MapTileDictionary | null;
isDeleted?: boolean | null;
isDeleted?: boolean;
}
export interface PlayerUpdate {
@ -110,10 +119,10 @@ export namespace KafkatorioPacketData {
afkTime?: Tick | null;
ticksToRespawn?: Tick | null;
forceIndex?: ForceIndex | null;
isAdmin?: boolean | null;
isConnected?: boolean | null;
isShowOnMap?: boolean | null;
isSpectator?: boolean | null;
isAdmin?: boolean;
isConnected?: boolean;
isShowOnMap?: boolean;
isSpectator?: boolean;
lastOnline?: Tick | null;
onlineTime?: Tick | null;
position?: MapEntityPosition | null;
@ -162,6 +171,16 @@ export interface MapChunkEntityUpdateKey {
chunkPosition: MapChunkPosition;
}
export type FactorioEntityUpdateEntityDictionary = { [key: string]: { [key: string]: FactorioEntityUpdateEntity } };
export interface MapChunkResourceUpdateKey {
protoId: PrototypeId;
surfaceIndex: SurfaceIndex;
chunkPosition: MapChunkPosition;
}
export type FactorioEntityUpdateResourceDictionary = { [key: string]: { [key: string]: FactorioEntityUpdateResource } };
export interface MapChunkTileUpdateKey {
chunkPosition: MapChunkPosition;
surfaceIndex: SurfaceIndex;
@ -218,10 +237,11 @@ export namespace FactorioPrototype {
mapColourFriend?: Colour | null;
mapColourEnemy?: Colour | null;
maxHealth: Float;
isBuilding: boolean;
isEntityWithOwner: boolean;
isMilitaryTarget: boolean;
isBuilding?: boolean;
isEntityWithOwner?: boolean;
isMilitaryTarget?: boolean;
miningProperties?: EntityMiningProperties | null;
collisionBox?: MapBoundingBox | null;
}
export interface MapTile {
@ -229,7 +249,7 @@ export namespace FactorioPrototype {
protoId: PrototypeId;
mapColour: Colour;
layer: UInt;
collisionMasks: List;
collisionMasks: string[];
order: string;
canBeMined: boolean;
}
@ -244,42 +264,9 @@ export type Byte = int8;
export type MapChunkPosition = [
x: Int,
y: Int,
chunkSize: ChunkSize,
];
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 EntityItemGroup {
name: string;
type: string;
@ -291,56 +278,36 @@ export interface EntityMiningProperties {
products: MinedProduct[] | null;
}
export type List = any & { __List__: void };
export interface MapBoundingBox {
leftTop: MapEntityPosition;
rightBottom: MapEntityPosition;
}
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 ChunkSize {
CHUNK_512 = "CHUNK_512",
CHUNK_256 = "CHUNK_256",
CHUNK_128 = "CHUNK_128",
CHUNK_064 = "CHUNK_064",
CHUNK_032 = "CHUNK_032",
}
export type PrototypeKey = Int & { __PrototypeKey__: void };
export interface FactorioEntityUpdateEntity {
status?: FactorioEntityStatus | null;
unitNumber?: UnitNumber | null;
graphicsVariation?: UByte | null;
health?: Float | null;
isActive?: boolean | null;
isRotatable?: boolean | null;
lastUser?: UInt | null;
}
export interface FactorioEntityUpdateResource {
amount: UInt;
initialAmount?: UInt | null;
}
export type MinedProduct =
| MinedProduct.MinedProductFluid
| MinedProduct.MinedProductItem;
@ -362,4 +329,50 @@ export namespace MinedProduct {
amount?: Double | null;
resultProtoId: PrototypeId;
}
}
export enum FactorioEntityStatus {
WORKING = "WORKING",
NORMAL = "NORMAL",
NO_POWER = "NO_POWER",
LOW_POWER = "LOW_POWER",
NO_FUEL = "NO_FUEL",
DISABLED_BY_CONTROL_BEHAVIOR = "DISABLED_BY_CONTROL_BEHAVIOR",
OPENED_BY_CIRCUIT_NETWORK = "OPENED_BY_CIRCUIT_NETWORK",
CLOSED_BY_CIRCUIT_NETWORK = "CLOSED_BY_CIRCUIT_NETWORK",
DISABLED_BY_SCRIPT = "DISABLED_BY_SCRIPT",
MARKED_FOR_DECONSTRUCTION = "MARKED_FOR_DECONSTRUCTION",
NOT_PLUGGED_IN_ELECTRIC_NETWORK = "NOT_PLUGGED_IN_ELECTRIC_NETWORK",
NETWORKS_CONNECTED = "NETWORKS_CONNECTED",
NETWORKS_DISCONNECTED = "NETWORKS_DISCONNECTED",
CHARGING = "CHARGING",
DISCHARGING = "DISCHARGING",
FULLY_CHARGED = "FULLY_CHARGED",
OUT_OF_LOGISTIC_NETWORK = "OUT_OF_LOGISTIC_NETWORK",
NO_RECIPE = "NO_RECIPE",
NO_INGREDIENTS = "NO_INGREDIENTS",
NO_INPUT_FLUID = "NO_INPUT_FLUID",
NO_RESEARCH_IN_PROGRESS = "NO_RESEARCH_IN_PROGRESS",
NO_MINABLE_RESOURCES = "NO_MINABLE_RESOURCES",
LOW_INPUT_FLUID = "LOW_INPUT_FLUID",
FLUID_INGREDIENT_SHORTAGE = "FLUID_INGREDIENT_SHORTAGE",
FULL_OUTPUT = "FULL_OUTPUT",
ITEM_INGREDIENT_SHORTAGE = "ITEM_INGREDIENT_SHORTAGE",
MISSING_REQUIRED_FLUID = "MISSING_REQUIRED_FLUID",
MISSING_SCIENCE_PACKS = "MISSING_SCIENCE_PACKS",
WAITING_FOR_SOURCE_ITEMS = "WAITING_FOR_SOURCE_ITEMS",
WAITING_FOR_SPACE_IN_DESTINATION = "WAITING_FOR_SPACE_IN_DESTINATION",
PREPARING_ROCKET_FOR_LAUNCH = "PREPARING_ROCKET_FOR_LAUNCH",
WAITING_TO_LAUNCH_ROCKET = "WAITING_TO_LAUNCH_ROCKET",
LAUNCHING_ROCKET = "LAUNCHING_ROCKET",
NO_MODULES_TO_TRANSMIT = "NO_MODULES_TO_TRANSMIT",
RECHARGING_AFTER_POWER_OUTAGE = "RECHARGING_AFTER_POWER_OUTAGE",
WAITING_FOR_TARGET_TO_BE_BUILT = "WAITING_FOR_TARGET_TO_BE_BUILT",
WAITING_FOR_TRAIN = "WAITING_FOR_TRAIN",
NO_AMMO = "NO_AMMO",
LOW_TEMPERATURE = "LOW_TEMPERATURE",
DISABLED = "DISABLED",
TURNED_OFF_DURING_DAYTIME = "TURNED_OFF_DURING_DAYTIME",
NOT_CONNECTED_TO_RAIL = "NOT_CONNECTED_TO_RAIL",
CANT_DIVIDE_SEGMENTS = "CANT_DIVIDE_SEGMENTS",
}

View file

@ -1,6 +1,6 @@
import {initGlobal} from "../global-init";
import {emitPrototypes} from "../events/prototypeUpdates";
import EventDataQueue from "../queue/queue";
import EventDataQueue from "../emitting/EventDataQueue";
commands.add_command(

View file

@ -1,5 +1,12 @@
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
/**
* Simple ordered queue for {@link KafkatorioPacketData}.
*
* Implemented so printing multiple packets won't flood the output log, causing messages to be
* missed or garbled.
*/
export class KafkatorioPacketQueueManager {
private size: int = 0

View file

@ -1,16 +1,20 @@
import {DefinedEventName} from "../types";
import {
ChunkSize,
Colour,
EntityMiningProperties,
EntityStatus,
EventName,
FactorioEntityData,
FactorioEntityStatus,
FactorioEntityUpdateResource,
KafkatorioPacketData,
MapBoundingBox,
MapChunkPosition,
MapEntityPosition,
MinedProduct,
PrototypeId,
} from "../../generated/kafkatorio-schema";
export namespace Converters {
@ -63,13 +67,6 @@ export namespace Converters {
}
export function convertMapTablePosition(
position: MapPositionTable,
): MapChunkPosition {
return [position.x, position.y]
}
export function convertCollisionMaskToNames(cm: CollisionMask): string[] {
let masks: string[] = []
for (const [name] of pairs(cm)) {
@ -81,17 +78,13 @@ export namespace Converters {
export function convertResourceEntity(
entity: LuaEntity
): FactorioEntityData.Resource | null {
): FactorioEntityUpdateResource | 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),
}
}
}
@ -99,7 +92,7 @@ export namespace Converters {
function convertEntityStatus(
status: defines.entity_status | undefined
): EntityStatus | null {
): FactorioEntityStatus | null {
if (status == undefined) {
return null
} else {
@ -119,15 +112,26 @@ export namespace Converters {
export function tilePositionToChunkPosition(mapPosition: TilePositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
return [floor(mapPosition.x / 32), floor(mapPosition.y / 32), ChunkSize.CHUNK_032]
}
export function mapPositionToChunkPosition(mapPosition: MapPositionTable): MapChunkPosition {
return [floor(mapPosition.x / 32), floor(mapPosition.y)]
return [floor(mapPosition.x / 32), floor(mapPosition.y / 32), ChunkSize.CHUNK_032]
}
export function entityStatus(status: defines.entity_status | undefined): EntityStatus | undefined {
export function entityPosition(position: MapPositionTable): MapEntityPosition {
return [position.x, position.y];
}
export function chunkPosition(position: ChunkPositionTable): MapChunkPosition {
return [position.x, position.y, ChunkSize.CHUNK_032]
}
export function entityStatus(status: defines.entity_status | undefined): FactorioEntityStatus | undefined {
if (status == undefined) {
return undefined
} else {
@ -176,51 +180,59 @@ export namespace Converters {
}
const EntityStatusRecord: Record<defines.entity_status, EntityStatus> = {
export function collisionBox(collision_box: BoundingBoxRead): MapBoundingBox {
return {
leftTop: Converters.entityPosition(collision_box.left_top),
rightBottom: Converters.entityPosition(collision_box.right_bottom),
}
}
const EntityStatusRecord: Record<defines.entity_status, FactorioEntityStatus> = {
//@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,
[ defines.entity_status.cant_divide_segments ]: FactorioEntityStatus.CANT_DIVIDE_SEGMENTS,
[ defines.entity_status.charging ]: FactorioEntityStatus.CHARGING,
[ defines.entity_status.closed_by_circuit_network ]: FactorioEntityStatus.CLOSED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.disabled ]: FactorioEntityStatus.DISABLED,
[ defines.entity_status.disabled_by_control_behavior ]: FactorioEntityStatus.DISABLED_BY_CONTROL_BEHAVIOR,
[ defines.entity_status.disabled_by_script ]: FactorioEntityStatus.DISABLED_BY_SCRIPT,
[ defines.entity_status.discharging ]: FactorioEntityStatus.DISCHARGING,
[ defines.entity_status.fluid_ingredient_shortage ]: FactorioEntityStatus.FLUID_INGREDIENT_SHORTAGE,
[ defines.entity_status.fully_charged ]: FactorioEntityStatus.FULLY_CHARGED,
[ defines.entity_status.full_output ]: FactorioEntityStatus.FULL_OUTPUT,
[ defines.entity_status.item_ingredient_shortage ]: FactorioEntityStatus.ITEM_INGREDIENT_SHORTAGE,
[ defines.entity_status.launching_rocket ]: FactorioEntityStatus.LAUNCHING_ROCKET,
[ defines.entity_status.low_input_fluid ]: FactorioEntityStatus.LOW_INPUT_FLUID,
[ defines.entity_status.low_power ]: FactorioEntityStatus.LOW_POWER,
[ defines.entity_status.low_temperature ]: FactorioEntityStatus.LOW_TEMPERATURE,
[ defines.entity_status.marked_for_deconstruction ]: FactorioEntityStatus.MARKED_FOR_DECONSTRUCTION,
[ defines.entity_status.missing_required_fluid ]: FactorioEntityStatus.MISSING_REQUIRED_FLUID,
[ defines.entity_status.missing_science_packs ]: FactorioEntityStatus.MISSING_SCIENCE_PACKS,
[ defines.entity_status.networks_connected ]: FactorioEntityStatus.NETWORKS_CONNECTED,
[ defines.entity_status.networks_disconnected ]: FactorioEntityStatus.NETWORKS_DISCONNECTED,
[ defines.entity_status.normal ]: FactorioEntityStatus.NORMAL,
[ defines.entity_status.not_connected_to_rail ]: FactorioEntityStatus.NOT_CONNECTED_TO_RAIL,
[ defines.entity_status.not_plugged_in_electric_network ]: FactorioEntityStatus.NOT_PLUGGED_IN_ELECTRIC_NETWORK,
[ defines.entity_status.no_ammo ]: FactorioEntityStatus.NO_AMMO,
[ defines.entity_status.no_fuel ]: FactorioEntityStatus.NO_FUEL,
[ defines.entity_status.no_ingredients ]: FactorioEntityStatus.NO_INGREDIENTS,
[ defines.entity_status.no_input_fluid ]: FactorioEntityStatus.NO_INPUT_FLUID,
[ defines.entity_status.no_minable_resources ]: FactorioEntityStatus.NO_MINABLE_RESOURCES,
[ defines.entity_status.no_modules_to_transmit ]: FactorioEntityStatus.NO_MODULES_TO_TRANSMIT,
[ defines.entity_status.no_power ]: FactorioEntityStatus.NO_POWER,
[ defines.entity_status.no_recipe ]: FactorioEntityStatus.NO_RECIPE,
[ defines.entity_status.no_research_in_progress ]: FactorioEntityStatus.NO_RESEARCH_IN_PROGRESS,
[ defines.entity_status.opened_by_circuit_network ]: FactorioEntityStatus.OPENED_BY_CIRCUIT_NETWORK,
[ defines.entity_status.out_of_logistic_network ]: FactorioEntityStatus.OUT_OF_LOGISTIC_NETWORK,
[ defines.entity_status.preparing_rocket_for_launch ]: FactorioEntityStatus.PREPARING_ROCKET_FOR_LAUNCH,
[ defines.entity_status.recharging_after_power_outage ]: FactorioEntityStatus.RECHARGING_AFTER_POWER_OUTAGE,
[ defines.entity_status.turned_off_during_daytime ]: FactorioEntityStatus.TURNED_OFF_DURING_DAYTIME,
[ defines.entity_status.waiting_for_source_items ]: FactorioEntityStatus.WAITING_FOR_SOURCE_ITEMS,
[ defines.entity_status.waiting_for_space_in_destination ]: FactorioEntityStatus.WAITING_FOR_SPACE_IN_DESTINATION,
[ defines.entity_status.waiting_for_target_to_be_built ]: FactorioEntityStatus.WAITING_FOR_TARGET_TO_BE_BUILT,
[ defines.entity_status.waiting_for_train ]: FactorioEntityStatus.WAITING_FOR_TRAIN,
[ defines.entity_status.waiting_to_launch_rocket ]: FactorioEntityStatus.WAITING_TO_LAUNCH_ROCKET,
[ defines.entity_status.working ]: FactorioEntityStatus.WORKING,
//@formatter:on
}
}

View file

@ -1,8 +1,9 @@
import {
EventName,
FactorioEntityData,
FactorioEntityUpdateEntity,
KafkatorioPacketData,
MapChunkPosition, PrototypeId,
MapChunkPosition,
PrototypeId,
Tick
} from "../../../generated/kafkatorio-schema";
import {Converters} from "../converters";
@ -19,29 +20,34 @@ export class EntityUpdatesHandler {
return
}
const tilesByChunk: LuaTable<MapChunkPosition, OldTileAndPosition> = new LuaTable<MapChunkPosition, OldTileAndPosition>()
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)
if (!tilesByChunk.has(chunkPosition)) {
tilesByChunk.set(chunkPosition, [])
}
const tiles = tilesByChunk.get(chunkPosition)
tiles[tiles.length] = tile
tilesByChunk.set(chunkPosition, tiles)
}
for (const [chunkPos, tiles] of pairs(tilesByChunk)) {
const entities: FactorioEntityData.Resource[] = []
const entities: LuaEntity[] = []
for (const tile in tiles) {
const resources: FactorioEntityData.Resource[] = EntityUpdatesHandler.getTileResourceEntities(surface, chunkPos)
for (const resource of resources) {
for (const tile of tiles) {
const tileEntities = EntityUpdatesHandler.getTileResourceEntities(surface, tile.position)
for (const resource of tileEntities) {
entities[entities.length] = resource
}
}
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
EntityUpdatesHandler.throttleResourcesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface_index,
[chunkPos[0], chunkPos[1]],
chunkPos,
entities,
)
}
@ -64,11 +70,11 @@ export class EntityUpdatesHandler {
handleChunkGeneratedEvent(event: OnChunkGeneratedEvent) {
const entities = EntityUpdatesHandler.getAreaResourceEntities(event.surface, event.area)
EntityUpdatesHandler.throttleResourceEntitiesUpdate(
EntityUpdatesHandler.throttleResourcesUpdate(
Converters.eventNameString(event.name),
event.tick as Tick,
event.surface.index,
[event.position.x, event.position.y],
Converters.chunkPosition(event.position),
entities,
)
}
@ -88,22 +94,16 @@ export class EntityUpdatesHandler {
chunkPosition: Converters.mapPositionToChunkPosition(entity.position)
}
const entityUpdate: FactorioEntityData.Standard = {
type: FactorioEntityData.Type.Standard,
protoId: Converters.prototypeIdEntity(entity),
const entityUpdate: FactorioEntityUpdateEntity = {
unitNumber: entity.unit_number,
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,
@ -113,42 +113,44 @@ export class EntityUpdatesHandler {
data.events[eventName] ??= []
data.events[eventName].push(event.tick)
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entityUpdate
data.entitiesXY ??= {}
data.entitiesXY[`${entity.position.x}`] ??= {}
data.entitiesXY[`${entity.position.x}`][`${entity.position.y}`] ??= entityUpdate
}
)
}
private static throttleResourceEntitiesUpdate(
private static throttleResourcesUpdate(
eventName: EventName,
eventTick: Tick,
surfaceIndex: SurfaceIndex,
chunkPosition: MapChunkPosition,
entities: FactorioEntityData.Resource[],
entities: LuaEntity[],
) {
// first group by prototype ID
const entitiesByProtoId: { [protoId: PrototypeId]: FactorioEntityData.Resource[] } = {}
const entitiesByProtoId: { [protoId: PrototypeId]: LuaEntity[] } = {}
for (const entity of entities) {
entitiesByProtoId[entity.protoId] ??= []
entitiesByProtoId[entity.protoId].push(entity)
const protoId = Converters.prototypeIdEntity(entity)
entitiesByProtoId[protoId] ??= []
entitiesByProtoId[protoId].push(entity)
}
for (const [protoId, resourceEntities] of pairs(entitiesByProtoId)) {
// create an update for each prototype
// create an update for each prototype ID
const entityUpdateKey: KafkatorioPacketData.MapChunkEntityUpdate["key"] = {
const entityUpdateKey: KafkatorioPacketData.MapChunkResourceUpdate["key"] = {
protoId: protoId,
surfaceIndex: surfaceIndex,
chunkPosition: [chunkPosition[0], chunkPosition[1]],
chunkPosition: chunkPosition,
}
EventDataCache.throttle<KafkatorioPacketData.MapChunkEntityUpdate>(
EventDataCache.throttle<KafkatorioPacketData.MapChunkResourceUpdate>(
entityUpdateKey,
KafkatorioPacketData.Type.MapChunkEntityUpdate,
KafkatorioPacketData.Type.MapChunkResourceUpdate,
data => {
data.events ??= {}
@ -156,11 +158,13 @@ export class EntityUpdatesHandler {
data.events[eventName].push(eventTick)
for (const entity of resourceEntities) {
const [x, y] = entity.position
const entityKey: string = `${x},${y}`
const resourceUpdate = Converters.convertResourceEntity(entity)
data.distinctEntities ??= {}
data.distinctEntities[entityKey] = entity
if (resourceUpdate != null) {
data.resourcesXY ??= {}
data.resourcesXY[`${entity.position.x}`] ??= {}
data.resourcesXY[`${entity.position.x}`][`${entity.position.y}`] = resourceUpdate
}
}
}
)
@ -173,24 +177,12 @@ export class EntityUpdatesHandler {
private static getTileResourceEntities(
surface: LuaSurface,
mapPosition: MapPosition,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
): LuaEntity[] {
return 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
}
@ -198,24 +190,12 @@ export class EntityUpdatesHandler {
private static getAreaResourceEntities(
surface: LuaSurface,
area: BoundingBoxTable,
): FactorioEntityData.Resource[] {
const rawEntities = surface.find_entities_filtered({
): LuaEntity[] {
return 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
}
}

View file

@ -22,7 +22,7 @@ class MapChunkUpdateHandler {
MapChunkUpdateHandler.mapTilesUpdateDebounce(
event.surface,
Converters.convertMapTablePosition(event.position),
Converters.chunkPosition(event.position),
tiles,
event,
undefined,
@ -136,7 +136,7 @@ class MapChunkUpdateHandler {
for (const position of event.positions) {
const key: PacketKey<KafkatorioPacketData.MapChunkTileUpdate> = {
surfaceIndex: surface.index,
chunkPosition: [position.x, position.y],
chunkPosition: Converters.chunkPosition(position),
}
EventUpdates.debounce<KafkatorioPacketData.MapChunkTileUpdate>(
@ -219,10 +219,7 @@ class MapChunkUpdateHandler {
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)),
]
const chunkPosition = Converters.tilePositionToChunkPosition(tile.position)
if (!mapChunkPositionToTiles.has(chunkPosition)) {
mapChunkPositionToTiles.set(chunkPosition, [])
}

View file

@ -1,4 +1,3 @@
import PacketEmitter from "../emitting/PacketEmitter";
import {
EntityItemGroup,
FactorioPrototype,
@ -16,7 +15,6 @@ export function emitPrototypes() {
const mapTileProtos: PrototypesByType<FactorioPrototype.MapTile> = getMapTilePrototypes()
emit(mapTileProtos)
const entityProtos: PrototypesByType<FactorioPrototype.Entity> = getEntityPrototypes()
emit(entityProtos)
}
@ -58,7 +56,7 @@ function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
const protos: PrototypesByType<FactorioPrototype.Entity> = {}
for (const [, entity] of game.entity_prototypes) {
const key: string = `${entity.group.name}/${entity.subgroup.name}`
const key: string = `${entity.group.name}/${entity.subgroup.name}/${entity.type}`
protos[key] ??= []
@ -73,7 +71,9 @@ function getEntityPrototypes(): PrototypesByType<FactorioPrototype.Entity> {
isEntityWithOwner: entity.is_entity_with_owner,
isMilitaryTarget: entity.is_military_target,
maxHealth: entity.max_health,
collisionBox: Converters.collisionBox(entity.collision_box),
}
entity.infinite_resource
if (entity.map_color != undefined) {
entityProto.mapColour = Converters.mapColour(entity.map_color)

View file

@ -3,10 +3,10 @@ import {Converters} from "./converters";
import {handleSurfaceUpdate} from "./surfaceUpdates";
import EventCacheService from "../emitting/EventDataCache";
import MapChunkUpdate from "./handlers/MapChunkUpdateHandler";
import EventDataQueue from "../queue/queue";
import KafkatorioPacketQueue from "../emitting/KafkatorioPacketQueue";
import PacketEmitter from "../emitting/PacketEmitter";
import {KafkatorioPacketData} from "../../generated/kafkatorio-schema";
import EventDataQueue from "../emitting/EventDataQueue";
script.on_event(defines.events.on_tick, (event: OnTickEvent) => {

View file

@ -1,5 +1,5 @@
import EventUpdates from "./emitting/EventDataCache";
import {EventDataQueueManager} from "./queue/queue";
import {EventDataQueueManager} from "./emitting/EventDataQueue";
export function initGlobal(force: boolean = false) {

View file

@ -8,6 +8,7 @@ export class KafkatorioSettingsConfig {
"kafkatorio.packet.keyed.PlayerUpdate": 0.5,
"kafkatorio.packet.keyed.MapChunkTileUpdate": 30,
"kafkatorio.packet.keyed.MapChunkEntityUpdate": 30,
"kafkatorio.packet.keyed.MapChunkResourceUpdate": 30,
"kafkatorio.packet.keyed.EntityUpdate": 5,
}