diff --git a/messages.js b/messages.js index 36e8032..09515ce 100644 --- a/messages.js +++ b/messages.js @@ -602,16 +602,18 @@ module.exports = { type: "array", items: { type: "string" }, }, + layer: { type: "string" }, // optional, defaults to "" for entities }, }; - constructor(type, data, position, size) { + constructor(type, data, position, size, layer = "") { this.type = type; this.data = data; this.position = position; this.size = size; + this.layer = layer; } static fromJSON(json) { - return new this(json.type, json.data, json.position, json.size); + return new this(json.type, json.data, json.position, json.size, json.layer); } }, RefreshTileData: class RefreshTileData { diff --git a/module/gridworld.lua b/module/gridworld.lua index da89eb6..881a506 100644 --- a/module/gridworld.lua +++ b/module/gridworld.lua @@ -67,6 +67,7 @@ gridworld.events[clusterio_api.events.on_server_startup] = function() added_entities_to_update = nil, removed_entities_to_update = nil, entity_registrations = {}, + tiles_to_update = nil, } end if global.gridworld.map.entity_registrations == nil then @@ -247,6 +248,31 @@ gridworld.events[defines.events.on_tick] = function(event) worldgen.events.on_tick(event) end end +gridworld.events[defines.events.on_player_built_tile] = function(event) + if not global.gridworld.lobby_server then + map.events.on_player_built_tile(event) + end +end +gridworld.events[defines.events.on_robot_built_tile] = function(event) + if not global.gridworld.lobby_server then + map.events.on_robot_built_tile(event) + end +end +gridworld.events[defines.events.on_player_mined_tile] = function(event) + if not global.gridworld.lobby_server then + map.events.on_player_mined_tile(event) + end +end +gridworld.events[defines.events.on_robot_mined_tile] = function(event) + if not global.gridworld.lobby_server then + map.events.on_robot_mined_tile(event) + end +end +gridworld.events[defines.events.script_raised_set_tiles] = function(event) + if not global.gridworld.lobby_server then + map.events.script_raised_set_tiles(event) + end +end gridworld.on_nth_tick = {} gridworld.on_nth_tick[37] = function() if not global.gridworld.lobby_server then diff --git a/module/map/dump_mapview.lua b/module/map/dump_mapview.lua index 3db2ee2..ca30a20 100644 --- a/module/map/dump_mapview.lua +++ b/module/map/dump_mapview.lua @@ -27,6 +27,7 @@ local function dump_mapview(position_a, position_b) position = position_a, size = CHUNK_SIZE, data = table.concat(map_data, ";"), + layer = "tiles_" }) end -- dump_mapview(game.player.position, {x = game.player.position.x + 128, y = game.player.position.y + 128}) diff --git a/module/map/dump_tiles.lua b/module/map/dump_tiles.lua new file mode 100644 index 0000000..06abc5a --- /dev/null +++ b/module/map/dump_tiles.lua @@ -0,0 +1,28 @@ +--[[ + Dump changed tiles + + Unlike dump_mapview, this one dumps an array of single tiles. This is less + space efficient than the chunked format for normal exports, but nmore + efficient for many small updates such as what happens when robots build concrete. +]] + +local clusterio_api = require("modules/clusterio/api") + +local function dump_tiles(tiles) + local map_data = {} + for _, tilePosition in pairs(tiles) do + local tile = game.surfaces[1].get_tile(tilePosition) + local map_color = tile.prototype.map_color + table.insert(map_data, tilePosition.x) + table.insert(map_data, tilePosition.y) + table.insert(map_data, string.format("%02x%02x%02x%02x", map_color.r, map_color.g, map_color.b, map_color.a)) + end + + clusterio_api.send_json("gridworld:tile_data", { + type = "pixels", + data = table.concat(map_data, ";"), + layer = "tiles_", + }) +end + +return dump_tiles diff --git a/module/map/events/on_nth_tick.lua b/module/map/events/on_nth_tick.lua index f5556e2..29bf126 100644 --- a/module/map/events/on_nth_tick.lua +++ b/module/map/events/on_nth_tick.lua @@ -1,4 +1,5 @@ local dump_entities = require("modules/gridworld/map/dump_entities") +local dump_tiles = require("modules/gridworld/map/dump_tiles") local function on_nth_tick() if global.gridworld.map.added_entities_to_update or (global.gridworld.map.removed_entities_to_update and #global.gridworld.map.removed_entities_to_update > 0) then @@ -6,6 +7,10 @@ local function on_nth_tick() global.gridworld.map.added_entities_to_update = nil global.gridworld.map.removed_entities_to_update = {} end + if global.gridworld.map.tiles_to_update ~= nil then + dump_tiles(global.gridworld.map.tiles_to_update) + global.gridworld.map.tiles_to_update = nil + end end return on_nth_tick diff --git a/module/map/events/on_tile_changed.lua b/module/map/events/on_tile_changed.lua new file mode 100644 index 0000000..3993e31 --- /dev/null +++ b/module/map/events/on_tile_changed.lua @@ -0,0 +1,11 @@ +local function on_tile_changed(event) + local tiles = event.tiles + if global.gridworld.map.tiles_to_update == nil then + global.gridworld.map.tiles_to_update = {} + end + for _, tile in pairs(tiles) do + table.insert(global.gridworld.map.tiles_to_update, tile.position) + end +end + +return on_tile_changed diff --git a/module/map/map.lua b/module/map/map.lua index a52fd6c..ce2fb8d 100644 --- a/module/map/map.lua +++ b/module/map/map.lua @@ -7,6 +7,7 @@ local entity_removed_unregistered = require("modules/gridworld/map/entity_remove local on_nth_tick = require("modules/gridworld/map/events/on_nth_tick") local on_chunk_generated = require("modules/gridworld/map/events/on_chunk_generated") +local on_tile_changed = require("modules/gridworld/map/events/on_tile_changed") return { dump_mapview = dump_mapview, @@ -26,5 +27,11 @@ return { on_robot_mined_entity = entity_removed_unregistered, on_entity_died = entity_removed_unregistered, on_pre_robot_exploded_cliff = entity_removed_unregistered, + -- Tile update events + on_player_built_tile = on_tile_changed, + on_player_mined_tile = on_tile_changed, + on_robot_built_tile = on_tile_changed, + on_robot_mined_tile = on_tile_changed, + script_raised_set_tiles = on_tile_changed, }, } diff --git a/src/mapview/tileDataEventHandler.js b/src/mapview/tileDataEventHandler.js index 1d29c18..89864d7 100644 --- a/src/mapview/tileDataEventHandler.js +++ b/src/mapview/tileDataEventHandler.js @@ -4,13 +4,13 @@ const path = require("path"); sharp.cache(false); const sleep = require("../util/sleep"); +const TILE_SIZE = 512; const fileLocks = {}; // Used to prevent multiple writes to the same file const updates = new Map(); -module.exports = async function tileDataEventHandler({ type, data, size, position }) { +module.exports = async function tileDataEventHandler({ type, data, size, position, layer }) { // console.log(type, size, position); // Image tiles are 512x512 pixels arranged in a grid, starting at 0,0 - const TILE_SIZE = 512; if (type === "pixels") { if (data.length % 3 !== 0) { this.logger.error(`Invalid pixel data length: ${data.length}`); @@ -24,7 +24,7 @@ module.exports = async function tileDataEventHandler({ type, data, size, positio // Figure out which image tile the pixel belongs to const x_tile = (x - x % TILE_SIZE) / TILE_SIZE + (x < 0 ? -1 : 0); const y_tile = (y - y % TILE_SIZE) / TILE_SIZE + (y < 0 ? -1 : 0); - const filename = `z10x${x_tile}y${y_tile}.png`; + const filename = `${layer}z10x${x_tile}y${y_tile}.png`; if (!updates.has(filename)) { updates.set(filename, new Set()); } @@ -51,7 +51,7 @@ module.exports = async function tileDataEventHandler({ type, data, size, positio const pixel_world_y = originPosition[1] + y; const x_tile = (pixel_world_x - pixel_world_x % TILE_SIZE) / TILE_SIZE + (pixel_world_x < 0 ? -1 : 0); const y_tile = (pixel_world_y - pixel_world_y % TILE_SIZE) / TILE_SIZE + (pixel_world_y < 0 ? -1 : 0); - const filename = `tiles_z10x${x_tile}y${y_tile}.png`; + const filename = `${layer}z10x${x_tile}y${y_tile}.png`; if (!updates.has(filename)) { updates.set(filename, new Set()); } diff --git a/src/mapview/tileDataIpcHandler.js b/src/mapview/tileDataIpcHandler.js index 8bb34f4..5a547af 100644 --- a/src/mapview/tileDataIpcHandler.js +++ b/src/mapview/tileDataIpcHandler.js @@ -13,5 +13,5 @@ module.exports = async function handleTileDataIpc(instancePlugin, json) { instancePlugin.logger.error(`Received tile data with invalid type ${type}`); } - await instancePlugin.instance.sendTo("controller", new messages.TileData(type, tileData, json.position, json.size)); + await instancePlugin.instance.sendTo("controller", new messages.TileData(type, tileData, json.position, json.size, json.layer)); };