mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 07:03:16 +00:00
refactor a bunch of things to get ready for rust world sync
This commit is contained in:
parent
4ff165b815
commit
067570672d
32 changed files with 540 additions and 3126 deletions
16
noita-proxy/Cargo.lock
generated
16
noita-proxy/Cargo.lock
generated
|
@ -890,9 +890,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
@ -2625,9 +2625,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
|||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.5"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||
checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -4041,9 +4041,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.51"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a457e416a0f90d246a4c3288bd7a25b2304ca727f253f95be383dd17af56be8f"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
@ -6410,9 +6410,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.22"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a179ac8923651ff1d15efbee760b4dd3679fd85fa5a8b2bb1109b7248f80e30f"
|
||||
checksum = "edcacf11b6f48dd21b9ba002f991bdd5de29b2da8cc2800412f4b80f677e4957"
|
||||
dependencies = [
|
||||
"toml",
|
||||
"version_check",
|
||||
|
|
|
@ -55,13 +55,13 @@ use util::{args::Args, steam_helper::LobbyExtraData};
|
|||
mod bookkeeping;
|
||||
use crate::net::messages::NetMsg;
|
||||
use crate::net::omni::OmniPeerId;
|
||||
use crate::net::world::world_model::ChunkCoord;
|
||||
use crate::player_cosmetics::{
|
||||
display_player_skin, get_player_skin, player_path, player_select_current_color_slot,
|
||||
player_skin_display_color_picker, shift_hue,
|
||||
};
|
||||
pub use bookkeeping::{mod_manager, releases, self_update};
|
||||
use shared::WorldPos;
|
||||
use shared::world_sync::ChunkCoord;
|
||||
mod lobby_code;
|
||||
pub mod net;
|
||||
mod player_cosmetics;
|
||||
|
@ -126,7 +126,7 @@ pub enum LocalHealthMode {
|
|||
#[serde(default)]
|
||||
pub struct GameSettings {
|
||||
seed: u64,
|
||||
world_num: u16,
|
||||
world_num: u8,
|
||||
debug_mode: Option<bool>,
|
||||
use_constant_seed: bool,
|
||||
duplicate: Option<bool>,
|
||||
|
@ -1660,7 +1660,7 @@ impl App {
|
|||
|
||||
fn connect_screen(&mut self, ctx: &Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
if self.app_saved_state.times_started % 20 == 0 {
|
||||
if self.app_saved_state.times_started.is_multiple_of(20) {
|
||||
let image = egui::Image::new(egui::include_image!("../assets/longleg.png"))
|
||||
.texture_options(TextureOptions::NEAREST);
|
||||
image.paint_at(ui, ctx.screen_rect());
|
||||
|
|
|
@ -25,12 +25,12 @@ use std::{
|
|||
thread::{self, JoinHandle},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use world::{NoitaWorldUpdate, WorldManager};
|
||||
use world::WorldManager;
|
||||
|
||||
use crate::lobby_code::LobbyKind;
|
||||
use crate::mod_manager::{ModmanagerSettings, get_mods};
|
||||
use crate::net::world::world_model::ChunkData;
|
||||
use crate::net::world::world_model::chunk::{Pixel, PixelFlags};
|
||||
use crate::net::world::world_model::{ChunkCoord, ChunkData};
|
||||
use crate::player_cosmetics::{PlayerPngDesc, create_player_png, get_player_skin};
|
||||
use crate::steam_helper::LobbyExtraData;
|
||||
use crate::{
|
||||
|
@ -38,6 +38,7 @@ use crate::{
|
|||
bookkeeping::save_state::{SaveState, SaveStateEntry},
|
||||
};
|
||||
use shared::des::ProxyToDes;
|
||||
use shared::world_sync::{ChunkCoord, ProxyToWorldSync};
|
||||
use tangled::Reliability;
|
||||
use tracing::{error, info, warn};
|
||||
mod audio;
|
||||
|
@ -535,12 +536,10 @@ impl NetManager {
|
|||
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_ms_write(&ws_encode_proxy_bin(0, &update));
|
||||
}
|
||||
let updates = state.world.update();
|
||||
state.try_ms_write(&NoitaInbound::ProxyToWorldSync(ProxyToWorldSync::Updates(
|
||||
updates,
|
||||
)));
|
||||
|
||||
if state.had_a_disconnect {
|
||||
self.broadcast(&NetMsg::NoitaDisconnected, Reliability::Reliable);
|
||||
|
@ -718,6 +717,10 @@ impl NetManager {
|
|||
sendm: &Sender<FxHashMap<u16, u32>>,
|
||||
) {
|
||||
match net_msg {
|
||||
NetMsg::ForwardWorldSyncToProxy(msg) => state.world.handle_noita_msg(src, msg),
|
||||
NetMsg::ForwardProxyToWorldSync(msg) => {
|
||||
state.try_ms_write(&NoitaInbound::ProxyToWorldSync(msg));
|
||||
}
|
||||
NetMsg::AudioData(data, global, tx, ty, vol) => {
|
||||
if !self.is_cess.load(Ordering::Relaxed) {
|
||||
let audio = self.audio.lock().unwrap().clone();
|
||||
|
@ -1144,8 +1147,6 @@ impl NetManager {
|
|||
},
|
||||
);
|
||||
}
|
||||
// Binary message to proxy
|
||||
3 => self.handle_bin_message_to_proxy(&raw_msg[1..], state),
|
||||
0 => {
|
||||
let flags = String::from_utf8_lossy(&raw_msg[1..]).into();
|
||||
let msg = NetMsg::Flags(flags);
|
||||
|
@ -1167,6 +1168,19 @@ impl NetManager {
|
|||
);
|
||||
}
|
||||
}
|
||||
NoitaOutbound::WorldSyncToProxy(world_sync_msg) => {
|
||||
if self.is_host() {
|
||||
state
|
||||
.world
|
||||
.handle_noita_msg(self.peer.my_id(), world_sync_msg)
|
||||
} else {
|
||||
self.send(
|
||||
self.peer.host_id(),
|
||||
&NetMsg::ForwardWorldSyncToProxy(world_sync_msg),
|
||||
Reliability::Reliable,
|
||||
);
|
||||
}
|
||||
}
|
||||
NoitaOutbound::RemoteMessage {
|
||||
reliable,
|
||||
destination,
|
||||
|
@ -1432,29 +1446,6 @@ impl NetManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_bin_message_to_proxy(&self, msg: &[u8], state: &mut NetInnerState) {
|
||||
let key = msg[0];
|
||||
let data = &msg[1..];
|
||||
match key {
|
||||
// world frame
|
||||
0 => {
|
||||
let update = NoitaWorldUpdate::load(data);
|
||||
state.world.add_update(update);
|
||||
}
|
||||
// world end
|
||||
1 => {
|
||||
let pos = data[1..]
|
||||
.split(|b| *b == b':')
|
||||
.map(|s| String::from_utf8_lossy(s).parse::<i32>().unwrap_or(0))
|
||||
.collect::<Vec<i32>>();
|
||||
state.world.add_end(data[0], &pos);
|
||||
}
|
||||
key => {
|
||||
error!("Unknown bin msg from mod: {:?}", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn end_run(&self, state: &mut NetInnerState) {
|
||||
self.init_settings.save_state.reset();
|
||||
{
|
||||
|
@ -1468,7 +1459,7 @@ impl NetManager {
|
|||
.modmanager_settings
|
||||
.get_progress()
|
||||
.unwrap_or_default();
|
||||
if settings.world_num == u16::MAX {
|
||||
if settings.world_num == u8::MAX {
|
||||
settings.world_num = 0
|
||||
} else {
|
||||
settings.world_num += 1
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use super::{omni::OmniPeerId, world::WorldNetMessage};
|
||||
use crate::net::world::world_model::{ChunkCoord, ChunkData};
|
||||
use crate::net::world::world_model::ChunkData;
|
||||
use crate::{GameSettings, player_cosmetics::PlayerPngDesc};
|
||||
use bitcode::{Decode, Encode};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use shared::world_sync::ChunkCoord;
|
||||
pub(crate) type Destination = shared::Destination<OmniPeerId>;
|
||||
|
||||
pub(crate) struct MessageRequest<T> {
|
||||
|
@ -27,7 +27,9 @@ pub(crate) enum NetMsg {
|
|||
PlayerColor(PlayerPngDesc, bool, Option<OmniPeerId>, String),
|
||||
RemoteMsg(shared::RemoteMessage),
|
||||
ForwardDesToProxy(shared::des::DesToProxy),
|
||||
ForwardWorldSyncToProxy(shared::world_sync::WorldSyncToProxy),
|
||||
ForwardProxyToDes(shared::des::ProxyToDes),
|
||||
ForwardProxyToWorldSync(shared::world_sync::ProxyToWorldSync),
|
||||
NoitaDisconnected,
|
||||
Flags(String),
|
||||
RespondFlagNormal(String, bool),
|
||||
|
|
|
@ -4,22 +4,19 @@ use rand::{Rng, rng};
|
|||
use rayon::iter::IntoParallelIterator;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::f32::consts::TAU;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::time::Duration;
|
||||
use std::{cmp, env, mem, thread};
|
||||
use std::{cmp, mem, thread};
|
||||
use tracing::{debug, info, warn};
|
||||
use wide::f32x8;
|
||||
use world_model::{
|
||||
CHUNK_SIZE, ChunkCoord, ChunkData, ChunkDelta, WorldModel,
|
||||
ChunkData, ChunkDelta, WorldModel,
|
||||
chunk::{Chunk, Pixel},
|
||||
};
|
||||
|
||||
pub use world_model::encoding::NoitaWorldUpdate;
|
||||
|
||||
use crate::bookkeeping::save_state::{SaveState, SaveStateEntry};
|
||||
|
||||
use super::{
|
||||
|
@ -30,12 +27,6 @@ use super::{
|
|||
|
||||
pub mod world_model;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum WorldUpdateKind {
|
||||
Update(NoitaWorldUpdate),
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, Decode, Encode, Clone)]
|
||||
pub(crate) enum WorldNetMessage {
|
||||
// Authority request
|
||||
|
@ -75,13 +66,13 @@ pub(crate) enum WorldNetMessage {
|
|||
RelinquishAuthority {
|
||||
chunk: ChunkCoord,
|
||||
chunk_data: Option<ChunkData>,
|
||||
world_num: i32,
|
||||
world_num: u8,
|
||||
},
|
||||
// Ttell how to update a chunk storage
|
||||
// Tell how to update a chunk storage
|
||||
UpdateStorage {
|
||||
chunk: ChunkCoord,
|
||||
chunk_data: Option<ChunkData>,
|
||||
world_num: i32,
|
||||
world_num: u8,
|
||||
priority: Option<u8>,
|
||||
},
|
||||
// When listening
|
||||
|
@ -200,7 +191,7 @@ pub(crate) struct WorldManager {
|
|||
chunk_last_update: FxHashMap<ChunkCoord, u64>,
|
||||
/// Stores last priority we used for that chunk, in case transfer fails and we'll need to request authority normally.
|
||||
last_request_priority: FxHashMap<ChunkCoord, u8>,
|
||||
world_num: i32,
|
||||
world_num: u8,
|
||||
pub materials: FxHashMap<u16, (u32, u32, CellType, u32)>,
|
||||
is_storage_recent: FxHashSet<ChunkCoord>,
|
||||
explosion_pointer: FxHashMap<ChunkCoord, Vec<usize>>,
|
||||
|
@ -330,64 +321,23 @@ impl WorldManager {
|
|||
self.chunk_storage.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn add_update(&mut self, update: NoitaWorldUpdate) {
|
||||
self.outbound_model
|
||||
.apply_noita_update(&update, &mut self.is_storage_recent);
|
||||
}
|
||||
|
||||
pub(crate) fn add_end(&mut self, priority: u8, pos: &[i32]) {
|
||||
let updated_chunks = self
|
||||
.outbound_model
|
||||
.updated_chunks()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
self.current_update += 1;
|
||||
let chunks_to_send: Vec<Vec<(OmniPeerId, u8)>> = updated_chunks
|
||||
.iter()
|
||||
.map(|chunk| self.chunk_updated_locally(*chunk, priority, pos))
|
||||
.collect();
|
||||
let mut chunk_packet: HashMap<OmniPeerId, Vec<(ChunkDelta, u8)>> = HashMap::new();
|
||||
for (chunk, who_sending) in updated_chunks.iter().zip(chunks_to_send.iter()) {
|
||||
let Some(delta) = self.outbound_model.get_chunk_delta(*chunk, false) else {
|
||||
continue;
|
||||
};
|
||||
for (peer, pri) in who_sending {
|
||||
chunk_packet
|
||||
.entry(*peer)
|
||||
.or_default()
|
||||
.push((delta.clone(), *pri));
|
||||
}
|
||||
}
|
||||
let mut emit_queue = Vec::new();
|
||||
for (peer, chunkpacket) in chunk_packet {
|
||||
emit_queue.push((
|
||||
Destination::Peer(peer),
|
||||
WorldNetMessage::ChunkPacket { chunkpacket },
|
||||
));
|
||||
}
|
||||
for (dst, msg) in emit_queue {
|
||||
self.emit_msg(dst, msg)
|
||||
}
|
||||
self.outbound_model.reset_change_tracking();
|
||||
}
|
||||
|
||||
fn chunk_updated_locally(
|
||||
&mut self,
|
||||
chunk: ChunkCoord,
|
||||
priority: u8,
|
||||
pos: &[i32],
|
||||
pos: Option<(i32, i32, i32, i32, bool)>,
|
||||
world_num: u8,
|
||||
) -> Vec<(OmniPeerId, u8)> {
|
||||
if pos.len() == 6 {
|
||||
self.my_pos = (pos[0], pos[1]);
|
||||
self.cam_pos = (pos[2], pos[3]);
|
||||
self.is_notplayer = pos[4] == 1;
|
||||
if self.world_num != pos[5] {
|
||||
self.world_num = pos[5];
|
||||
if let Some((px, py, cx, cy, is_not)) = pos {
|
||||
self.my_pos = (px, py);
|
||||
self.cam_pos = (cx, cy);
|
||||
self.is_notplayer = is_not;
|
||||
if self.world_num != world_num {
|
||||
self.world_num = world_num;
|
||||
self.reset();
|
||||
}
|
||||
} else if self.world_num != pos[0] {
|
||||
self.world_num = pos[0];
|
||||
} else if self.world_num != world_num {
|
||||
self.world_num = world_num;
|
||||
self.reset();
|
||||
}
|
||||
let entry = self.chunk_state.entry(chunk).or_insert_with(|| {
|
||||
|
@ -508,7 +458,7 @@ impl WorldManager {
|
|||
chunks_to_send
|
||||
}
|
||||
|
||||
pub(crate) fn update(&mut self) {
|
||||
pub(crate) fn update(&mut self) -> Vec<NoitaWorldUpdate> {
|
||||
fn should_kill(
|
||||
my_pos: (i32, i32),
|
||||
cam_pos: (i32, i32),
|
||||
|
@ -638,18 +588,11 @@ impl WorldManager {
|
|||
}
|
||||
retain
|
||||
});
|
||||
self.get_noita_updates()
|
||||
}
|
||||
|
||||
pub(crate) fn get_noita_updates(&mut self) -> Vec<Vec<u8>> {
|
||||
// Sends random data to noita to check if it crashes.
|
||||
if env::var_os("NP_WORLD_SYNC_TEST").is_some() && self.current_update % 10 == 0 {
|
||||
let chunk_data = ChunkData::make_random();
|
||||
self.inbound_model
|
||||
.apply_chunk_data(ChunkCoord(0, 0), &chunk_data)
|
||||
}
|
||||
let updates = self.inbound_model.get_all_noita_updates();
|
||||
self.inbound_model.reset_change_tracking();
|
||||
updates
|
||||
pub(crate) fn get_noita_updates(&mut self) -> Vec<NoitaWorldUpdate> {
|
||||
self.inbound_model.get_all_noita_updates()
|
||||
}
|
||||
|
||||
pub(crate) fn reset(&mut self) {
|
||||
|
@ -3137,6 +3080,7 @@ use crate::net::world::world_model::chunk::PixelFlags;
|
|||
use rand::seq::SliceRandom;
|
||||
#[cfg(test)]
|
||||
use serial_test::serial;
|
||||
use shared::world_sync::{CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, WorldSyncToProxy};
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
#[serial]
|
||||
|
@ -3406,3 +3350,52 @@ fn test_cut_perf() {
|
|||
}
|
||||
println!("total micros: {}", total / iters);
|
||||
}
|
||||
|
||||
impl WorldManager {
|
||||
pub fn handle_noita_msg(&mut self, _: OmniPeerId, msg: WorldSyncToProxy) {
|
||||
match msg {
|
||||
WorldSyncToProxy::Updates(updates) => {
|
||||
for update in updates {
|
||||
self.outbound_model
|
||||
.apply_noita_update(update, &mut self.is_storage_recent)
|
||||
}
|
||||
}
|
||||
WorldSyncToProxy::End(pos, priority, world_num) => {
|
||||
let updated_chunks = self
|
||||
.outbound_model
|
||||
.updated_chunks()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
self.current_update += 1;
|
||||
let chunks_to_send: Vec<Vec<(OmniPeerId, u8)>> = updated_chunks
|
||||
.iter()
|
||||
.map(|chunk| self.chunk_updated_locally(*chunk, priority, pos, world_num))
|
||||
.collect();
|
||||
let mut chunk_packet: HashMap<OmniPeerId, Vec<(ChunkDelta, u8)>> = HashMap::new();
|
||||
for (chunk, who_sending) in updated_chunks.iter().zip(chunks_to_send.iter()) {
|
||||
let Some(delta) = self.outbound_model.get_chunk_delta(*chunk, false) else {
|
||||
continue;
|
||||
};
|
||||
for (peer, pri) in who_sending {
|
||||
chunk_packet
|
||||
.entry(*peer)
|
||||
.or_default()
|
||||
.push((delta.clone(), *pri));
|
||||
}
|
||||
}
|
||||
let mut emit_queue = Vec::new();
|
||||
for (peer, chunkpacket) in chunk_packet {
|
||||
emit_queue.push((
|
||||
Destination::Peer(peer),
|
||||
WorldNetMessage::ChunkPacket { chunkpacket },
|
||||
));
|
||||
}
|
||||
for (dst, msg) in emit_queue {
|
||||
self.emit_msg(dst, msg)
|
||||
}
|
||||
self.outbound_model.reset_change_tracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,13 @@ use std::sync::Arc;
|
|||
|
||||
use bitcode::{Decode, Encode};
|
||||
use chunk::{Chunk, CompactPixel, Pixel, PixelFlags};
|
||||
use encoding::{NoitaWorldUpdate, PixelRun, PixelRunner};
|
||||
use encoding::PixelRunner;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use shared::world_sync::{CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, PixelRun};
|
||||
use tracing::info;
|
||||
|
||||
pub(crate) mod chunk;
|
||||
pub mod encoding;
|
||||
|
||||
pub(crate) const CHUNK_SIZE: usize = 128;
|
||||
|
||||
#[derive(Debug, Encode, Decode, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct ChunkCoord(pub i32, pub i32);
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct WorldModel {
|
||||
chunks: FxHashMap<ChunkCoord, Chunk>,
|
||||
|
@ -38,7 +33,7 @@ pub(crate) struct ChunkDelta {
|
|||
}
|
||||
|
||||
impl ChunkData {
|
||||
pub(crate) fn make_random() -> Self {
|
||||
/*pub(crate) fn make_random() -> Self {
|
||||
let mut runner = PixelRunner::new();
|
||||
for i in 0..CHUNK_SIZE * CHUNK_SIZE {
|
||||
runner.put_pixel(
|
||||
|
@ -51,7 +46,7 @@ impl ChunkData {
|
|||
}
|
||||
let runs = runner.build();
|
||||
ChunkData { runs }
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(mat: u16) -> Self {
|
||||
|
@ -123,17 +118,17 @@ impl WorldModel {
|
|||
self.updated_chunks.insert(chunk_coord);
|
||||
}*/
|
||||
|
||||
fn get_pixel(&self, x: i32, y: i32) -> Pixel {
|
||||
/*fn get_pixel(&self, x: i32, y: i32) -> Pixel {
|
||||
let (chunk_coord, offset) = Self::get_chunk_coords(x, y);
|
||||
self.chunks
|
||||
.get(&chunk_coord)
|
||||
.map(|chunk| chunk.pixel(offset))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn apply_noita_update(
|
||||
&mut self,
|
||||
update: &NoitaWorldUpdate,
|
||||
update: NoitaWorldUpdate,
|
||||
changed: &mut FxHashSet<ChunkCoord>,
|
||||
) {
|
||||
fn set_pixel(pixel: Pixel, chunk: &mut Chunk, offset: usize) -> bool {
|
||||
|
@ -145,21 +140,23 @@ impl WorldModel {
|
|||
false
|
||||
}
|
||||
}
|
||||
let header = &update.header;
|
||||
let runs = &update.runs;
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
let (mut chunk_coord, _) = Self::get_chunk_coords(header.x, header.y);
|
||||
let mut chunk = self.chunks.entry(chunk_coord).or_default();
|
||||
for run in runs {
|
||||
let (start_x, start_y) = (
|
||||
update.coord.0 * CHUNK_SIZE as i32,
|
||||
update.coord.1 * CHUNK_SIZE as i32,
|
||||
);
|
||||
let mut chunk_coord = update.coord;
|
||||
let mut chunk = self.chunks.entry(update.coord).or_default();
|
||||
for run in update.runs {
|
||||
let flags = if run.data.flags > 0 {
|
||||
PixelFlags::Fluid
|
||||
} else {
|
||||
PixelFlags::Normal
|
||||
};
|
||||
for _ in 0..run.length {
|
||||
let xs = header.x + x;
|
||||
let ys = header.y + y;
|
||||
let xs = start_x + x;
|
||||
let ys = start_y + y;
|
||||
let (new_chunk_coord, offset) = Self::get_chunk_coords(xs, ys);
|
||||
if chunk_coord != new_chunk_coord {
|
||||
chunk_coord = new_chunk_coord;
|
||||
|
@ -178,37 +175,32 @@ impl WorldModel {
|
|||
changed.remove(&chunk_coord);
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
if x == i32::from(header.w) + 1 {
|
||||
if x == CHUNK_SIZE as i32 {
|
||||
x = 0;
|
||||
y += 1;
|
||||
} else {
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_noita_update(&self, x: i32, y: i32, w: u32, h: u32) -> NoitaWorldUpdate {
|
||||
assert!(w <= 256);
|
||||
assert!(h <= 256);
|
||||
let mut runner = PixelRunner::new();
|
||||
for j in 0..(h as i32) {
|
||||
for i in 0..(w as i32) {
|
||||
runner.put_pixel(self.get_pixel(x + i, y + j).to_raw())
|
||||
}
|
||||
}
|
||||
runner.into_noita_update(x, y, (w - 1) as u8, (h - 1) as u8)
|
||||
}
|
||||
|
||||
pub fn get_all_noita_updates(&self) -> Vec<Vec<u8>> {
|
||||
pub fn get_all_noita_updates(&mut self) -> Vec<NoitaWorldUpdate> {
|
||||
let mut updates = Vec::new();
|
||||
for chunk_coord in &self.updated_chunks {
|
||||
let update = self.get_noita_update(
|
||||
chunk_coord.0 * (CHUNK_SIZE as i32),
|
||||
chunk_coord.1 * (CHUNK_SIZE as i32),
|
||||
CHUNK_SIZE as u32,
|
||||
CHUNK_SIZE as u32,
|
||||
);
|
||||
updates.push(update.save());
|
||||
for coord in self.updated_chunks.drain() {
|
||||
if let Some(chunk) = self.chunks.get_mut(&coord) {
|
||||
chunk.clear_changed();
|
||||
let mut runner = PixelRunner::new();
|
||||
for j in 0..CHUNK_SIZE {
|
||||
for i in 0..CHUNK_SIZE {
|
||||
runner.put_pixel(chunk.pixel(i + j * CHUNK_SIZE).to_raw())
|
||||
}
|
||||
}
|
||||
updates.push(NoitaWorldUpdate {
|
||||
coord,
|
||||
runs: runner.build(),
|
||||
});
|
||||
}
|
||||
}
|
||||
updates
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
use std::num::NonZeroU16;
|
||||
|
||||
use super::{ChunkData, encoding::PixelRunner};
|
||||
use bitcode::{Decode, Encode};
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
|
||||
use super::{
|
||||
CHUNK_SIZE, ChunkData,
|
||||
encoding::{PixelRunner, RawPixel},
|
||||
};
|
||||
use shared::world_sync::{CHUNK_SIZE, RawPixel};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode)]
|
||||
pub enum PixelFlags {
|
||||
|
|
|
@ -1,46 +1,12 @@
|
|||
use bitcode::{Decode, Encode};
|
||||
use bytemuck::{AnyBitPattern, NoUninit, bytes_of, pod_read_unaligned};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::mem::size_of;
|
||||
|
||||
#[derive(Debug, Clone, Copy, AnyBitPattern, NoUninit, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Header {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub w: u8,
|
||||
pub h: u8,
|
||||
pub run_count: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct NoitaWorldUpdate {
|
||||
pub(crate) header: Header,
|
||||
pub(crate) runs: Vec<PixelRun<RawPixel>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
|
||||
pub(crate) struct RawPixel {
|
||||
pub material: u16,
|
||||
pub flags: u8,
|
||||
}
|
||||
|
||||
struct ByteParser<'a> {
|
||||
use shared::world_sync::PixelRun;
|
||||
/*struct ByteParser<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
/// Stores a run of pixels.
|
||||
/// Not specific to Noita side - length is an actual length
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Encode, Decode)]
|
||||
pub struct PixelRun<Pixel> {
|
||||
pub length: u32,
|
||||
pub data: Pixel,
|
||||
}
|
||||
}*/
|
||||
|
||||
/// Converts a normal sequence of pixels to a run-length-encoded one.
|
||||
pub struct PixelRunner<Pixel> {
|
||||
current_pixel: Option<Pixel>,
|
||||
current_run_len: u32,
|
||||
current_run_len: u16,
|
||||
runs: Vec<PixelRun<Pixel>>,
|
||||
}
|
||||
|
||||
|
@ -86,24 +52,7 @@ impl<Pixel: Eq + Copy> PixelRunner<Pixel> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PixelRunner<RawPixel> {
|
||||
/// Note: w/h are actualy width/height -1
|
||||
pub fn into_noita_update(self, x: i32, y: i32, w: u8, h: u8) -> NoitaWorldUpdate {
|
||||
let runs = self.build();
|
||||
NoitaWorldUpdate {
|
||||
header: Header {
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
run_count: runs.len() as u16,
|
||||
},
|
||||
runs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ByteParser<'a> {
|
||||
/*impl<'a> ByteParser<'a> {
|
||||
fn new(data: &'a [u8]) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
@ -117,7 +66,7 @@ impl<'a> ByteParser<'a> {
|
|||
|
||||
fn next_run(&mut self) -> PixelRun<RawPixel> {
|
||||
PixelRun {
|
||||
length: u32::from(self.next::<u16>()) + 1,
|
||||
length: self.next::<u16>() + 1,
|
||||
data: RawPixel {
|
||||
material: self.next(),
|
||||
flags: self.next(),
|
||||
|
@ -125,37 +74,4 @@ impl<'a> ByteParser<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoitaWorldUpdate {
|
||||
pub fn load(data: &[u8]) -> Self {
|
||||
let mut parser = ByteParser::new(data);
|
||||
|
||||
let header: Header = parser.next();
|
||||
let mut runs = Vec::with_capacity(header.run_count.into());
|
||||
|
||||
for _ in 0..header.run_count {
|
||||
runs.push(parser.next_run());
|
||||
}
|
||||
|
||||
assert!(parser.data.is_empty());
|
||||
|
||||
Self { header, runs }
|
||||
}
|
||||
pub fn save(&self) -> Vec<u8> {
|
||||
let header = Header {
|
||||
run_count: self.runs.len() as u16,
|
||||
..self.header
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
buf.extend_from_slice(bytes_of(&header));
|
||||
|
||||
for run in &self.runs {
|
||||
let len = u16::try_from(run.length - 1).unwrap();
|
||||
buf.extend_from_slice(bytes_of(&len));
|
||||
buf.extend_from_slice(bytes_of(&run.data.material));
|
||||
buf.extend_from_slice(bytes_of(&run.data.flags));
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue