level parsing i think

This commit is contained in:
Reid 2023-09-05 21:43:36 -07:00
parent 22a87d6a38
commit 1e913b9ec8
Signed by: reidlab
GPG key ID: 6C9EAA3364F962C8
8 changed files with 253 additions and 15 deletions

View file

@ -108,6 +108,7 @@ pub struct Level {
pub editor_time_copies: i32,
pub song_id: i32,
pub length: i32,
pub length_real: f64,
pub objects: i32,
pub coins: i32,
pub has_ldm: i32,
@ -142,6 +143,7 @@ pub struct NewLevel {
pub editor_time_copies: i32,
pub song_id: i32,
pub length: i32,
pub length_real: f64,
pub objects: i32,
pub coins: i32,
pub has_ldm: i32,

View file

@ -46,6 +46,7 @@ diesel::table! {
editor_time_copies -> Int4,
song_id -> Int4,
length -> Int4,
length_real -> Int4,
objects -> Int4,
coins -> Int4,
has_ldm -> Int4,

View file

@ -73,7 +73,30 @@ pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str
None => { extra_string = helpers::levels::DEFAULT_EXTRA_STRING.to_owned() }
}
// db shit
// level parsing
let level_raw_objects = helpers::levels::decode(input.levelString.clone());
let level_objects = helpers::levels::to_objectdata(level_raw_objects.clone());
let inner_level_string = level_raw_objects
.iter()
.find(|obj| !obj.contains_key("1") && obj.get("kA9") == Some(&"0".to_string()))
.expect("couldnt decode inner level string");
let level_length_secs = helpers::levels::measure_length(
level_objects.clone(),
inner_level_string.get("kA4").unwrap_or(&String::from("0")).parse::<i32>().expect("kA4 not int")
);
let coins_val = level_objects.iter().filter(|obj| obj.id() == 1329).count(); // 1329 is coin id
let objects_val = level_objects.len();
let two_player_val = if inner_level_string.get("kA10").unwrap_or(&String::from("0")).parse::<i32>().expect("kA10 not int") == 1 { 1 } else { 0 };
let level_length_val = helpers::levels::secs_to_time(level_length_secs);
// blocking
if coins_val > 3 {
return status::Custom(Status::Ok, "1")
}
// data base 🤣😁
use crate::models::{Level, NewLevel};
{
@ -110,11 +133,11 @@ pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str
editor_time.eq(input.wt.unwrap_or(0)),
editor_time_copies.eq(input.wt2.unwrap_or(0)),
song_id.eq(song_id_val),
length.eq(0), // unimplemeneted
objects.eq(0), // unimplemented
coins.eq(0), // unimplemented
length.eq(level_length_val),
objects.eq(objects_val as i32),
coins.eq(coins_val as i32),
has_ldm.eq(input.ldm.unwrap_or(0)),
two_player.eq(0) // unimplemented
two_player.eq(two_player_val)
))
.get_result::<Level, >(connection)
.expect("failed to update level");
@ -143,11 +166,12 @@ pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str
editor_time: input.wt.unwrap_or(0),
editor_time_copies: input.wt2.unwrap_or(0),
song_id: song_id_val,
length: 0, // not implemeneted
objects: 0, // not implemeneted
coins: 0, // not implemeneted
length: level_length_val,
length_real: level_length_secs,
objects: objects_val as i32,
coins: coins_val as i32,
has_ldm: input.ldm.unwrap_or(0),
two_player: 0 // not implemented
two_player: two_player_val
};
let inserted_level = diesel::insert_into(levels)

View file

@ -1,7 +1,180 @@
use std::sync::LazyLock;
use std::io::prelude::*;
use base64::{Engine as _, engine::general_purpose};
use flate2::read::GzDecoder;
pub static DEFAULT_EXTRA_STRING: LazyLock<String> = LazyLock::new(|| {
let string = String::from("29_29_29_40_29_29_29_29_29_29_29_29_29_29_29_29");
return string;
});
});
macro_rules! object_prop_bool {
($key:expr, $name:ident) => {
pub fn $name(&self) -> bool {
self.raw.get($key).map_or(false, |value| value == "1")
}
};
}
use std::collections::HashMap;
#[derive(Clone)]
pub struct ObjectData {
raw: HashMap<String, String>
}
impl ObjectData {
pub fn new(raw: HashMap<String, String>) -> Self {
ObjectData { raw }
}
pub fn id(&self) -> i32 {
self.raw.get("1").unwrap_or(&String::new()).parse().unwrap_or(0)
}
pub fn x(&self) -> f64 {
self.raw.get("2").unwrap_or(&String::new()).parse().unwrap_or(0.0)
}
pub fn y(&self) -> f64 {
self.raw.get("3").unwrap_or(&String::new()).parse().unwrap_or(0.0)
}
object_prop_bool!("13", checked);
}
pub mod portal_speed {
pub enum PortalSpeed {
Slow,
Normal,
Medium,
Fast,
VeryFast
}
impl PortalSpeed {
pub fn portal_speed(&self) -> f64 {
match self {
PortalSpeed::Slow => 251.16,
PortalSpeed::Normal => 311.58,
PortalSpeed::Medium => 387.42,
PortalSpeed::Fast => 478.0,
PortalSpeed::VeryFast => 576.0
}
}
}
}
pub fn id_to_portal_speed(id: i32) -> Option<portal_speed::PortalSpeed> {
match id {
200 => Some(portal_speed::PortalSpeed::Slow),
201 => Some(portal_speed::PortalSpeed::Normal),
202 => Some(portal_speed::PortalSpeed::Medium),
203 => Some(portal_speed::PortalSpeed::Fast),
1334 => Some(portal_speed::PortalSpeed::VeryFast),
_ => None,
}
}
pub fn get_seconds_from_xpos(pos: f64, start_speed: portal_speed::PortalSpeed, portals: Vec<ObjectData>) -> f64 {
let mut speed;
let mut last_obj_pos = 0.0;
let mut last_segment = 0.0;
let mut segments = 0.0;
speed = start_speed.portal_speed();
if portals.is_empty() {
return pos / speed
}
for portal in portals {
let mut s = portal.x() - last_obj_pos;
if pos < s {
s = s / speed;
last_segment = s;
segments += s;
speed = id_to_portal_speed(portal.id()).expect("not a portal").portal_speed();
last_obj_pos = portal.x()
}
}
return ((pos - last_segment) / speed) + segments;
}
pub fn measure_length(objects: Vec<ObjectData>, ka4: i32) -> f64 {
let start_speed = match ka4 {
0 => portal_speed::PortalSpeed::Normal,
1 => portal_speed::PortalSpeed::Slow,
2 => portal_speed::PortalSpeed::Medium,
3 => portal_speed::PortalSpeed::Fast,
4 => portal_speed::PortalSpeed::VeryFast,
_ => portal_speed::PortalSpeed::Normal
};
let max_x_pos = objects
.iter()
.fold(0.0, |max_x, obj| f64::max(max_x, obj.x()));
let mut portals: Vec<ObjectData> = objects
.into_iter()
.filter(|obj| id_to_portal_speed(obj.id()).is_some() && obj.checked())
.collect();
portals.sort_by(|a, b| a.x().partial_cmp(&b.x()).unwrap());
return get_seconds_from_xpos(max_x_pos, start_speed, portals)
}
pub fn secs_to_time(time: f64) -> i32 {
match time {
time if time < 10.0 => return 0,
time if time < 30.0 => return 1,
time if time < 60.0 => return 2,
time if time < 120.0 => return 3,
time if time >= 120.0 => return 4,
_ => 0
}
}
pub fn array_to_hash(arr: Vec<String>) -> HashMap<String, String> {
return arr.chunks(2)
.map(|chunk| (chunk[0].clone(), chunk[1].clone()))
.collect()
}
pub fn parse(raw_level_data: &str) -> Vec<HashMap<String, String>> {
raw_level_data
.trim_end_matches(';')
.split(';')
.map(|v| {
let values: Vec<String> = v.split(',').map(|s| s.to_string()).collect();
array_to_hash(values)
})
.collect()
}
pub fn decode(level_data: String) -> Vec<HashMap<String, String>> {
let decoded_bytes = general_purpose::URL_SAFE.decode(level_data).expect("couldnt decode b64");
let mut decoder = GzDecoder::new(&decoded_bytes[..]);
let mut uncompressed_data = String::new();
decoder.read_to_string(&mut uncompressed_data).expect("err unzipping level");
return parse(uncompressed_data.as_str())
}
pub fn to_objectdata(objects: Vec<HashMap<String, String>>) -> Vec<ObjectData> {
return objects
.into_iter()
.filter(|v| v.contains_key("1"))
.map(|v| ObjectData::new(v))
.collect()
}