mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 15:13:16 +00:00
Seems to work now. Still have several things to fix tho.
This commit is contained in:
parent
25130c0316
commit
fd84404a39
7 changed files with 60 additions and 96 deletions
3
Justfile
3
Justfile
|
@ -22,6 +22,9 @@ run: add_dylib_debug
|
|||
run2: add_dylib_debug
|
||||
cd noita-proxy && NP_APPID=480 NP_SKIP_MOD_CHECK=1 NP_NOITA_ADDR=127.0.0.1:21252 cargo run -- --launch-cmd "wine noita.exe -gamemode 0"
|
||||
|
||||
run2-alt: add_dylib_debug
|
||||
cd noita-proxy && NP_APPID=480 NP_SKIP_MOD_CHECK=1 NP_NOITA_ADDR=127.0.0.1:21252 cargo run
|
||||
|
||||
run3: add_dylib_debug
|
||||
cd noita-proxy && NP_APPID=480 NP_SKIP_MOD_CHECK=1 NP_NOITA_ADDR=127.0.0.1:21253 cargo run -- --launch-cmd "wine noita.exe -gamemode 0"
|
||||
|
||||
|
|
|
@ -229,28 +229,26 @@ impl NetManager {
|
|||
state.try_ws_write(ws_encode_mod(src, &decompressed));
|
||||
}
|
||||
}
|
||||
NetMsg::WorldDelta { delta: deltas } => {
|
||||
state.world.handle_deltas(deltas);
|
||||
}
|
||||
NetMsg::WorldFrame => {
|
||||
let updates = state.world.get_noita_updates();
|
||||
for update in updates {
|
||||
state.try_ws_write(ws_encode_proxy_bin(0, &update));
|
||||
}
|
||||
}
|
||||
NetMsg::WorldMessage(msg) => state.world.handle_msg(src, msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Are we really reading only one message per loop?
|
||||
if let Some(ws) = &mut state.ws {
|
||||
let msg = ws.read();
|
||||
self.handle_mod_message(msg, &mut state);
|
||||
}
|
||||
|
||||
for msg in state.world.get_emitted_msgs() {
|
||||
self.do_message_request(msg)
|
||||
}
|
||||
state.world.update();
|
||||
let updates = state.world.get_noita_updates();
|
||||
for update in updates {
|
||||
state.try_ws_write(ws_encode_proxy_bin(0, &update));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -435,16 +433,7 @@ impl NetManager {
|
|||
}
|
||||
// world end
|
||||
1 => {
|
||||
let deltas = state.world.add_end();
|
||||
let limit = if self.peer.is_steam() {
|
||||
1024 * 1024
|
||||
} else {
|
||||
30000
|
||||
};
|
||||
for delta in deltas.split(limit) {
|
||||
self.broadcast(&NetMsg::WorldDelta { delta }, Reliability::Reliable);
|
||||
}
|
||||
self.broadcast(&NetMsg::WorldFrame, Reliability::Reliable);
|
||||
state.world.add_end();
|
||||
}
|
||||
key => {
|
||||
error!("Unknown bin msg from mod: {:?}", key)
|
||||
|
|
|
@ -23,8 +23,6 @@ pub enum NetMsg {
|
|||
StartGame { settings: GameSettings },
|
||||
ModRaw { data: Vec<u8> },
|
||||
ModCompressed { data: Vec<u8> },
|
||||
WorldDelta { delta: WorldDelta },
|
||||
WorldFrame,
|
||||
WorldMessage(WorldNetMessage),
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::mem;
|
|||
use bitcode::{Decode, Encode};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{info, warn};
|
||||
use tracing::{debug, info, warn};
|
||||
use world_model::{ChunkCoord, ChunkData, ChunkDelta, WorldModel};
|
||||
|
||||
pub use world_model::encoding::NoitaWorldUpdate;
|
||||
|
@ -87,8 +87,8 @@ impl WorldDelta {
|
|||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum ChunkState {
|
||||
/// Chunk isn't synced yet.
|
||||
Unsynced,
|
||||
/// Chunk isn't synced yet, but will request authority for it.
|
||||
RequestAuthority,
|
||||
/// Transitioning into Listening or Authority state.
|
||||
WaitingForAuthority,
|
||||
/// Listening for chunk updates from this peer.
|
||||
|
@ -96,7 +96,7 @@ enum ChunkState {
|
|||
/// Sending chunk updates to these listeners.
|
||||
Authority { listeners: FxHashSet<OmniPeerId> },
|
||||
/// Chunk is to be cleaned up.
|
||||
Unloaded,
|
||||
UnloadPending,
|
||||
}
|
||||
impl ChunkState {
|
||||
fn authority() -> ChunkState {
|
||||
|
@ -106,14 +106,14 @@ impl ChunkState {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WorldManager {
|
||||
pub(crate) struct WorldManager {
|
||||
is_host: bool,
|
||||
my_peer_id: OmniPeerId,
|
||||
/// We receive changes from other clients here, intending to send them to Noita.
|
||||
inbound_model: WorldModel,
|
||||
/// We use that to create changes to be sent to other clients.
|
||||
outbound_model: WorldModel,
|
||||
/// Current
|
||||
/// Stores chunks that aren't under any authority.
|
||||
chunk_storage: FxHashMap<ChunkCoord, ChunkData>,
|
||||
/// Who is the current chunk authority.
|
||||
authority_map: FxHashMap<ChunkCoord, OmniPeerId>,
|
||||
|
@ -129,7 +129,7 @@ pub struct WorldManager {
|
|||
}
|
||||
|
||||
impl WorldManager {
|
||||
pub fn new(is_host: bool, my_peer_id: OmniPeerId) -> Self {
|
||||
pub(crate) fn new(is_host: bool, my_peer_id: OmniPeerId) -> Self {
|
||||
WorldManager {
|
||||
is_host,
|
||||
my_peer_id,
|
||||
|
@ -149,8 +149,7 @@ impl WorldManager {
|
|||
self.outbound_model.apply_noita_update(&update);
|
||||
}
|
||||
|
||||
pub(crate) fn add_end(&mut self) -> WorldDelta {
|
||||
let deltas = self.outbound_model.get_all_deltas();
|
||||
pub(crate) fn add_end(&mut self) {
|
||||
let updated_chunks = self
|
||||
.outbound_model
|
||||
.updated_chunks()
|
||||
|
@ -162,13 +161,12 @@ impl WorldManager {
|
|||
self.chunk_updated_locally(chunk);
|
||||
}
|
||||
self.outbound_model.reset_change_tracking();
|
||||
WorldDelta(deltas)
|
||||
}
|
||||
|
||||
fn chunk_updated_locally(&mut self, chunk: ChunkCoord) {
|
||||
let entry = self.chunk_state.entry(chunk).or_insert_with(|| {
|
||||
info!("Created entry for {chunk:?}");
|
||||
ChunkState::Unsynced
|
||||
debug!("Created entry for {chunk:?}");
|
||||
ChunkState::RequestAuthority
|
||||
});
|
||||
let mut emit_queue = Vec::new();
|
||||
self.chunk_last_update.insert(chunk, self.current_update);
|
||||
|
@ -193,7 +191,7 @@ impl WorldManager {
|
|||
pub(crate) fn update(&mut self) {
|
||||
let mut emit_queue = Vec::new();
|
||||
|
||||
let unload_limit = 60;
|
||||
let unload_limit = 1;
|
||||
|
||||
for (&chunk, state) in self.chunk_state.iter_mut() {
|
||||
let chunk_last_update = self
|
||||
|
@ -202,7 +200,7 @@ impl WorldManager {
|
|||
.copied()
|
||||
.unwrap_or_default();
|
||||
match state {
|
||||
ChunkState::Unsynced => {
|
||||
ChunkState::RequestAuthority => {
|
||||
emit_queue.push((
|
||||
Destination::Host,
|
||||
WorldNetMessage::RequestAuthority { chunk },
|
||||
|
@ -219,12 +217,12 @@ impl WorldManager {
|
|||
Destination::Peer(*authority),
|
||||
WorldNetMessage::ListenStopRequest { chunk },
|
||||
));
|
||||
*state = ChunkState::Unloaded;
|
||||
*state = ChunkState::UnloadPending;
|
||||
}
|
||||
}
|
||||
ChunkState::Authority { listeners } => {
|
||||
ChunkState::Authority { listeners: _ } => {
|
||||
if self.current_update - chunk_last_update > unload_limit {
|
||||
info!("Unloading [authority] chunk {chunk:?}");
|
||||
info!("Unloading [authority] chunk {chunk:?} (updates: {chunk_last_update} {})", self.current_update);
|
||||
emit_queue.push((
|
||||
Destination::Host,
|
||||
WorldNetMessage::RelinquishAuthority {
|
||||
|
@ -232,10 +230,10 @@ impl WorldManager {
|
|||
chunk_data: self.outbound_model.get_chunk_data(chunk),
|
||||
},
|
||||
));
|
||||
*state = ChunkState::Unloaded;
|
||||
*state = ChunkState::UnloadPending;
|
||||
}
|
||||
}
|
||||
ChunkState::Unloaded => {}
|
||||
ChunkState::UnloadPending => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +241,7 @@ impl WorldManager {
|
|||
self.emit_msg(dst, msg)
|
||||
}
|
||||
self.chunk_state
|
||||
.retain(|_chunk, state| *state != ChunkState::Unloaded);
|
||||
.retain(|_chunk, state| *state != ChunkState::UnloadPending);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_deltas(&mut self, deltas: WorldDelta) {
|
||||
|
@ -259,6 +257,7 @@ impl WorldManager {
|
|||
pub(crate) fn reset(&mut self) {
|
||||
self.inbound_model.reset();
|
||||
self.outbound_model.reset();
|
||||
self.chunk_storage.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn get_emitted_msgs(&mut self) -> Vec<MessageRequest<WorldNetMessage>> {
|
||||
|
@ -335,7 +334,10 @@ impl WorldManager {
|
|||
if let Some(chunk_data) = chunk_data {
|
||||
self.chunk_storage.insert(chunk, chunk_data);
|
||||
}
|
||||
// TODO notify others?
|
||||
self.emit_msg(
|
||||
Destination::Broadcast,
|
||||
WorldNetMessage::ListenAuthorityRelinquished { chunk },
|
||||
)
|
||||
}
|
||||
|
||||
WorldNetMessage::AuthorityAlreadyTaken { chunk, authority } => {
|
||||
|
@ -376,7 +378,7 @@ impl WorldManager {
|
|||
}
|
||||
}
|
||||
WorldNetMessage::ListenUpdate { delta } => {
|
||||
let Some(ChunkState::Listening { authority }) =
|
||||
let Some(ChunkState::Listening { authority: _ }) =
|
||||
self.chunk_state.get_mut(&delta.chunk_coord)
|
||||
else {
|
||||
warn!(
|
||||
|
@ -385,9 +387,10 @@ impl WorldManager {
|
|||
);
|
||||
return;
|
||||
};
|
||||
self.inbound_model.apply_chunk_delta(&delta);
|
||||
}
|
||||
WorldNetMessage::ListenAuthorityRelinquished { chunk } => {
|
||||
self.chunk_state.insert(chunk, ChunkState::Unsynced);
|
||||
self.chunk_state.insert(chunk, ChunkState::UnloadPending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ impl WorldModel {
|
|||
updates
|
||||
}
|
||||
|
||||
fn apply_chunk_delta(&mut self, delta: &ChunkDelta) {
|
||||
pub(crate) fn apply_chunk_delta(&mut self, delta: &ChunkDelta) {
|
||||
self.updated_chunks.insert(delta.chunk_coord);
|
||||
let chunk = self.chunks.entry(delta.chunk_coord).or_default();
|
||||
let mut offset = 0;
|
||||
|
@ -177,7 +177,7 @@ impl WorldModel {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_chunk_delta(
|
||||
pub(crate) fn get_chunk_delta(
|
||||
&self,
|
||||
chunk_coord: ChunkCoord,
|
||||
ignore_changed: bool,
|
||||
|
|
BIN
quant.ew/files/debug/box_128x128.png
Normal file
BIN
quant.ew/files/debug/box_128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 B |
|
@ -19,66 +19,37 @@ local KEY_WORLD_END = 1
|
|||
|
||||
local CHUNK_SIZE = 128
|
||||
|
||||
local function chunk_producer()
|
||||
local initialized_chunks = {}
|
||||
local sent_anything = false
|
||||
|
||||
for _, player_data in pairs(ctx.players) do
|
||||
if not EntityGetIsAlive(player_data.entity) then
|
||||
goto continue
|
||||
end
|
||||
local px, py = EntityGetTransform(player_data.entity)
|
||||
local ocx, ocy = math.floor(px / CHUNK_SIZE), math.floor(py / CHUNK_SIZE)
|
||||
|
||||
for cx = ocx-1,ocx+1 do
|
||||
for cy = ocy-1,ocy+1 do
|
||||
local chunk_id = cx.." "..cy
|
||||
if initialized_chunks[chunk_id] == nil then
|
||||
local crect = rect.Rectangle(cx * CHUNK_SIZE, cy * CHUNK_SIZE, (cx+1) * CHUNK_SIZE, (cy+1) * CHUNK_SIZE)
|
||||
if DoesWorldExistAt(crect.left, crect.top, crect.right, crect.bottom) then
|
||||
-- GamePrint("Sending chunk "..chunk_id)
|
||||
initialized_chunks[chunk_id] = true
|
||||
coroutine.yield(crect)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
local producer_coro = nil
|
||||
local sent_anything = false
|
||||
|
||||
function world_sync.on_world_update_host()
|
||||
function world_sync.on_world_update()
|
||||
|
||||
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
|
||||
|
||||
|
||||
if producer_coro == nil then
|
||||
producer_coro = coroutine.wrap(chunk_producer)
|
||||
end
|
||||
|
||||
if GameGetFrameNum() % 1 == 0 then
|
||||
local crect = producer_coro()
|
||||
|
||||
if crect == nil then
|
||||
producer_coro = nil
|
||||
if GameGetFrameNum() % 10 == 0 then
|
||||
local player_data = ctx.my_player
|
||||
if not EntityGetIsAlive(player_data.entity) then
|
||||
return
|
||||
end
|
||||
local px, py = EntityGetTransform(player_data.entity)
|
||||
local ocx, ocy = math.floor(px / CHUNK_SIZE), math.floor(py / CHUNK_SIZE)
|
||||
|
||||
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))
|
||||
net.proxy_bin_send(KEY_WORLD_FRAME, str)
|
||||
sent_anything = true
|
||||
for cx = ocx-2,ocx+2 do
|
||||
for cy = ocy-2,ocy+2 do
|
||||
local crect = rect.Rectangle(cx * CHUNK_SIZE, cy * CHUNK_SIZE, (cx+1) * CHUNK_SIZE, (cy+1) * CHUNK_SIZE)
|
||||
if DoesWorldExistAt(crect.left, crect.top, crect.right, crect.bottom) then
|
||||
local area = world.encode_area(chunk_map, crect.left, crect.top, crect.right, crect.bottom, encoded_area)
|
||||
if area ~= nil then
|
||||
if ctx.proxy_opt.debug then
|
||||
GameCreateSpriteForXFrames("mods/quant.ew/files/debug/box_128x128.png", crect.left+64, crect.top + 64, true, 0, 0, 11, true)
|
||||
end
|
||||
local str = ffi.string(area, world.encoded_size(area))
|
||||
net.proxy_bin_send(KEY_WORLD_FRAME, str)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if GameGetFrameNum() % 4 == 0 then
|
||||
net.proxy_bin_send(KEY_WORLD_END, "")
|
||||
end
|
||||
net.proxy_bin_send(KEY_WORLD_END, "")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue