Skip to content

Further Client & Editor improvements #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Jun 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3c7c989
Add player flags for input.
Jupeyy May 6, 2025
b547718
Fix spectate vote and vote remaining time.
Jupeyy May 6, 2025
d5c8ee5
Move event handling in own functions.
Jupeyy May 7, 2025
4c5ce11
Respect player name & clan in browser search.
Jupeyy May 7, 2025
c139007
Fix plot height in pred timer example.
Jupeyy May 15, 2025
4d81699
Fix new clippy 1.87.0 warnings
Jupeyy May 16, 2025
a9b4e82
Allow filesystem constructor with data dir.
Jupeyy May 22, 2025
4b66bc0
Update libtw2 and properly implement reconnect.
Jupeyy Jun 1, 2025
9c4c3ca
Try a different ping calculation in legacy proxy.
Jupeyy Jun 1, 2025
346002f
Always try to capture backtrace in panic handler.
Jupeyy Jun 1, 2025
6d96dd0
Simplify primitive streaming.
Jupeyy Jun 3, 2025
3ee2156
Rename `client-extra` -> `assets-splitting`.
Jupeyy Jun 4, 2025
6f78b96
Add a wrapper trait for apps with graphics to simplify calls that...
Jupeyy Jun 4, 2025
3671d50
Wrap `NativeApp` for operating systems without app state logic to mat…
Jupeyy Jun 4, 2025
281e5f4
Use `IoFileSys` for config saving to match loading behavior.
Jupeyy Jun 4, 2025
23ff5fc
Decouple more window related config handling and reduce duplicated code.
Jupeyy Jun 4, 2025
a41f85c
Add proper camera concept.
Jupeyy Jun 6, 2025
4375524
Move legacy map into own crate.
Jupeyy Jun 6, 2025
88d68ff
Switch to proper arguments for physics layer rendering.
Jupeyy Jun 6, 2025
16b57a9
Convert rotation ratios to radians in animations, apply body rotation…
Jupeyy Jun 7, 2025
59b78b6
Rename some enums and use `EnumCount` for the number of supported var…
Jupeyy Jun 8, 2025
a2e4453
Add quad groups to optimize quads with equal animation props (or none).
Jupeyy Jun 8, 2025
bd346ab
Fix warnings from clippy nightly.
Jupeyy Jun 9, 2025
0aab0ff
Switch to a tar based map format (with checks for downloaded maps).
Jupeyy Jun 10, 2025
d37a70a
Don't assume `CMapItemLayerTilemap` size. Simply let the loader deal …
Jupeyy Jun 12, 2025
12cc797
Be graceful for duplicated external image assets.
Jupeyy Jun 12, 2025
43e9817
Allow lossy sound & image names.
Jupeyy Jun 12, 2025
535cc54
Add support for tile map layer version < 3.
Jupeyy Jun 12, 2025
005cf53
Consistent unique image order during conversions.
Jupeyy Jun 12, 2025
b1ebd13
Be less tolerant against invalid utf8 strings.
Jupeyy Jun 13, 2025
f446c1d
Improvements to map convert test.
Jupeyy Jun 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 78 additions & 40 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ members = [
"src/skin-convert",
"src/part-convert",
"src/master-server",
"game/client-extra",
"game/assets-splitting",
"game/client-map",
"game/client-render-base",
"game/client-render",
Expand Down Expand Up @@ -109,7 +109,7 @@ members = [
"game/community", "lib/input-binds", "game/ghost", "game/client-ghost", "game/client-replay", "game/editor-wasm",
"game/client-notifications", "src/editor-server", "src/extra-convert", "game/editor-interface", "game/editor-auto-mapper-wasm",
"game/api-auto-mapper",
"examples/wasm-modules/auto-mapper", "game/legacy_proxy",
"examples/wasm-modules/auto-mapper", "game/legacy-proxy", "game/camera", "game/legacy-map",
]

[package]
Expand Down Expand Up @@ -179,7 +179,8 @@ prediction-timer = { path = "game/prediction-timer" }
editor-wasm = { path = "game/editor-wasm", default-features = false }
game-state-wasm = { path = "game/game-state-wasm" }
editor = { path = "game/editor", default-features = false }
legacy_proxy = { path = "game/legacy_proxy" }
legacy-proxy = { path = "game/legacy-proxy" }
camera = { path = "game/camera" }

egui-winit = { version = "0.31.1", default-features = false, features = ["x11", "arboard", "links"] }
tokio = { version = "1.45.0", features = ["rt-multi-thread", "sync", "fs", "time", "macros"] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn render(ui: &mut egui::Ui, history: &mut VecDeque<PredictionTiming>) {

let line = Line::new("", sin);
Plot::new(name)
.height(150.0)
.height(75.0)
.show(ui, |plot_ui| plot_ui.line(line));
};

Expand Down
1 change: 1 addition & 0 deletions examples/wasm-modules/state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ command-parser = { path = "../../../lib/command-parser" }

api-state = { path = "../../../game/api-state" }
game-base = { path = "../../../game/game-base" }
legacy-map = { path = "../../../game/legacy-map" }
vanilla = { path = "../../../game/vanilla" }
game-interface = { path = "../../../game/game-interface" }
map = { path = "../../../game/map" }
Expand Down
2 changes: 1 addition & 1 deletion game/assets-base/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod loader;
pub mod tar;
pub mod verify;

use std::collections::HashMap;
Expand Down
45 changes: 0 additions & 45 deletions game/assets-base/src/loader.rs

This file was deleted.

212 changes: 212 additions & 0 deletions game/assets-base/src/tar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use std::borrow::Cow;
use std::ops::Range;
use std::path::Path;

use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::{io::Read, path::PathBuf};
use tar::Header;

use anyhow::anyhow;
use rustc_hash::FxHashMap;
use tar::EntryType;

pub struct TarReaderWrapper {
cur_pos: Arc<AtomicUsize>,
file: Arc<Vec<u8>>,
}

impl std::io::Read for TarReaderWrapper {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let pos = self.cur_pos.load(std::sync::atomic::Ordering::Relaxed);
if pos >= self.file.len() {
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
}
let file = &self.file[pos..];
let read_len = file.len().min(buf.len());
if read_len == 0 {
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, ""));
}
buf.copy_from_slice(&file[0..read_len]);
self.cur_pos.store(
self.cur_pos.load(std::sync::atomic::Ordering::Relaxed) + read_len,
std::sync::atomic::Ordering::Relaxed,
);
Ok(read_len)
}
}

impl std::io::Seek for TarReaderWrapper {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
match pos {
std::io::SeekFrom::Start(off) => {
self.cur_pos
.store(off as usize, std::sync::atomic::Ordering::Relaxed);
Ok(off)
}
std::io::SeekFrom::End(off) => {
let pos = self.file.len() as i64 + off;
if pos < 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"seek pos below 0",
));
}
let pos = pos as usize;
self.cur_pos
.store(pos, std::sync::atomic::Ordering::Relaxed);
Ok(pos as u64)
}
std::io::SeekFrom::Current(off) => {
let pos = self.cur_pos.load(std::sync::atomic::Ordering::Relaxed) as i64 + off;
if pos < 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"seek pos below 0",
));
}
let pos = pos as usize;
self.cur_pos
.store(pos, std::sync::atomic::Ordering::Relaxed);
Ok(pos as u64)
}
}
}
}

pub type TarBuilder = tar::Builder<Vec<u8>>;
pub struct TarReader {
tar: tar::Archive<TarReaderWrapper>,

cur_pos: Arc<AtomicUsize>,
file: Arc<Vec<u8>>,
}
pub struct TarEntry {
file: Arc<Vec<u8>>,
range: Range<usize>,
}
pub type TarEntries = FxHashMap<PathBuf, TarEntry>;

pub fn new_tar() -> TarBuilder {
let mut builder = tar::Builder::new(Vec::new());
builder.mode(tar::HeaderMode::Deterministic);
builder
}

pub fn tar_add_file(builder: &mut TarBuilder, path: impl AsRef<Path>, file: &[u8]) {
let mut header = Header::new_gnu();
header.set_cksum();
header.set_size(file.len() as u64);
header.set_mode(0o644);
header.set_uid(1000);
header.set_gid(1000);
builder
.append_data(&mut header, path, std::io::Cursor::new(file))
.unwrap();
}

/// Constructs a reader for tar files.
///
/// ### Performance
///
/// Causes smaller heap allocations.
pub fn tar_reader(file: Vec<u8>) -> TarReader {
let file = Arc::new(file);
let cur_pos: Arc<AtomicUsize> = Default::default();
TarReader {
tar: tar::Archive::new(TarReaderWrapper {
cur_pos: cur_pos.clone(),
file: file.clone(),
}),
cur_pos,
file,
}
}

fn entry_to_path<T: std::io::Read>(
entry: &mut tar::Entry<'_, T>,
) -> Option<Result<PathBuf, anyhow::Error>> {
let ty = entry.header().entry_type();
if let EntryType::Regular = ty {
Some(
entry
.path()
.map(|path| path.to_path_buf())
.map_err(|err| anyhow::anyhow!(err)),
)
} else if matches!(ty, EntryType::Directory) {
None
} else {
Some(Err(anyhow!(
"The tar reader expects files & dictionaries only"
)))
}
}

fn tar_entry_to_vec(
entry: &mut tar::Entry<'_, std::io::Cursor<Cow<[u8]>>>,
) -> anyhow::Result<Vec<u8>> {
let mut file: Vec<_> = Default::default();
entry.read_to_end(&mut file)?;
Ok(file)
}

pub fn read_tar_files(file: Cow<[u8]>) -> anyhow::Result<FxHashMap<PathBuf, Vec<u8>>> {
let mut file = tar::Archive::new(std::io::Cursor::new(file));
match file.entries_with_seek() {
Ok(entries) => entries
.filter_map(|entry| {
entry
.map_err(|err| anyhow::anyhow!(err))
.map(|mut entry| {
entry_to_path(&mut entry).map(|p| {
p.and_then(|path| {
let file = tar_entry_to_vec(&mut entry)?;
Ok((path, file))
})
})
})
.transpose()
.map(|r| r.and_then(|r| r))
})
.collect::<anyhow::Result<FxHashMap<_, _>>>(),
Err(err) => Err(anyhow::anyhow!(err)),
}
}

pub fn tar_file_entries(reader: &mut TarReader) -> anyhow::Result<FxHashMap<PathBuf, TarEntry>> {
reader
.tar
.entries_with_seek()?
.filter_map(|entry| {
entry
.map_err(|err| anyhow::anyhow!(err))
.map(|mut entry| {
entry_to_path(&mut entry).map(|p| {
p.map(|p| {
let pos = reader.cur_pos.load(std::sync::atomic::Ordering::Relaxed);
(
p,
TarEntry {
file: reader.file.clone(),
range: pos..pos + entry.size() as usize,
},
)
})
})
})
.transpose()
.map(|r| r.and_then(|r| r))
})
.collect::<anyhow::Result<FxHashMap<_, _>>>()
}

pub fn tar_entry_to_file(entry: &TarEntry) -> anyhow::Result<&[u8]> {
Ok(entry.file.get(entry.range.clone()).ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"tar file's size in header was \
bigger than the actual file",
)
})?)
}
1 change: 1 addition & 0 deletions game/assets-base/src/verify.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod json;
pub mod ogg_vorbis;
pub mod txt;
4 changes: 4 additions & 0 deletions game/assets-base/src/verify/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn verify_json(file: &[u8]) -> anyhow::Result<()> {
serde_json::from_slice::<serde_json::Value>(file)?;
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "client-extra"
name = "assets-splitting"
version = "0.1.0"
edition = "2021"

Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions game/camera/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "camera"
version = "0.1.0"
edition = "2021"

[dependencies]
math = { path = "../../lib/math" }
hiarc = { path = "../../lib/hiarc", features = ["derive"] }
graphics = { path = "../../lib/graphics" }
graphics-types = { path = "../../lib/graphics-types" }

map = { path = "../map" }

Loading
Loading