World sync

This commit is contained in:
IQuant 2024-05-10 18:47:01 +03:00
parent d36d12ae5e
commit 9836937107
11 changed files with 126 additions and 21 deletions

Binary file not shown.

Binary file not shown.

View file

@ -245,7 +245,7 @@ function world_ffi.get_grid_world()
return grid_world
end
local material_props_size = 0x28c
local celldata_size = 0x290
--- Turn a standard material id into a material pointer.
-- @param id material id that is used in the standard Noita functions
@ -255,7 +255,7 @@ function world_ffi.get_material_ptr(id)
local game_global = ffi.cast("char*", gg_ptr)
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
local ptr = begin + material_props_size * id
local ptr = begin + celldata_size * id
return ptr
end
@ -270,7 +270,7 @@ function world_ffi.get_material_id(ptr)
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
local offset = ffi.cast('char*', ptr) - begin
return offset / material_props_size
return offset / celldata_size
end
return world_ffi

View file

@ -2,4 +2,7 @@
Special thanks to @EvaisaDev for allowing to use code from Noita Arena mod.
Special thanks to:
- @EvaisaDev for allowing to use code from Noita Arena mod.
- @dextered for NoitaPatcher.
- Creators of other libraries used in this project.

View file

@ -1,5 +1,6 @@
local np = require("noitapatcher")
local EZWand = dofile_once("mods/quant.ew/files/lib/EZWand.lua")
local pretty = dofile_once("mods/quant.ew/files/lib/pretty_print.lua")
local inventory_helper = {}
@ -92,7 +93,7 @@ function inventory_helper.get_item_data(player_data, fresh)
-- local item_id = entity.GetVariable(item, "arena_entity_id")
GlobalsSetValue(tostring(item) .. "_item", tostring(k))
-- GlobalsSetValue(tostring(item) .. "_item", tostring(k))
if(entity_is_wand(item))then
local wand = EZWand(item)
table.insert(spellData,
@ -144,7 +145,8 @@ end
function inventory_helper.set_item_data(item_data, player_data)
local player = player_data.entity
if (EntityGetIsAlive(player)) then
if (not EntityGetIsAlive(player)) then
GamePrint("Skip set_item_data, player ".. player_data.name .. " " .. player_data.entity .. " is dead")
return
end
@ -154,13 +156,13 @@ function inventory_helper.set_item_data(item_data, player_data)
EntityKill(item_id)
end
if (item_data ~= nil) then
local active_item_entity = nil
for k, itemInfo in ipairs(item_data) do
local x, y = EntityGetTransform(player)
local item_entity = nil
local item = nil
if(itemInfo.is_wand)then
item = EZWand(itemInfo.data, x, y, GameHasFlagRun("refresh_all_charges"))

View file

@ -59,8 +59,11 @@ function net.init()
print("Unknown msg")
end
if msg_decoded ~= nil and net_handling[msg_decoded.kind] ~= nil and net_handling[msg_decoded.kind][msg_decoded.key] ~= nil then
if ctx.ready or msg_decoded.kind ~= "mod" then
net_handling[msg_decoded.kind][msg_decoded.key](msg_decoded.peer_id, msg_decoded.value)
if ctx.ready or msg_decoded.kind ~= "mod" then
local result, err = pcall(net_handling[msg_decoded.kind][msg_decoded.key], msg_decoded.peer_id, msg_decoded.value)
if not result then
GamePrint(tostring(err))
end
end
-- GamePrint("NetHnd: "..msg_decoded.kind.." "..msg_decoded.key)
end
@ -103,4 +106,8 @@ function net.send_enemy_data(enemy_data)
net.send("enemy", enemy_data)
end
function net.send_world_data(world_data)
net.send("world", world_data)
end
return net

View file

@ -1,6 +1,7 @@
local player_fns = dofile_once("mods/quant.ew/files/src/player_fns.lua")
local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua")
local enemy_sync = dofile_once("mods/quant.ew/files/src/enemy_sync.lua")
local world_sync = dofile_once("mods/quant.ew/files/src/world_sync.lua")
local perk_fns = dofile_once("mods/quant.ew/files/src/perk_fns.lua")
local net_handling = {
@ -42,7 +43,7 @@ function net_handling.mod.inventory(peer_id, inventory_state)
end
local player_data = player_fns.peer_get_player_data(peer_id)
player_fns.deserialize_items(inventory_state, player_data)
-- GamePrint("synced inventory")
GamePrint("synced inventory")
end
function net_handling.mod.perks(peer_id, perk_data)
@ -59,4 +60,10 @@ function net_handling.mod.enemy(peer_id, enemy_data)
end
end
function net_handling.mod.world(peer_id, world_data)
if peer_id == ctx.host_id then
world_sync.handle_world_data(world_data)
end
end
return net_handling

79
files/src/world_sync.lua Normal file
View file

@ -0,0 +1,79 @@
local world_ffi = require("noitapatcher.nsew.world_ffi")
local world = require("noitapatcher.nsew.world")
local rect = require("noitapatcher.nsew.rect")
local ffi = require("ffi")
local rect_optimiser = rect.Optimiser_new()
local encoded_area = world.EncodedArea()
local world_sync = {}
function world_sync.host_upload()
local grid_world = world_ffi.get_grid_world()
local chunk_map = grid_world.vtable.get_chunk_map(grid_world)
local thread_impl = grid_world.mThreadImpl
local begin = thread_impl.updated_grid_worlds.begin
local end_ = begin + thread_impl.chunk_update_count
local count = thread_impl.chunk_update_count
-- GamePrint("w update "..count)
for i = 0, count - 1 do
local it = begin[i]
local start_x = it.update_region.top_left.x
local start_y = it.update_region.top_left.y
local end_x = it.update_region.bottom_right.x
local end_y = it.update_region.bottom_right.y
start_x = start_x - 1
start_y = start_y - 1
end_x = end_x + 1
end_y = end_y + 2
-- if i < 9 then
-- GamePrint(start_x.." "..start_y.." "..end_x.." "..end_y)
-- end
local rectangle = rect.Rectangle(start_x, start_y, end_x, end_y)
rect_optimiser:submit(rectangle)
end
for i = 0, tonumber(thread_impl.world_update_params_count) - 1 do
local wup = thread_impl.world_update_params.begin[i]
local start_x = wup.update_region.top_left.x
local start_y = wup.update_region.top_left.y
local end_x = wup.update_region.bottom_right.x
local end_y = wup.update_region.bottom_right.y
local rectangle = rect.Rectangle(start_x, start_y, end_x, end_y)
rect_optimiser:submit(rectangle)
end
if GameGetFrameNum() % 20 == 0 then
rect_optimiser:scan()
local result = {}
for crect in rect.parts(rect_optimiser:iterate(), 256) do
local area = world.encode_area(chunk_map, crect.left, crect.top, crect.right, crect.bottom, encoded_area)
if area ~= nil then
local str = ffi.string(area, world.encoded_size(area))
result[#result+1] = str
end
end
rect_optimiser:reset()
return result
end
end
local PixelRun_const_ptr = ffi.typeof("struct PixelRun const*")
function world_sync.handle_world_data(world_data)
local grid_world = world_ffi.get_grid_world()
for i, datum in ipairs(world_data) do
-- GamePrint("Decoding world data "..i)
local header = ffi.cast("struct EncodedAreaHeader const*", ffi.cast('char const*', datum))
local runs = ffi.cast(PixelRun_const_ptr, ffi.cast("const char*", datum) + ffi.sizeof(world.EncodedAreaHeader))
world.decode(grid_world, header, runs)
end
end
return world_sync

View file

@ -14,6 +14,7 @@ local ctx = dofile_once("mods/quant.ew/files/src/ctx.lua")
local pretty = dofile_once("mods/quant.ew/files/lib/pretty_print.lua")
local perk_fns = dofile_once("mods/quant.ew/files/src/perk_fns.lua")
local enemy_sync = dofile_once("mods/quant.ew/files/src/enemy_sync.lua")
local world_sync = dofile_once("mods/quant.ew/files/src/world_sync.lua")
function OnProjectileFired(shooter_id, projectile_id, rng, position_x, position_y, target_x, target_y, send_message,
unknown1, multicast_index, unknown3)
@ -90,6 +91,12 @@ function OnPlayerSpawned( player_entity ) -- This runs when player entity has be
end
function OnWorldPreUpdate() -- This is called every time the game is about to start updating the world
local result, err = pcall(on_world_pre_update_inner)
if not result then
GamePrint(tostring(err))
end
end
function on_world_pre_update_inner()
if my_player == nil then return end
-- GamePrint( "Pre-update hook " .. tostring(GameGetFrameNum()) )
@ -109,24 +116,21 @@ function OnWorldPreUpdate() -- This is called every time the game is about to st
end
end
if ctx.is_host then
local world_data = world_sync.host_upload()
if world_data ~= nil then
net.send_world_data(world_data)
end
end
if GameGetFrameNum() % 120 == 0 then
-- GamePrint("Serialize items")
local inventory_state = player_fns.serialize_items(my_player)
net.send_player_inventory(inventory_state)
local perk_data = perk_fns.get_my_perks()
-- print(pretty.table(perk_data))
net.send_player_perks(perk_data)
end
-- for i=1,#events do
-- local event = events[i]
-- GamePrint("event "..i.." "..event.kind)
-- if event.kind == "connect" then
-- player_fns.spawn_player_for(event.peer_id)
-- end
-- end
-- local sinp = player_fns.serialize_inputs(my_player)
-- player_fns.deserialize_inputs(sinp, other_player)
end
function register_localizations(translation_file, clear_count)

View file

@ -11,6 +11,9 @@
- Figure out what to do with one-off perks,
- remove perks, remove other perks
- Do something about cascading projectiles
- Fix replicated projectiles exploding randomly
- Sync enemies with sights
- Позволить менять сид мира
- Никнеймы игроков
k Лимит на длину сообщения