mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 03:57:13 +01:00
Ports rust_g -> rustlibs: logging, toml, dmi, json, and noisegen (#28858)
* Rustlibs logging, toml, dmi, and dbpnoise * missed one * Hopefully fix logging utf-8 decode errors * Fuck * Build Rust library * ports rust_g json validator * rustlibs_file clippy lint * Build Rust library * fix merge conflict --------- Co-authored-by: paradisess13[bot] <165046124+paradisess13[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
paradisess13[bot] <165046124+paradisess13[bot]@users.noreply.github.com>
parent
4094c80116
commit
5c8ba2ee4e
@@ -2,6 +2,12 @@ mod logging;
|
||||
mod mapmanip;
|
||||
mod milla;
|
||||
mod redis_pubsub;
|
||||
mod rustlibs_dmi;
|
||||
mod rustlibs_file;
|
||||
mod rustlibs_json;
|
||||
mod rustlibs_logging;
|
||||
mod rustlibs_noisegen;
|
||||
mod rustlibs_toml;
|
||||
|
||||
#[cfg(all(not(feature = "byond-515"), not(feature = "byond-516")))]
|
||||
compile_error!("Please specify byond-515 or byond-516 as a feature to specify BYOND version.");
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use byondapi::value::ByondValue;
|
||||
use png::{Decoder, Encoder};
|
||||
use std::fs::File;
|
||||
|
||||
#[byondapi::bind]
|
||||
fn dmi_strip_metadata(path: ByondValue) -> eyre::Result<ByondValue> {
|
||||
strip_metadata(&path.get_string()?)?;
|
||||
Ok(ByondValue::null())
|
||||
}
|
||||
|
||||
fn strip_metadata(path: &str) -> eyre::Result<()> {
|
||||
let mut reader = Decoder::new(File::open(path)?).read_info()?;
|
||||
let mut buf = vec![0; reader.output_buffer_size()];
|
||||
let frame_info = reader.next_frame(&mut buf)?;
|
||||
|
||||
let mut encoder = Encoder::new(File::create(path)?, frame_info.width, frame_info.height);
|
||||
encoder.set_color(frame_info.color_type);
|
||||
encoder.set_depth(frame_info.bit_depth);
|
||||
let reader_info = reader.info();
|
||||
|
||||
if let Some(palette) = reader_info.palette.clone() {
|
||||
encoder.set_palette(palette);
|
||||
}
|
||||
if let Some(trns_chunk) = reader_info.trns.clone() {
|
||||
encoder.set_palette(trns_chunk);
|
||||
}
|
||||
|
||||
let mut writer = encoder.write_header()?;
|
||||
Ok(writer.write_image_data(&buf)?)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use byondapi::value::ByondValue;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter, Read, Write},
|
||||
};
|
||||
|
||||
#[byondapi::bind]
|
||||
fn file_read(path: ByondValue) -> eyre::Result<ByondValue> {
|
||||
read(&path.get_string()?)
|
||||
}
|
||||
|
||||
fn read(path: &str) -> eyre::Result<ByondValue> {
|
||||
let file = File::open(path)?;
|
||||
let metadata = file.metadata()?;
|
||||
let mut file = BufReader::new(file);
|
||||
|
||||
let mut content = String::with_capacity(metadata.len() as usize);
|
||||
file.read_to_string(&mut content)?;
|
||||
let content = content.replace('\r', "");
|
||||
|
||||
Ok(content.try_into()?)
|
||||
}
|
||||
|
||||
#[byondapi::bind]
|
||||
fn file_write(data: ByondValue, path: ByondValue) -> eyre::Result<ByondValue> {
|
||||
let data: String = data.get_string()?;
|
||||
let path: String = path.get_string()?;
|
||||
write(&data, &path)
|
||||
}
|
||||
|
||||
fn write(data: &str, path: &str) -> eyre::Result<ByondValue> {
|
||||
let path: &std::path::Path = path.as_ref();
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let mut file = BufWriter::new(File::create(path)?);
|
||||
let written = file.write(data.as_bytes())? as f32;
|
||||
|
||||
file.flush()?;
|
||||
file.into_inner()
|
||||
.map_err(|e| std::io::Error::new(e.error().kind(), e.error().to_string()))?
|
||||
.sync_all()?;
|
||||
|
||||
Ok(written.into())
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use byondapi::value::ByondValue;
|
||||
use serde_json::Value;
|
||||
use std::cmp;
|
||||
|
||||
const VALID_JSON_MAX_RECURSION_DEPTH: usize = 8;
|
||||
|
||||
#[byondapi::bind]
|
||||
fn json_is_valid(text: ByondValue) -> eyre::Result<ByondValue> {
|
||||
let value = match serde_json::from_str::<Value>(&text.get_string()?) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Ok("false".try_into()?),
|
||||
};
|
||||
|
||||
Ok(get_recursion_level(&value).is_ok().to_string().try_into()?)
|
||||
}
|
||||
|
||||
/// Gets the recursion level of the given value
|
||||
/// If it is above `VALID_JSON_MAX_RECURSION_DEPTH`, returns Err(())
|
||||
fn get_recursion_level(value: &Value) -> Result<usize, ()> {
|
||||
let values: Vec<&Value> = match value {
|
||||
Value::Array(array) => array.iter().collect(),
|
||||
|
||||
Value::Object(map) => map.values().collect(),
|
||||
|
||||
_ => return Ok(0),
|
||||
};
|
||||
|
||||
let mut max_recursion_level = 0;
|
||||
|
||||
for value in values {
|
||||
max_recursion_level = cmp::max(max_recursion_level, get_recursion_level(value)?);
|
||||
}
|
||||
|
||||
max_recursion_level += 1;
|
||||
|
||||
if max_recursion_level >= VALID_JSON_MAX_RECURSION_DEPTH {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(max_recursion_level)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_recursion_level() {
|
||||
assert_eq!(
|
||||
get_recursion_level(&serde_json::from_str("[]").unwrap()),
|
||||
Ok(1)
|
||||
);
|
||||
assert_eq!(
|
||||
get_recursion_level(&serde_json::from_str("[[]]").unwrap()),
|
||||
Ok(2)
|
||||
);
|
||||
assert_eq!(
|
||||
get_recursion_level(&serde_json::from_str("[[[]]]").unwrap()),
|
||||
Ok(3)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_recursion_level_max_depth() {
|
||||
assert_eq!(
|
||||
get_recursion_level(
|
||||
&serde_json::from_str(&format!(
|
||||
"{}{}",
|
||||
"[".repeat(VALID_JSON_MAX_RECURSION_DEPTH),
|
||||
"]".repeat(VALID_JSON_MAX_RECURSION_DEPTH)
|
||||
))
|
||||
.unwrap()
|
||||
),
|
||||
Err(())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use byondapi::value::ByondValue;
|
||||
use chrono::Utc;
|
||||
use eyre::Context;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::hash_map::{Entry, HashMap},
|
||||
ffi::OsString,
|
||||
fs,
|
||||
fs::{File, OpenOptions},
|
||||
io::Write,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
static FILE_MAP: RefCell<HashMap<OsString, File>> = RefCell::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[byondapi::bind]
|
||||
fn log_write(path: ByondValue, data: ByondValue) -> eyre::Result<ByondValue> {
|
||||
FILE_MAP.with(|cell| -> eyre::Result<ByondValue> {
|
||||
let mut map = cell.borrow_mut();
|
||||
let path = path.get_string()?;
|
||||
let path = Path::new(&path as &str);
|
||||
let file = match map.entry(path.into()) {
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
Entry::Vacant(e) => e.insert(open(path)?),
|
||||
};
|
||||
// Byond will happily send over invalid bytes from unpurged text macros, which Rust does not like
|
||||
// This circumvents ByondValue::get_string() to strip utf-8 before it can cause a panic
|
||||
let data = data
|
||||
.get_cstring()
|
||||
.map(|cstring| cstring.to_string_lossy().into_owned())
|
||||
.wrap_err(format!("UTF-8 decode error: {:#?}", data))?;
|
||||
let iter = data.split('\n');
|
||||
|
||||
for line in iter {
|
||||
writeln!(file, "[{}] {}", Utc::now().format("%FT%T"), line)?;
|
||||
}
|
||||
|
||||
Ok(ByondValue::null())
|
||||
})
|
||||
}
|
||||
|
||||
#[byondapi::bind]
|
||||
fn log_close_all() -> eyre::Result<ByondValue> {
|
||||
FILE_MAP.with(|cell| {
|
||||
let mut map = cell.borrow_mut();
|
||||
map.clear();
|
||||
});
|
||||
Ok(ByondValue::null())
|
||||
}
|
||||
|
||||
fn open(path: &Path) -> eyre::Result<File> {
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
Ok(OpenOptions::new().append(true).create(true).open(path)?)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use byondapi::value::ByondValue;
|
||||
use dbpnoise::gen_noise;
|
||||
|
||||
#[byondapi::bind]
|
||||
fn dbp_generate(
|
||||
seed: ByondValue,
|
||||
accuracy: ByondValue,
|
||||
stamp_size: ByondValue,
|
||||
world_size: ByondValue,
|
||||
lower_range: ByondValue,
|
||||
upper_range: ByondValue,
|
||||
) -> eyre::Result<ByondValue> {
|
||||
Ok(gen_dbp_noise(
|
||||
&seed.get_string()?,
|
||||
&accuracy.get_string()?,
|
||||
&stamp_size.get_string()?,
|
||||
&world_size.get_string()?,
|
||||
&lower_range.get_string()?,
|
||||
&upper_range.get_string()?,
|
||||
)?
|
||||
.try_into()?)
|
||||
}
|
||||
|
||||
fn gen_dbp_noise(
|
||||
seed: &str,
|
||||
accuracy: &str,
|
||||
stamp_size: &str,
|
||||
world_size: &str,
|
||||
lower_range: &str,
|
||||
upper_range: &str,
|
||||
) -> eyre::Result<String> {
|
||||
let map: Vec<Vec<bool>> = gen_noise(
|
||||
seed,
|
||||
accuracy.parse::<usize>()?,
|
||||
stamp_size.parse::<usize>()?,
|
||||
world_size.parse::<usize>()?,
|
||||
lower_range.parse::<f32>()?,
|
||||
upper_range.parse::<f32>()?,
|
||||
);
|
||||
let mut result = String::new();
|
||||
for row in map {
|
||||
for cell in row {
|
||||
result.push(if cell { '1' } else { '0' });
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::gen_dbp_noise;
|
||||
|
||||
const TEST_SEED: &str = "meowrrpmraoooow~";
|
||||
#[test]
|
||||
fn test_gen_dbp_noise() {
|
||||
let value = gen_dbp_noise(TEST_SEED, "360", "4", "25", "0.1", "1.1").unwrap();
|
||||
println!("(length: {})", value.len());
|
||||
println!("{}", value);
|
||||
assert_eq!(value.len(), 625);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use byondapi::value::ByondValue;
|
||||
|
||||
#[byondapi::bind]
|
||||
fn toml_file_to_json(path: ByondValue) -> eyre::Result<ByondValue> {
|
||||
let path: String = path.get_string()?;
|
||||
Ok(serde_json::to_string(&match toml_file_to_json_impl(&path) {
|
||||
Ok(value) => serde_json::json!({
|
||||
"success": true, "content": value
|
||||
}),
|
||||
Err(error) => serde_json::json!({
|
||||
"success": false, "content": error.to_string()
|
||||
}),
|
||||
})?
|
||||
.try_into()?)
|
||||
}
|
||||
|
||||
fn toml_file_to_json_impl(path: &str) -> eyre::Result<String> {
|
||||
Ok(serde_json::to_string(&toml::from_str::<toml::Value>(
|
||||
&std::fs::read_to_string(path)?,
|
||||
)?)?)
|
||||
}
|
||||
|
||||
#[byondapi::bind]
|
||||
fn toml_encode(value: ByondValue) -> eyre::Result<ByondValue> {
|
||||
let path: String = value.get_string()?;
|
||||
Ok(serde_json::to_string(&match toml_encode_impl(&path) {
|
||||
Ok(value) => serde_json::json!({
|
||||
"success": true, "content": value
|
||||
}),
|
||||
Err(error) => serde_json::json!({
|
||||
"success": false, "content": error.to_string()
|
||||
}),
|
||||
})?
|
||||
.try_into()?)
|
||||
}
|
||||
|
||||
fn toml_encode_impl(value: &str) -> eyre::Result<String> {
|
||||
Ok(toml::to_string_pretty(
|
||||
&serde_json::from_str::<toml::Value>(value)?,
|
||||
)?)
|
||||
}
|
||||
Reference in New Issue
Block a user