Update NoitaPatcher to 1.34.0

This commit is contained in:
IQuant 2024-08-25 14:45:47 +03:00
parent 4fbecb8bfe
commit ebb66dfb0a
8 changed files with 264 additions and 102 deletions

View file

@ -1,5 +1,7 @@
--- World read / write functionality.
---@diagnostic disable: cast-local-type
---World read / write functionality.
---@module 'noitapatcher.nsew.world'
---@class World
local world = {}
local ffi = require("ffi")
@ -10,7 +12,7 @@ local C = ffi.C
ffi.cdef([[
enum ENCODE_CONST {
PIXEL_RUN_MAX = 16000,
PIXEL_RUN_MAX = 4096,
LIQUID_FLAG_STATIC = 1,
};
@ -37,40 +39,60 @@ struct __attribute__ ((__packed__)) EncodedArea {
]])
world.last_material_id = 0
---@class PixelRun
---@field flags integer
---@field material integer
---@field length integer
---@class EncodedAreaHeader
---@field x integer
---@field y integer
---@field width integer
---@field height integer
---@field pixel_run_count integer
---@class EncodedArea
---@field header EncodedAreaHeader
---@field pixel_runs PixelRun[] a pointer
world.EncodedAreaHeader = ffi.typeof("struct EncodedAreaHeader")
world.PixelRun = ffi.typeof("struct PixelRun")
---@type fun(): EncodedArea
---@diagnostic disable-next-line: assign-type-mismatch
world.EncodedArea = ffi.typeof("struct EncodedArea")
local pliquid_cell = ffi.typeof("struct CLiquidCell*")
--- Total bytes taken up by the encoded area
-- @tparam EncodedArea encoded_area
-- @treturn int total number of bytes that encodes the area
-- @usage
-- local data = ffi.string(area, world.encoded_size(area))
-- peer:send(data)
---Total bytes taken up by the encoded area
---@param encoded_area EncodedArea
---@return integer total number of bytes that encodes the area
---```lua
---local data = ffi.string(area, world.encoded_size(area))
---peer:send(data)
---```
function world.encoded_size(encoded_area)
return (ffi.sizeof(world.EncodedAreaHeader) + encoded_area.header.pixel_run_count * ffi.sizeof(world.PixelRun))
return ffi.sizeof(world.EncodedAreaHeader) + encoded_area.header.pixel_run_count * ffi.sizeof(world.PixelRun)
end
--- Encode the given rectangle of the world
-- The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not
-- exceed 256 in width or height.
-- @param chunk_map
-- @tparam int start_x coordinate
-- @tparam int start_y coordinate
-- @tparam int end_x coordinate
-- @tparam int end_y coordinate
-- @tparam EncodedArea encoded_area memory to use, if nil this function allocates its own memory
-- @return returns an EncodedArea or nil if the area could not be encoded
-- @see decode
---Encode the given rectangle of the world
---The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not exceed 256 in width or height.
---@param chunk_map unknown
---@param start_x integer coordinate
---@param start_y integer coordinate
---@param end_x integer coordinate
---@param end_y integer coordinate
---@param encoded_area EncodedArea? memory to use, if nil this function allocates its own memory
---@return EncodedArea? encoded_area returns an EncodedArea or nil if the area could not be encoded
---@see decode
function world.encode_area(chunk_map, start_x, start_y, end_x, end_y, encoded_area)
start_x = ffi.cast('int32_t', start_x)
start_y = ffi.cast('int32_t', start_y)
end_x = ffi.cast('int32_t', end_x)
end_y = ffi.cast('int32_t', end_y)
---@cast start_x integer
---@cast start_y integer
---@cast end_x integer
---@cast end_x integer
encoded_area = encoded_area or world.EncodedArea()
@ -163,13 +185,13 @@ function world.encode_area(chunk_map, start_x, start_y, end_x, end_y, encoded_ar
return encoded_area
end
--local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
--- Load an encoded area back into the world.
-- @param grid_world
-- @tparam EncodedAreaHeader header header of the encoded area
-- @param received pointer or ffi array of PixelRun from the encoded area
-- @see encode_area
---Load an encoded area back into the world.
---@param grid_world unknown
---@param header EncodedAreaHeader header of the encoded area
---@param pixel_runs PixelRun[] or ffi array of PixelRun from the encoded area
---@see encode_area
function world.decode(grid_world, header, pixel_runs)
local chunk_map = grid_world.vtable.get_chunk_map(grid_world)
@ -192,19 +214,10 @@ function world.decode(grid_world, header, pixel_runs)
while x < bottom_right_x do
if world_ffi.chunk_loaded(chunk_map, x, y) then
local ppixel = world_ffi.get_cell(chunk_map, x, y)
local current_material = 0
if new_material == -1 then
goto next_pixel
end
if ppixel[0] ~= nil then
local pixel = ppixel[0]
local cell_type = pixel.vtable.get_cell_type(pixel)
if cell_type == C.CELL_TYPE_SOLID then
goto next_pixel
end
current_material = world_ffi.get_material_id(pixel.vtable.get_material(pixel))
if new_material ~= current_material then
@ -213,14 +226,7 @@ function world.decode(grid_world, header, pixel_runs)
end
if current_material ~= new_material and new_material ~= 0 then
if new_material > world.last_material_id then
goto next_pixel
end
local mat_ptr = world_ffi.get_material_ptr(new_material)
if mat_ptr == nil then
goto next_pixel
end
local pixel = world_ffi.construct_cell(grid_world, x, y, mat_ptr, nil)
local pixel = world_ffi.construct_cell(grid_world, x, y, world_ffi.get_material_ptr(new_material), nil)
if pixel == nil then
-- TODO: This can happen when the material texture has a
-- transparent pixel at the given coordinate. There's
@ -228,7 +234,6 @@ function world.decode(grid_world, header, pixel_runs)
-- we skip positions like this.
goto next_pixel
end
local cell_type = pixel.vtable.get_cell_type(pixel)
if cell_type == C.CELL_TYPE_LIQUID then
@ -264,4 +269,4 @@ function world.decode(grid_world, header, pixel_runs)
end
end
return world
return world

View file

@ -37,6 +37,139 @@ struct AABB {
struct Position bottom_right;
};
struct std_string { /* VC++ std::string */
char *buffer;
char sso_buffer[12];
size_t size;
size_t capacity;
};
typedef enum cell_type {
none=0,
liquid=1,
gas=2,
solid=3,
fire=4,
invalid=4294967295
} cell_type;
struct CellData {
struct std_string name;
struct std_string ui_name;
int material_type;
int id_2;
enum cell_type cell_type;
int platform_type;
unsigned int wang_color;
int gfx_glow;
unsigned int gfx_glow_color;
char unknown1[24];
unsigned int default_primary_colour;
char unknown2[36];
bool cell_holes_in_texture;
bool stainable;
bool burnable;
bool on_fire;
int fire_hp;
int autoignition_temperature;
int _100_minus_autoignition_temp;
int temperature_of_fire;
int generates_smoke;
int generates_flames;
bool requires_oxygen;
char padding1[3];
struct std_string on_fire_convert_to_material;
int on_fire_convert_to_material_id;
struct std_string on_fire_flame_material;
int on_fire_flame_material_id;
struct std_string on_fire_smoke_material;
int on_fire_smoke_material_id;
struct ConfigExplosion *explosion_config;
int durability;
int crackability;
bool electrical_conductivity;
bool slippery;
char padding2[2];
float stickyness;
struct std_string cold_freezes_to_material;
struct std_string warmth_melts_to_material;
int warmth_melts_to_material_id;
int cold_freezes_to_material_id;
int16_t cold_freezes_chance_rev;
int16_t warmth_melts_chance_rev;
bool cold_freezes_to_dont_do_reverse_reaction;
char padding3[3];
int lifetime;
int hp;
float density;
bool liquid_sand;
bool liquid_slime;
bool liquid_static;
bool liquid_stains_self;
int liquid_sticks_to_ceiling;
float liquid_gravity;
int liquid_viscosity;
int liquid_stains;
unsigned int liquid_stains_custom_color;
float liquid_sprite_stain_shaken_drop_chance;
float liquid_sprite_stain_ignited_drop_chance;
int8_t liquid_sprite_stains_check_offset;
char padding4[3];
float liquid_sprite_stains_status_threshold;
float liquid_damping;
float liquid_flow_speed;
bool liquid_sand_never_box2d;
char unknown7[3];
int8_t gas_speed;
int8_t gas_upwards_speed;
int8_t gas_horizontal_speed;
int8_t gas_downwards_speed;
float solid_friction;
float solid_restitution;
float solid_gravity_scale;
int solid_static_type;
float solid_on_collision_splash_power;
bool solid_on_collision_explode;
bool solid_on_sleep_convert;
bool solid_on_collision_convert;
bool solid_on_break_explode;
bool solid_go_through_sand;
bool solid_collide_with_self;
char padding5[2];
struct std_string solid_on_collision_material;
int solid_on_collision_material_id;
struct std_string solid_break_to_type;
int solid_break_to_type_id;
struct std_string convert_to_box2d_material;
int convert_to_box2d_material_id;
int vegetation_full_lifetime_growth;
struct std_string vegetation_sprite;
bool vegetation_random_flip_x_scale;
char padding6[3];
char unknown11[12];
float wang_noise_percent;
float wang_curvature;
int wang_noise_type;
char unknown12[12];
bool danger_fire;
bool danger_radioactive;
bool danger_poison;
bool danger_water;
char unknown13[24];
bool always_ignites_damagemodel;
bool ignore_self_reaction_warning;
char padding7[2];
char unknown14[12];
float audio_size_multiplier;
bool audio_is_soft;
char padding8[3];
char unknown15[8];
bool show_in_creative_mode;
bool is_just_particle_fx;
char padding9[2];
// struct grid_CosmeticParticleConfig *ParticleEffect;
};
enum CellType {
CELL_TYPE_NONE = 0,
CELL_TYPE_LIQUID = 1,
@ -58,7 +191,7 @@ struct Cell_vtable {
void* field9_0x24;
void* field10_0x28;
void* field11_0x2c;
void* (__thiscall *get_material)(void *);
struct CellData* (__thiscall *get_material)(void *);
void* field13_0x34;
void* field14_0x38;
void* field15_0x3c;
@ -189,9 +322,28 @@ typedef struct Cell* __thiscall construct_cell_f(struct GridWorld*, int x, int y
]])
--local function check_celldata_field(f, o)
-- local offset = ffi.offsetof("struct CellData", f)
-- assert(offset == o, "Expected field " .. f .. " to be at offset " .. o)
--end
--
--check_celldata_field("wang_color", 0x40)
--check_celldata_field("generates_flames", 0xa4)
--check_celldata_field("durability", 0x104)
--check_celldata_field("cold_freezes_to_material", 0x114)
--check_celldata_field("liquid_sand", 0x160)
--check_celldata_field("liquid_sprite_stain_ignited_drop_chance", 0x17c)
--check_celldata_field("gas_horizontal_speed", 0x196)
--check_celldata_field("solid_on_sleep_convert", 0x1ad)
--check_celldata_field("solid_break_to_type", 0x1d0)
--check_celldata_field("vegetation_sprite", 0x20c)
--check_celldata_field("wang_noise_type", 0x23c)
--check_celldata_field("ignore_self_reaction_warning", 0x269)
--check_celldata_field("is_just_particle_fx", 0x289)
---@class ChunkMap pointer type
---@class GridWorld pointer type
---@class Material pointer type
---@class CellData pointer type
---@class Cell pointer type
---Access a pixel in the world.
@ -205,7 +357,7 @@ world_ffi.get_cell = ffi.cast("get_cell_f*", world_info.get_cell)
world_ffi.remove_cell = ffi.cast("remove_cell_f*", world_info.remove_cell)
---Create a new cell. If memory is null pointer it will allocate its own memory.
---@type fun(grid_world: GridWorld, x: integer, y: integer, material: Material, memory: ffi.cdata*)
---@type fun(grid_world: GridWorld, x: integer, y: integer, material: CellData, memory: ffi.cdata*)
world_ffi.construct_cell = ffi.cast("construct_cell_f*", world_info.construct_cell)
---Check if a chunk is loaded. x and y are world coordinates.
@ -239,10 +391,11 @@ function world_ffi.get_grid_world()
end
local celldata_size = 0x290
local CellData_ptr = ffi.typeof("struct CellData*")
---Turn a standard material id into a material pointer.
---@param id integer material id that is used in the standard Noita functions
---@return Material material to internal material data (aka cell data).
---@return CellData material to internal material data (aka cell data).
---```lua
---local gold_ptr = world_ffi.get_material_ptr(CellFactory_GetType("gold"))
---```
@ -251,11 +404,11 @@ function world_ffi.get_material_ptr(id)
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
local ptr = begin + celldata_size * id
return ptr
return ffi.cast(CellData_ptr, ptr) --[[@as CellData]]
end
---Turn a material pointer into a standard material id.
---@param material Material to a material (aka cell data)
---@param material CellData to a material (aka cell data)
---@return integer material id that is accepted by standard Noita functions such as `CellFactory_GetUIName` and `ConvertMaterialOnAreaInstantly`.
---```lua
---local mat_id = world_ffi.get_material_id(cell.vtable.get_material(cell))

View file

@ -1,7 +1,5 @@
---@diagnostic disable: cast-local-type
---World read / write functionality.
--- World read / write functionality.
---@module 'noitapatcher.nsew.world'
---@class World
local world = {}
local ffi = require("ffi")
@ -12,7 +10,7 @@ local C = ffi.C
ffi.cdef([[
enum ENCODE_CONST {
PIXEL_RUN_MAX = 4096,
PIXEL_RUN_MAX = 16000,
LIQUID_FLAG_STATIC = 1,
};
@ -39,60 +37,40 @@ struct __attribute__ ((__packed__)) EncodedArea {
]])
---@class PixelRun
---@field flags integer
---@field material integer
---@field length integer
---@class EncodedAreaHeader
---@field x integer
---@field y integer
---@field width integer
---@field height integer
---@field pixel_run_count integer
---@class EncodedArea
---@field header EncodedAreaHeader
---@field pixel_runs PixelRun[] a pointer
world.last_material_id = 0
world.EncodedAreaHeader = ffi.typeof("struct EncodedAreaHeader")
world.PixelRun = ffi.typeof("struct PixelRun")
---@type fun(): EncodedArea
---@diagnostic disable-next-line: assign-type-mismatch
world.EncodedArea = ffi.typeof("struct EncodedArea")
local pliquid_cell = ffi.typeof("struct CLiquidCell*")
---Total bytes taken up by the encoded area
---@param encoded_area EncodedArea
---@return integer total number of bytes that encodes the area
---```lua
---local data = ffi.string(area, world.encoded_size(area))
---peer:send(data)
---```
--- Total bytes taken up by the encoded area
-- @tparam EncodedArea encoded_area
-- @treturn int total number of bytes that encodes the area
-- @usage
-- local data = ffi.string(area, world.encoded_size(area))
-- peer:send(data)
function world.encoded_size(encoded_area)
return (ffi.sizeof(world.EncodedAreaHeader) + encoded_area.header.pixel_run_count * ffi.sizeof(world.PixelRun))
end
---Encode the given rectangle of the world
---The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not exceed 256 in width or height.
---@param chunk_map unknown
---@param start_x integer coordinate
---@param start_y integer coordinate
---@param end_x integer coordinate
---@param end_y integer coordinate
---@param encoded_area EncodedArea? memory to use, if nil this function allocates its own memory
---@return EncodedArea? encoded_area returns an EncodedArea or nil if the area could not be encoded
---@see decode
--- Encode the given rectangle of the world
-- The rectangle defined by {`start_x`, `start_y`, `end_x`, `end_y`} must not
-- exceed 256 in width or height.
-- @param chunk_map
-- @tparam int start_x coordinate
-- @tparam int start_y coordinate
-- @tparam int end_x coordinate
-- @tparam int end_y coordinate
-- @tparam EncodedArea encoded_area memory to use, if nil this function allocates its own memory
-- @return returns an EncodedArea or nil if the area could not be encoded
-- @see decode
function world.encode_area(chunk_map, start_x, start_y, end_x, end_y, encoded_area)
start_x = ffi.cast('int32_t', start_x)
start_y = ffi.cast('int32_t', start_y)
end_x = ffi.cast('int32_t', end_x)
end_y = ffi.cast('int32_t', end_y)
---@cast start_x integer
---@cast start_y integer
---@cast end_x integer
---@cast end_x integer
encoded_area = encoded_area or world.EncodedArea()
@ -187,11 +165,11 @@ end
--local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
---Load an encoded area back into the world.
---@param grid_world unknown
---@param header EncodedAreaHeader header of the encoded area
---@param pixel_runs PixelRun[] or ffi array of PixelRun from the encoded area
---@see encode_area
--- Load an encoded area back into the world.
-- @param grid_world
-- @tparam EncodedAreaHeader header header of the encoded area
-- @param received pointer or ffi array of PixelRun from the encoded area
-- @see encode_area
function world.decode(grid_world, header, pixel_runs)
local chunk_map = grid_world.vtable.get_chunk_map(grid_world)
@ -214,10 +192,19 @@ function world.decode(grid_world, header, pixel_runs)
while x < bottom_right_x do
if world_ffi.chunk_loaded(chunk_map, x, y) then
local ppixel = world_ffi.get_cell(chunk_map, x, y)
local current_material = 0
if new_material == -1 then
goto next_pixel
end
if ppixel[0] ~= nil then
local pixel = ppixel[0]
local cell_type = pixel.vtable.get_cell_type(pixel)
if cell_type == C.CELL_TYPE_SOLID then
goto next_pixel
end
current_material = world_ffi.get_material_id(pixel.vtable.get_material(pixel))
if new_material ~= current_material then
@ -226,7 +213,22 @@ function world.decode(grid_world, header, pixel_runs)
end
if current_material ~= new_material and new_material ~= 0 then
local pixel = world_ffi.construct_cell(grid_world, x, y, world_ffi.get_material_ptr(new_material), nil)
if new_material > world.last_material_id then
goto next_pixel
end
local mat_ptr = world_ffi.get_material_ptr(new_material)
if mat_ptr == nil then
goto next_pixel
end
local pixel = world_ffi.construct_cell(grid_world, x, y, mat_ptr, nil)
if pixel == nil then
-- TODO: This can happen when the material texture has a
-- transparent pixel at the given coordinate. There's
-- probably a better way to deal with this, but for now
-- we skip positions like this.
goto next_pixel
end
local cell_type = pixel.vtable.get_cell_type(pixel)
if cell_type == C.CELL_TYPE_LIQUID then
@ -238,6 +240,8 @@ function world.decode(grid_world, header, pixel_runs)
end
end
::next_pixel::
left = left - 1
if left <= 0 then
current_run_ix = current_run_ix + 1

View file

@ -1,5 +1,5 @@
local world_ffi = require("noitapatcher.nsew.world_ffi")
local world = require("noitapatcher.nsew.world")
local world = dofile_once("mods/quant.ew/files/system/world_sync/world.lua")
local rect = require("noitapatcher.nsew.rect")
local ffi = require("ffi")

View file

@ -66,7 +66,7 @@ local function load_modules()
ctx.dofile_and_add_hooks("mods/quant.ew/files/system/weather_sync.lua")
ctx.load_system("polymorph")
ctx.dofile_and_add_hooks("mods/quant.ew/files/system/world_sync_v2.lua")
ctx.load_system("world_sync")
ctx.load_system("spawn_hooks")
ctx.dofile_and_add_hooks("mods/quant.ew/files/system/proxy_info.lua")