mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-19 14:26:51 +00:00
Add preliminary support for restoring character backups from Auracite
Not much is importable yet - pretty much just the character's name and appearance.
This commit is contained in:
parent
e4870762eb
commit
243d94c586
8 changed files with 277 additions and 15 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ config.json
|
|||
*.db
|
||||
config.yaml
|
||||
src/opcodes.rs
|
||||
backups/
|
||||
|
|
151
Cargo.lock
generated
151
Cargo.lock
generated
|
@ -17,6 +17,15 @@ version = "2.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
|
@ -141,18 +150,49 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
|
||||
dependencies = [
|
||||
"bzip2-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2-sys"
|
||||
version = "0.1.13+1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.17"
|
||||
|
@ -168,12 +208,53 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
|
@ -214,6 +295,16 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -431,6 +522,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -481,6 +573,18 @@ dependencies = [
|
|||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lockfree-object-pool"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "lua-src"
|
||||
version = "547.0.0"
|
||||
|
@ -500,6 +604,16 @@ dependencies = [
|
|||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
|
@ -875,6 +989,12 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -1184,8 +1304,39 @@ dependencies = [
|
|||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27c03817464f64e23f6f37574b4fdc8cf65925b5bfd2b0f2aedf959791941f88"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"bzip2",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"lzma-rs",
|
||||
"memchr",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"lockfree-object-pool",
|
||||
"log",
|
||||
"once_cell",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
|
|
@ -87,3 +87,6 @@ bitflags = { version = "1.3", default-features = false }
|
|||
|
||||
# For server-side scripting
|
||||
mlua = { version = "0.10", features = ["lua51", "vendored", "send", "async"], default-features = false }
|
||||
|
||||
# For character backup decompression
|
||||
zip = { version = "2.2", features = ["deflate", "lzma", "bzip2"], default-features = false }
|
||||
|
|
6
USAGE.md
6
USAGE.md
|
@ -72,6 +72,12 @@ In this example, lobby number 4 will replace the **Aether data center**, but the
|
|||
|
||||
Some other launchers (like XIVLauncher) will allow you to specify these extra arguments, but they will still authenticate to the retail servers. You can still connect to Kawari with this way, but **make sure to specify your own session ID, or your retail account's session ID will be sent to the lobby server**!
|
||||
|
||||
## Importing characters from retail
|
||||
|
||||
It's possible to import existing characters from the retail server using [Auracite](https://auracite.xiv.zone). Place the backup ZIP under `backups/` and the World server will import it into the database the next time it's started.
|
||||
|
||||
This feature is still a work-in-progress, and not all data is imported yet.
|
||||
|
||||
## Chat commands
|
||||
|
||||
### Debug commands
|
||||
|
|
|
@ -32,6 +32,39 @@ pub struct CustomizeData {
|
|||
pub face_paint_color: u8,
|
||||
}
|
||||
|
||||
impl From<physis::chardat::CustomizeData> for CustomizeData {
|
||||
fn from(value: physis::chardat::CustomizeData) -> Self {
|
||||
Self {
|
||||
race: value.race as u8 + 1,
|
||||
gender: value.gender as u8,
|
||||
age: value.age,
|
||||
height: value.height,
|
||||
subrace: value.subrace as u8 + 1,
|
||||
face: value.face,
|
||||
hair: value.hair,
|
||||
enable_highlights: value.enable_highlights as u8,
|
||||
skin_tone: value.skin_tone,
|
||||
right_eye_color: value.right_eye_color,
|
||||
hair_tone: value.hair_tone,
|
||||
highlights: value.highlights,
|
||||
facial_features: value.facial_features,
|
||||
facial_feature_color: value.facial_feature_color,
|
||||
eyebrows: value.eyebrows,
|
||||
left_eye_color: value.left_eye_color,
|
||||
eyes: value.eyes,
|
||||
nose: value.nose,
|
||||
jaw: value.jaw,
|
||||
mouth: value.mouth,
|
||||
lips_tone_fur_pattern: value.lips_tone_fur_pattern,
|
||||
race_feature_size: value.race_feature_size,
|
||||
race_feature_type: value.race_feature_type,
|
||||
bust: value.bust,
|
||||
face_paint: value.face_paint,
|
||||
face_paint_color: value.face_paint_color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomizeData {
|
||||
pub fn to_json(&self) -> Value {
|
||||
json!([
|
||||
|
|
|
@ -32,12 +32,12 @@ impl CharaMake {
|
|||
pub fn to_json(&self) -> String {
|
||||
let content = json!([
|
||||
self.customize.to_json(),
|
||||
self.unk1,
|
||||
self.guardian,
|
||||
self.birth_month,
|
||||
self.birth_day,
|
||||
self.classjob_id,
|
||||
self.unk2,
|
||||
self.unk1.to_string(),
|
||||
self.guardian.to_string(),
|
||||
self.birth_month.to_string(),
|
||||
self.birth_day.to_string(),
|
||||
self.classjob_id.to_string(),
|
||||
self.unk2.to_string(),
|
||||
]);
|
||||
|
||||
let obj = json!({
|
||||
|
|
|
@ -591,13 +591,16 @@ pub async fn send_custom_world_packet(segment: CustomIpcSegment) -> Option<Custo
|
|||
// read response
|
||||
let mut buf = [0; 10024]; // TODO: this large buffer is just working around these packets not being compressed, but they really should be!
|
||||
let n = stream.read(&mut buf).await.expect("Failed to read data!");
|
||||
if n != 0 {
|
||||
println!("Got {n} bytes of response!");
|
||||
|
||||
println!("Got {n} bytes of response!");
|
||||
let (segments, _) = parse_packet::<CustomIpcSegment>(&buf[..n], &mut packet_state).await;
|
||||
|
||||
let (segments, _) = parse_packet::<CustomIpcSegment>(&buf[..n], &mut packet_state).await;
|
||||
|
||||
match &segments[0].segment_type {
|
||||
SegmentType::CustomIpc { data } => Some(data.clone()),
|
||||
_ => None,
|
||||
return match &segments[0].segment_type {
|
||||
SegmentType::CustomIpc { data } => Some(data.clone()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::sync::Mutex;
|
||||
use std::{io::Read, sync::Mutex};
|
||||
|
||||
use rusqlite::Connection;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
common::Position,
|
||||
common::{CustomizeData, Position},
|
||||
lobby::{
|
||||
CharaMake, ClientSelectData, RemakeMode,
|
||||
ipc::{CharacterDetails, CharacterFlag},
|
||||
|
@ -46,9 +47,73 @@ impl WorldDatabase {
|
|||
connection.execute(query, ()).unwrap();
|
||||
}
|
||||
|
||||
Self {
|
||||
let this = Self {
|
||||
connection: Mutex::new(connection),
|
||||
};
|
||||
|
||||
// Import any backups
|
||||
// NOTE: This won't make sense when service accounts are a real thing, so the functionality will probably be moved
|
||||
{
|
||||
if let Ok(paths) = std::fs::read_dir("./backups") {
|
||||
for path in paths {
|
||||
let path = path.unwrap().path();
|
||||
if path.extension().unwrap() == "zip" {
|
||||
this.import_character(path.to_str().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn import_character(&self, path: &str) {
|
||||
tracing::info!("Importing character backup {path}...");
|
||||
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
|
||||
let mut archive = zip::ZipArchive::new(file).unwrap();
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CharacterJson {
|
||||
name: String,
|
||||
}
|
||||
|
||||
let character: CharacterJson;
|
||||
{
|
||||
let mut character_file = archive.by_name("character.json").unwrap();
|
||||
|
||||
let mut json_string = String::new();
|
||||
character_file.read_to_string(&mut json_string).unwrap();
|
||||
|
||||
character = serde_json::from_str(&json_string).unwrap();
|
||||
}
|
||||
|
||||
if !self.check_is_name_free(&character.name) {
|
||||
tracing::warn!("* Skipping since this character already exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
let charsave_file = archive.by_name("FFXIV_CHARA_01.dat").unwrap();
|
||||
let charsave_bytes: Vec<u8> = charsave_file.bytes().map(|x| x.unwrap()).collect();
|
||||
let charsave = physis::chardat::CharacterData::from_existing(&charsave_bytes).unwrap();
|
||||
|
||||
let customize = CustomizeData::from(charsave.customize);
|
||||
|
||||
let chara_make = CharaMake {
|
||||
customize,
|
||||
unk1: 73,
|
||||
guardian: 1, // TODO: extract these as well
|
||||
birth_month: 1,
|
||||
birth_day: 1,
|
||||
classjob_id: 5,
|
||||
unk2: 1,
|
||||
};
|
||||
|
||||
// TODO: extract city-state
|
||||
self.create_player_data(&character.name, &chara_make.to_json(), 2, 132);
|
||||
|
||||
tracing::info!("{} added to the world!", character.name);
|
||||
}
|
||||
|
||||
pub fn find_player_data(&self, actor_id: u32) -> PlayerData {
|
||||
|
|
Loading…
Add table
Reference in a new issue