Merge pull request #11 from Chaoscaot/add-invalid-nbt-arg

🔧 Add invalid_nbt flag.
Dieser Commit ist enthalten in:
Chaoscaot 2024-04-27 21:55:13 +02:00 committet von GitHub
Commit 33f5fe03fe
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
9 geänderte Dateien mit 366 neuen und 126 gelöschten Zeilen

1
Cargo.toml Normale Datei → Ausführbare Datei
Datei anzeigen

@ -7,6 +7,7 @@ members = [
"schemsearch-sql",
"schemsearch-java"
]
resolver = "2"
[profile.small]
inherits = "release"

2
schemsearch-cli/Cargo.toml Normale Datei → Ausführbare Datei
Datei anzeigen

@ -1,6 +1,6 @@
[package]
name = "schemsearch-cli"
version = "0.1.6"
version = "0.1.7"
edition = "2021"
license = "AGPL-3.0-or-later"

89
schemsearch-cli/src/main.rs Normale Datei → Ausführbare Datei
Datei anzeigen

@ -26,8 +26,8 @@ use clap::{command, Arg, ArgAction, ValueHint};
use std::path::PathBuf;
use std::str::FromStr;
use clap::error::ErrorKind;
use schemsearch_lib::{Match, search, SearchBehavior};
use crate::types::{PathSchematicSupplier, SchematicSupplierType};
use schemsearch_lib::{Match, SearchBehavior};
use crate::types::{PathSchematicSupplier, SchematicSupplier, SchematicSupplierType};
#[cfg(feature = "sql")]
use futures::executor::block_on;
use rayon::prelude::*;
@ -42,15 +42,17 @@ use indicatif::*;
use schemsearch_files::SpongeSchematic;
use crate::sinks::{OutputFormat, OutputSink};
use crate::stderr::MaschineStdErr;
use schemsearch_lib::nbt_search::has_invalid_nbt;
use schemsearch_lib::search::search;
fn main() {
#[allow(unused_mut)]
let mut cmd = command!("schemsearch")
let mut cmd = command!("schemsearch")
.arg(
Arg::new("pattern")
.help("The pattern to search for")
.required(true)
.value_hint(ValueHint::FilePath)
.required_unless_present("invalid-nbt")
.action(ArgAction::Set),
)
.arg(
@ -94,6 +96,13 @@ fn main() {
.long("air-as-any")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("invalid-nbt")
.help("Search for Schematics with Invalid or missing NBT data")
.short('I')
.long("invalid-nbt")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("output")
.help("The output format and path [Format:Path] available formats: text, json, csv; available paths: std, err, (file path)")
@ -134,7 +143,7 @@ fn main() {
)
.arg(
Arg::new("threads")
.help("The number of threads to use [0 = Available Threads]")
.help("The number of threads to use [0 = all Available Threads]")
.short('T')
.long("threads")
.action(ArgAction::Set)
@ -163,9 +172,9 @@ fn main() {
.bin_name("schemsearch");
#[cfg(feature = "sql")]
let mut cmd = cmd
let mut cmd = cmd
.arg(
Arg::new("sql")
Arg::new("sql")
.help("Use the SteamWar SQL Database")
.short('s')
.long("sql")
@ -204,18 +213,22 @@ fn main() {
air_as_any: matches.get_flag("air-as-any"),
ignore_entities: matches.get_flag("ignore-entities"),
threshold: *matches.get_one::<f32>("threshold").expect("Couldn't get threshold"),
invalid_nbt: matches.get_flag("invalid-nbt"),
};
let pattern = match SpongeSchematic::load(&PathBuf::from(matches.get_one::<String>("pattern").unwrap())) {
Ok(x) => x,
Err(e) => {
cmd.error(ErrorKind::Io, format!("Error while loading Pattern: {}", e.to_string())).exit();
}
let pattern = match matches.get_one::<String>("pattern") {
Some(p) => match SpongeSchematic::load(&PathBuf::from(p)) {
Ok(x) => Some(x),
Err(e) => {
cmd.error(ErrorKind::Io, format!("Error while loading Pattern: {}", e.to_string())).exit();
}
},
None => None,
};
let mut schematics: Vec<SchematicSupplierType> = Vec::new();
match matches.get_many::<String>("schematic") {
None => {},
None => {}
Some(x) => {
let paths = x.map(|x| PathBuf::from(x));
for path in paths {
@ -226,12 +239,12 @@ fn main() {
.filter(|x| x.path().is_file())
.filter(|x| x.path().extension().unwrap().to_str().unwrap() == "schem")
.for_each(|x| {
schematics.push(SchematicSupplierType::PATH(Box::new(PathSchematicSupplier {
schematics.push(SchematicSupplierType::PATH(PathSchematicSupplier {
path: x.path(),
})))
}))
});
} else if path.extension().unwrap().to_str().unwrap() == "schem" {
schematics.push(SchematicSupplierType::PATH(Box::new(PathSchematicSupplier { path })));
schematics.push(SchematicSupplierType::PATH(PathSchematicSupplier { path }));
}
}
}
@ -247,7 +260,7 @@ fn main() {
filter = filter.name(x.collect());
}
for schem in block_on(load_all_schematics(filter)) {
schematics.push(SchematicSupplierType::SQL(SqlSchematicSupplier{
schematics.push(SchematicSupplierType::SQL(SqlSchematicSupplier {
node: schem
}))
};
@ -282,28 +295,20 @@ fn main() {
Some(x) => x,
None => return SearchResult {
name: schem.get_name(),
matches: Vec::default()
matches: Vec::default(),
}
};
SearchResult {
name: schem.get_name(),
matches: search(schematic, &pattern, search_behavior)
}
search_in_schem(schematic, pattern.as_ref(), search_behavior, schem)
}
#[cfg(feature = "sql")]
SchematicSupplierType::SQL(schem) => {
match schem.get_schematic() {
Ok(schematic) => {
SearchResult {
name: schem.get_name(),
matches: search(schematic, &pattern, search_behavior)
}
}
Ok(schematic) => search_in_schem(schematic, pattern.as_ref(), search_behavior, schem),
Err(e) => {
eprintln!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
SearchResult {
name: schem.get_name(),
matches: Vec::default()
matches: Vec::default(),
}
}
}
@ -334,6 +339,32 @@ fn main() {
}
}
fn search_in_schem(schematic: SpongeSchematic, pattern: Option<&SpongeSchematic>, search_behavior: SearchBehavior, schem: &impl SchematicSupplier) -> SearchResult {
if search_behavior.invalid_nbt {
if has_invalid_nbt(schematic) {
SearchResult {
name: schem.get_name(),
matches: vec![Match {
x: 0,
y: 0,
z: 0,
percent: 1.0,
}],
}
} else {
SearchResult {
name: schem.get_name(),
matches: vec![],
}
}
} else {
SearchResult {
name: schem.get_name(),
matches: search(schematic, pattern.unwrap(), search_behavior),
}
}
}
fn load_schem(schem_path: &PathBuf) -> Option<SpongeSchematic> {
match SpongeSchematic::load(schem_path) {
Ok(x) => Some(x),

17
schemsearch-cli/src/types.rs Normale Datei → Ausführbare Datei
Datei anzeigen

@ -26,17 +26,21 @@ use schemsearch_files::SpongeSchematic;
use schemsearch_sql::{load_schemdata, SchematicNode};
pub enum SchematicSupplierType {
PATH(Box<PathSchematicSupplier>),
PATH(PathSchematicSupplier),
#[cfg(feature = "sql")]
SQL(SqlSchematicSupplier),
}
pub trait SchematicSupplier {
fn get_name(&self) -> String;
}
pub struct PathSchematicSupplier {
pub path: PathBuf,
}
impl PathSchematicSupplier {
pub fn get_name(&self) -> String {
impl SchematicSupplier for PathSchematicSupplier {
fn get_name(&self) -> String {
self.path.file_stem().unwrap().to_str().unwrap().to_string()
}
}
@ -52,8 +56,13 @@ impl SqlSchematicSupplier {
let mut schemdata = block_on(load_schemdata(self.node.id));
SpongeSchematic::load_data(&mut Cursor::new(schemdata.as_mut_slice()))
}
}
pub fn get_name(&self) -> String {
#[cfg(feature = "sql")]
impl SchematicSupplier for SqlSchematicSupplier {
fn get_name(&self) -> String {
format!("{} ({})", self.node.name, self.node.id)
}
}

3
schemsearch-lib/Cargo.toml Normale Datei → Ausführbare Datei
Datei anzeigen

@ -1,6 +1,6 @@
[package]
name = "schemsearch-lib"
version = "0.1.6"
version = "0.1.7"
edition = "2021"
license = "AGPL-3.0-or-later"
@ -11,3 +11,4 @@ serde = { version = "1.0.160", features = ["derive"] }
schemsearch-files = { path = "../schemsearch-files" }
named-binary-tag = "0.6"
libmath = "0.2.1"
lazy_static = "1.4.0"

163
schemsearch-lib/src/blocks.txt Ausführbare Datei
Datei anzeigen

@ -0,0 +1,163 @@
oak_sign
oak_wall_sign
oak_hanging_sign
oak_wall_hanging_sign
birch_sign
birch_wall_sign
birch_hanging_sign
birch_wall_hanging_sign
spruce_sign
spruce_wall_sign
spruce_hanging_sign
spruce_wall_hanging_sign
jungle_sign
jungle_wall_sign
jungle_hanging_sign
jungle_wall_hanging_sign
dark_oak_sign
dark_oak_wall_sign
dark_oak_hanging_sign
dark_oak_wall_hanging_sign
acacia_sign
acacia_wall_sign
acacia_hanging_sign
acacia_wall_hanging_sign
mangrove_sign
mangrove_wall_sign
mangrove_hanging_sign
mangrove_wall_hanging_sign
cherry_sign
cherry_wall_sign
cherry_hanging_sign
cherry_wall_hanging_sign
bamboo_sign
bamboo_wall_sign
bamboo_hanging_sign
bamboo_wall_hanging_sign
warped_sign
warped_wall_sign
warped_hanging_sign
warped_wall_hanging_sign
crimson_sign
crimson_wall_sign
crimson_hanging_sign
crimson_wall_hanging_sign
suspicious_gravel
suspicious_sand
white_banner
light_gray_banner
gray_banner
black_banner
brown_banner
red_banner
orange_banner
yellow_banner
lime_banner
green_banner
cyan_banner
light_blue_banner
blue_banner
purple_banner
magenta_banner
pink_banner
white_wall_banner
light_gray_wall_banner
gray_wall_banner
black_wall_banner
brown_wall_banner
red_wall_banner
orange_wall_banner
yellow_wall_banner
lime_wall_banner
green_wall_banner
cyan_wall_banner
light_blue_wall_banner
blue_wall_banner
purple_wall_banner
magenta_wall_banner
pink_wall_banner
white_bed
light_gray_bed
gray_bed
black_bed
brown_bed
red_bed
orange_bed
yellow_bed
lime_bed
green_bed
cyan_bed
light_blue_bed
blue_bed
purple_bed
magenta_bed
pink_bed
shulker_box
white_shulker_box
light_gray_shulker_box
gray_shulker_box
black_shulker_box
brown_shulker_box
red_shulker_box
orange_shulker_box
yellow_shulker_box
lime_shulker_box
green_shulker_box
cyan_shulker_box
light_blue_shulker_box
blue_shulker_box
purple_shulker_box
magenta_shulker_box
pink_shulker_box
furnace
blast_furnace
smoker
chest
trapped_chest
ender_chest
enchanting_table
barrel
lectern
jukebox
bell
brewing_stand
bee_nest
beehive
decorated_pot
beacon
conduit
campfire
soul_campfire
redstone_comparator
hopper
dispenser
dropper
moving_piston
daylight_detector
sculk_sensor
calibrated_sculk_sensor
sculk_catalyst
sculk_shrieker
player_head
player_wall_head
wither_skeleton_skull
wither_skeleton_wall_skull
zombie_head
zombie_wall_head
skeleton_skull
skeleton_wall_skull
creeper_head
creeper_wall_head
piglin_head
piglin_wall_head
dragon_head
dragon_wall_head
chiseled_bookshelf
command_block
chain_command_block
repeating_command_block
structure_block
jigsaw_block
end_portal
end_gateway
monster_spawner

103
schemsearch-lib/src/lib.rs Normale Datei → Ausführbare Datei
Datei anzeigen

@ -16,12 +16,10 @@
*/
pub mod pattern_mapper;
pub mod search;
pub mod nbt_search;
use serde::{Serialize, Deserialize};
use pattern_mapper::match_palette;
use schemsearch_files::SpongeSchematic;
use crate::pattern_mapper::match_palette_adapt;
use math::round::ceil;
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct SearchBehavior {
@ -31,89 +29,7 @@ pub struct SearchBehavior {
pub air_as_any: bool,
pub ignore_entities: bool,
pub threshold: f32,
}
pub fn search(
schem: SpongeSchematic,
pattern_schem: &SpongeSchematic,
search_behavior: SearchBehavior,
) -> Vec<Match> {
if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
return Vec::new();
}
if pattern_schem.palette.len() > schem.palette.len() {
return Vec::new();
}
let pattern_schem = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data);
let mut matches: Vec<Match> = Vec::with_capacity(4);
let pattern_data = pattern_schem.block_data.as_ptr();
let schem_data = if search_behavior.ignore_block_data {
match_palette_adapt(&schem, &pattern_schem.palette, search_behavior.ignore_block_data)
} else {
schem.block_data
};
let schem_data = schem_data.as_ptr();
let air_id = if search_behavior.ignore_air || search_behavior.air_as_any { pattern_schem.palette.get("minecraft:air").unwrap_or(&-1) } else { &-1};
let pattern_blocks = pattern_schem.block_data.len() as f32;
let i_pattern_blocks = pattern_blocks as i32;
let pattern_width = pattern_schem.width as usize;
let pattern_height = pattern_schem.height as usize;
let pattern_length = pattern_schem.length as usize;
let schem_width = schem.width as usize;
let schem_height = schem.height as usize;
let schem_length = schem.length as usize;
let skip_amount = ceil((pattern_blocks * (1.0 - search_behavior.threshold)) as f64, 0) as i32;
for y in 0..=schem_height - pattern_height {
for z in 0..=schem_length - pattern_length {
for x in 0..=schem_width - pattern_width {
let mut not_matching = 0;
'outer:
for j in 0..pattern_height {
for k in 0..pattern_length {
'inner:
for i in 0..pattern_width {
let index = (x + i) + schem_width * ((z + k) + (y + j) * schem_length);
let pattern_index = i + pattern_width * (k + j * pattern_length);
let data = unsafe { *schem_data.add(index) };
let pattern_data = unsafe { *pattern_data.add(pattern_index) };
if (search_behavior.ignore_air && data != *air_id) || (search_behavior.air_as_any && pattern_data != *air_id) {
continue 'inner;
}
if data != pattern_data {
not_matching += 1;
if not_matching >= skip_amount {
break 'outer;
}
}
}
}
}
if not_matching < skip_amount {
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent: (i_pattern_blocks - not_matching) as f32 / pattern_blocks,
});
}
}
}
}
return matches;
pub invalid_nbt: bool,
}
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)]
@ -137,7 +53,9 @@ pub fn normalize_data(data: &str, ignore_data: bool) -> &str {
#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};
use crate::pattern_mapper::strip_data;
use schemsearch_files::SpongeSchematic;
use crate::pattern_mapper::{match_palette, strip_data};
use crate::search::search;
use super::*;
#[test]
@ -191,7 +109,8 @@ mod tests {
ignore_entities: true,
ignore_air: false,
air_as_any: false,
threshold: 0.9
threshold: 0.9,
invalid_nbt: false
});
}
@ -206,7 +125,8 @@ mod tests {
ignore_entities: true,
ignore_air: false,
air_as_any: false,
threshold: 0.9
threshold: 0.9,
invalid_nbt: false
});
assert_eq!(matches.len(), 1);
@ -227,7 +147,8 @@ mod tests {
ignore_entities: false,
ignore_air: false,
air_as_any: false,
threshold: 0.9
threshold: 0.9,
invalid_nbt: false
});
assert_eq!(matches.len(), 1);

27
schemsearch-lib/src/nbt_search.rs Ausführbare Datei
Datei anzeigen

@ -0,0 +1,27 @@
use std::borrow::ToOwned;
use std::collections::HashSet;
use std::iter::Iterator;
use lazy_static::lazy_static;
use schemsearch_files::SpongeSchematic;
const NBT_BLOCKS: &str = include_str!("blocks.txt");
lazy_static! {
static ref NBT_BLOCKS_SET: HashSet<String> = NBT_BLOCKS.lines().map(ToOwned::to_owned).collect();
}
pub fn has_invalid_nbt(schem: SpongeSchematic) -> bool {
if schem.block_entities.is_empty() && schem.palette.keys().any(|v| NBT_BLOCKS_SET.contains(v)) {
return true;
}
let nbt_blocks = schem.palette.iter().filter(|(k, _)| NBT_BLOCKS_SET.contains(*k)).map(|(_, v)| *v).collect::<HashSet<i32>>();
for block_entity in schem.block_data.iter() {
if nbt_blocks.contains(block_entity) {
return true;
}
}
return false;
}

87
schemsearch-lib/src/search.rs Ausführbare Datei
Datei anzeigen

@ -0,0 +1,87 @@
use math::round::ceil;
use schemsearch_files::SpongeSchematic;
use crate::{Match, SearchBehavior};
use crate::pattern_mapper::{match_palette, match_palette_adapt};
pub fn search(
schem: SpongeSchematic,
pattern_schem: &SpongeSchematic,
search_behavior: SearchBehavior,
) -> Vec<Match> {
if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
return Vec::new();
}
if pattern_schem.palette.len() > schem.palette.len() {
return Vec::new();
}
let pattern_schem = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data);
let mut matches: Vec<Match> = Vec::with_capacity(4);
let pattern_data = pattern_schem.block_data.as_ptr();
let schem_data = if search_behavior.ignore_block_data {
match_palette_adapt(&schem, &pattern_schem.palette, search_behavior.ignore_block_data)
} else {
schem.block_data
};
let schem_data = schem_data.as_ptr();
let air_id = if search_behavior.ignore_air || search_behavior.air_as_any { pattern_schem.palette.get("minecraft:air").unwrap_or(&-1) } else { &-1};
let pattern_blocks = pattern_schem.block_data.len() as f32;
let i_pattern_blocks = pattern_blocks as i32;
let pattern_width = pattern_schem.width as usize;
let pattern_height = pattern_schem.height as usize;
let pattern_length = pattern_schem.length as usize;
let schem_width = schem.width as usize;
let schem_height = schem.height as usize;
let schem_length = schem.length as usize;
let skip_amount = ceil((pattern_blocks * (1.0 - search_behavior.threshold)) as f64, 0) as i32;
for y in 0..=schem_height - pattern_height {
for z in 0..=schem_length - pattern_length {
for x in 0..=schem_width - pattern_width {
let mut not_matching = 0;
'outer:
for j in 0..pattern_height {
for k in 0..pattern_length {
'inner:
for i in 0..pattern_width {
let index = (x + i) + schem_width * ((z + k) + (y + j) * schem_length);
let pattern_index = i + pattern_width * (k + j * pattern_length);
let data = unsafe { *schem_data.add(index) };
let pattern_data = unsafe { *pattern_data.add(pattern_index) };
if (search_behavior.ignore_air && data != *air_id) || (search_behavior.air_as_any && pattern_data != *air_id) {
continue 'inner;
}
if data != pattern_data {
not_matching += 1;
if not_matching >= skip_amount {
break 'outer;
}
}
}
}
}
if not_matching < skip_amount {
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent: (i_pattern_blocks - not_matching) as f32 / pattern_blocks,
});
}
}
}
}
return matches;
}