mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 07:03:16 +00:00
World sync
This commit is contained in:
parent
d36d12ae5e
commit
9836937107
11 changed files with 126 additions and 21 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -245,7 +245,7 @@ function world_ffi.get_grid_world()
|
||||||
return grid_world
|
return grid_world
|
||||||
end
|
end
|
||||||
|
|
||||||
local material_props_size = 0x28c
|
local celldata_size = 0x290
|
||||||
|
|
||||||
--- Turn a standard material id into a material pointer.
|
--- Turn a standard material id into a material pointer.
|
||||||
-- @param id material id that is used in the standard Noita functions
|
-- @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 game_global = ffi.cast("char*", gg_ptr)
|
||||||
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
||||||
local begin = ffi.cast('char**', cell_factory + 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
|
return ptr
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ function world_ffi.get_material_id(ptr)
|
||||||
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
local cell_factory = ffi.cast('char**', (game_global + 0x18))[0]
|
||||||
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
|
local begin = ffi.cast('char**', cell_factory + 0x18)[0]
|
||||||
local offset = ffi.cast('char*', ptr) - begin
|
local offset = ffi.cast('char*', ptr) - begin
|
||||||
return offset / material_props_size
|
return offset / celldata_size
|
||||||
end
|
end
|
||||||
|
|
||||||
return world_ffi
|
return world_ffi
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
local np = require("noitapatcher")
|
local np = require("noitapatcher")
|
||||||
local EZWand = dofile_once("mods/quant.ew/files/lib/EZWand.lua")
|
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 = {}
|
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")
|
-- 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
|
if(entity_is_wand(item))then
|
||||||
local wand = EZWand(item)
|
local wand = EZWand(item)
|
||||||
table.insert(spellData,
|
table.insert(spellData,
|
||||||
|
@ -144,7 +145,8 @@ end
|
||||||
|
|
||||||
function inventory_helper.set_item_data(item_data, player_data)
|
function inventory_helper.set_item_data(item_data, player_data)
|
||||||
local player = player_data.entity
|
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
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -154,13 +156,13 @@ function inventory_helper.set_item_data(item_data, player_data)
|
||||||
EntityKill(item_id)
|
EntityKill(item_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if (item_data ~= nil) then
|
if (item_data ~= nil) then
|
||||||
local active_item_entity = nil
|
local active_item_entity = nil
|
||||||
|
|
||||||
for k, itemInfo in ipairs(item_data) do
|
for k, itemInfo in ipairs(item_data) do
|
||||||
local x, y = EntityGetTransform(player)
|
local x, y = EntityGetTransform(player)
|
||||||
local item_entity = nil
|
local item_entity = nil
|
||||||
|
|
||||||
local item = nil
|
local item = nil
|
||||||
if(itemInfo.is_wand)then
|
if(itemInfo.is_wand)then
|
||||||
item = EZWand(itemInfo.data, x, y, GameHasFlagRun("refresh_all_charges"))
|
item = EZWand(itemInfo.data, x, y, GameHasFlagRun("refresh_all_charges"))
|
||||||
|
|
|
@ -59,8 +59,11 @@ function net.init()
|
||||||
print("Unknown msg")
|
print("Unknown msg")
|
||||||
end
|
end
|
||||||
if msg_decoded ~= nil and net_handling[msg_decoded.kind] ~= nil and net_handling[msg_decoded.kind][msg_decoded.key] ~= nil then
|
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
|
if ctx.ready or msg_decoded.kind ~= "mod" then
|
||||||
net_handling[msg_decoded.kind][msg_decoded.key](msg_decoded.peer_id, msg_decoded.value)
|
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
|
end
|
||||||
-- GamePrint("NetHnd: "..msg_decoded.kind.." "..msg_decoded.key)
|
-- GamePrint("NetHnd: "..msg_decoded.kind.." "..msg_decoded.key)
|
||||||
end
|
end
|
||||||
|
@ -103,4 +106,8 @@ function net.send_enemy_data(enemy_data)
|
||||||
net.send("enemy", enemy_data)
|
net.send("enemy", enemy_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function net.send_world_data(world_data)
|
||||||
|
net.send("world", world_data)
|
||||||
|
end
|
||||||
|
|
||||||
return net
|
return net
|
|
@ -1,6 +1,7 @@
|
||||||
local player_fns = dofile_once("mods/quant.ew/files/src/player_fns.lua")
|
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 ctx = dofile_once("mods/quant.ew/files/src/ctx.lua")
|
||||||
local enemy_sync = dofile_once("mods/quant.ew/files/src/enemy_sync.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 perk_fns = dofile_once("mods/quant.ew/files/src/perk_fns.lua")
|
||||||
|
|
||||||
local net_handling = {
|
local net_handling = {
|
||||||
|
@ -42,7 +43,7 @@ function net_handling.mod.inventory(peer_id, inventory_state)
|
||||||
end
|
end
|
||||||
local player_data = player_fns.peer_get_player_data(peer_id)
|
local player_data = player_fns.peer_get_player_data(peer_id)
|
||||||
player_fns.deserialize_items(inventory_state, player_data)
|
player_fns.deserialize_items(inventory_state, player_data)
|
||||||
-- GamePrint("synced inventory")
|
GamePrint("synced inventory")
|
||||||
end
|
end
|
||||||
|
|
||||||
function net_handling.mod.perks(peer_id, perk_data)
|
function net_handling.mod.perks(peer_id, perk_data)
|
||||||
|
@ -59,4 +60,10 @@ function net_handling.mod.enemy(peer_id, enemy_data)
|
||||||
end
|
end
|
||||||
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
|
return net_handling
|
79
files/src/world_sync.lua
Normal file
79
files/src/world_sync.lua
Normal 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
|
26
init.lua
26
init.lua
|
@ -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 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 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 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,
|
function OnProjectileFired(shooter_id, projectile_id, rng, position_x, position_y, target_x, target_y, send_message,
|
||||||
unknown1, multicast_index, unknown3)
|
unknown1, multicast_index, unknown3)
|
||||||
|
@ -90,6 +91,12 @@ function OnPlayerSpawned( player_entity ) -- This runs when player entity has be
|
||||||
end
|
end
|
||||||
|
|
||||||
function OnWorldPreUpdate() -- This is called every time the game is about to start updating the world
|
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
|
if my_player == nil then return end
|
||||||
-- GamePrint( "Pre-update hook " .. tostring(GameGetFrameNum()) )
|
-- 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
|
||||||
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
|
if GameGetFrameNum() % 120 == 0 then
|
||||||
|
-- GamePrint("Serialize items")
|
||||||
local inventory_state = player_fns.serialize_items(my_player)
|
local inventory_state = player_fns.serialize_items(my_player)
|
||||||
net.send_player_inventory(inventory_state)
|
net.send_player_inventory(inventory_state)
|
||||||
local perk_data = perk_fns.get_my_perks()
|
local perk_data = perk_fns.get_my_perks()
|
||||||
-- print(pretty.table(perk_data))
|
-- print(pretty.table(perk_data))
|
||||||
net.send_player_perks(perk_data)
|
net.send_player_perks(perk_data)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
function register_localizations(translation_file, clear_count)
|
function register_localizations(translation_file, clear_count)
|
||||||
|
|
3
todo.txt
3
todo.txt
|
@ -11,6 +11,9 @@
|
||||||
- Figure out what to do with one-off perks,
|
- Figure out what to do with one-off perks,
|
||||||
- remove perks, remove other perks
|
- remove perks, remove other perks
|
||||||
|
|
||||||
|
- Do something about cascading projectiles
|
||||||
|
- Fix replicated projectiles exploding randomly
|
||||||
|
- Sync enemies with sights
|
||||||
- Позволить менять сид мира
|
- Позволить менять сид мира
|
||||||
- Никнеймы игроков
|
- Никнеймы игроков
|
||||||
k Лимит на длину сообщения
|
k Лимит на длину сообщения
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue