5 Commits

Author SHA1 Message Date
1a14d13303 build: Add SIMD optimizations to compare.c
Add SIMD (Single Instruction, Multiple Data) optimizations to the
compare.c file to improve performance. This includes vectorization
flags and architecture-specific optimizations to enhance the
processing of data in the comparison function. Additionally, the
implementation now dynamically selects an optimized code path based
on the size of the pattern data, ensuring efficient execution for
various scenarios.

By Lixfel
2024-04-27 20:07:46 +02:00
9c8551dc5a Move to i32 2023-12-27 20:49:21 +01:00
abb2084029 Move to i32 2023-12-27 20:44:37 +01:00
3eba89b119 Move to i32 2023-12-27 20:43:50 +01:00
511ce04f0b new Matcher 2023-12-26 21:28:46 +01:00
17 changed files with 324 additions and 453 deletions

2
.gitignore vendored Normal file → Executable file
View File

@@ -3,3 +3,5 @@
/.idea/ /.idea/
/SchemSearch.class /SchemSearch.class
/SchemSearch.h /SchemSearch.h
/schemsearch-lib/src/.idea/
/schemsearch-lib/src/cmake-build-debug/

4
Cargo.toml Executable file → Normal file
View File

@@ -7,7 +7,6 @@ members = [
"schemsearch-sql", "schemsearch-sql",
"schemsearch-java" "schemsearch-java"
] ]
resolver = "2"
[profile.small] [profile.small]
inherits = "release" inherits = "release"
@@ -17,4 +16,5 @@ opt-level = "z"
codegen-units = 1 codegen-units = 1
[profile.release] [profile.release]
lto = true lto = true
opt-level = 3

View File

@@ -1,10 +1,10 @@
default: default:
@echo "Building (Release)..."; @echo "Building (Release)...";
cargo rustc --release --color=always -p schemsearch-cli -- -C target-feature=+avx2 cargo rustc --release --color=always -p schemsearch-cli -- -C target-cpu=native
sql: sql:
@echo "Building (Release)..."; @echo "Building (Release)...";
cargo rustc --release --color=always -p schemsearch-cli --features sql -- -C target-feature=+avx2 cargo rustc --release --color=always -p schemsearch-cli --features sql -- -C target-cpu=native
debug: debug:
@echo "Building (Debug)..."; @echo "Building (Debug)...";

2
schemsearch-cli/Cargo.toml Executable file → Normal file
View File

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

89
schemsearch-cli/src/main.rs Executable file → Normal file
View File

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

2
schemsearch-cli/src/sinks.rs Executable file → Normal file
View File

@@ -71,7 +71,7 @@ impl OutputFormat {
pub fn start(&self, total: u32, search_behavior: &SearchBehavior, start_time: u128) -> String { pub fn start(&self, total: u32, search_behavior: &SearchBehavior, start_time: u128) -> String {
match self { match self {
OutputFormat::Text => format!("Starting search in {} schematics\n", total), OutputFormat::Text => format!("Starting search in {} schematics\n", total),
OutputFormat::CSV => "Name,X,Y,Z,Percent\n".to_owned(), OutputFormat::CSV => format!("Name,X,Y,Z,Percent\n"),
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Init(InitEvent { OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Init(InitEvent {
total, total,
search_behavior: search_behavior.clone(), search_behavior: search_behavior.clone(),

17
schemsearch-cli/src/types.rs Executable file → Normal file
View File

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

6
schemsearch-lib/Cargo.toml Executable file → Normal file
View File

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

11
schemsearch-lib/build.rs Executable file
View File

@@ -0,0 +1,11 @@
use cc;
fn main() {
cc::Build::new()
.file("src/compare.c")
.flag("-ftree-vectorize")
.flag("-march=native")
.flag("-mtune=native")
.flag("-ffast-math")
.compile("compare");
}

View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.27)
project(src C)
set(CMAKE_C_STANDARD 11)
add_executable(src
compare.c)

View File

@@ -1,163 +0,0 @@
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

110
schemsearch-lib/src/compare.c Executable file
View File

@@ -0,0 +1,110 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
int32_t isMatching(
const int32_t *schem_data,
const int32_t *pattern_data,
size_t pattern_data_length,
int32_t x,
int32_t y,
int32_t z,
int32_t schem_width,
int32_t schem_length,
int32_t pattern_width,
int32_t pattern_height,
int32_t pattern_length,
int32_t *w_ptr
) {
for (int j = 0; j < pattern_height; ++j) {
for (int k = 0; k < pattern_length; ++k) {
int pattern_index_pre = k * pattern_width + j * pattern_width * pattern_length;
int schem_index_pre = x + (k + z) * schem_width + (j + y) * schem_width * schem_length;
for (int i = 0; i < pattern_width; ++i) {
int pattern_index = i + pattern_index_pre;
int schem_index = i + schem_index_pre;
w_ptr[pattern_index] = schem_data[schem_index];
}
}
}
int32_t matching = 0;
for (int i = 0; i < pattern_data_length; ++i) {
matching += w_ptr[i] == pattern_data[i];
}
return matching;
}
void is_matching_all(
const int32_t *__restrict__ schem_data,
const int32_t *__restrict__ pattern_data,
int32_t schem_width,
int32_t schem_height,
int32_t schem_length,
int32_t pattern_width,
int32_t pattern_height,
int32_t pattern_length,
int32_t *__restrict__ result
) {
if(pattern_width*pattern_height*pattern_length >= 65536) { //TODO check for table size < 65536
for (int32_t pz = 0; pz < pattern_length; ++pz) {
int32_t maxZ = schem_length - pattern_length + pz + 1;
for (int32_t py = 0; py < pattern_height; ++py) {
int32_t maxY = schem_height - pattern_height + py + 1;
for (int32_t px = 0; px < pattern_width; ++px) {
int32_t pv = pattern_data[px + py * pattern_width + pz * pattern_width * pattern_height];
int32_t maxX = schem_width - pattern_width + px + 1;
for (int32_t z = pz; z < maxZ; ++z) {
int32_t sourceOffsetZ = z * schem_width * schem_height;
int32_t resultOffsetZ = (z - pz) * schem_width * schem_height - py * schem_width;
for (int32_t y = py; y < maxY; ++y) {
int32_t sourceOffsetY = sourceOffsetZ + y * schem_width;
int32_t resultOffsetY = resultOffsetZ + y * schem_width - px;
for (int32_t x = px; x < maxX; ++x) {
result[resultOffsetY + x] += schem_data[sourceOffsetY + x] == pv;
}
}
}
}
}
}
} else {
size_t schem_size = schem_width*schem_height*schem_length;
uint16_t *__restrict__ sschem_data = (uint16_t*)malloc(schem_size*2);
uint16_t *__restrict__ sresult = (uint16_t*)malloc(schem_size*2);
for(size_t i = 0; i < schem_size; i++) {
sschem_data[i] = schem_data[i];
sresult[i] = 0;
}
for (int32_t pz = 0; pz < pattern_length; ++pz) {
int32_t maxZ = schem_length - pattern_length + pz + 1;
for (int32_t py = 0; py < pattern_height; ++py) {
int32_t maxY = schem_height - pattern_height + py + 1;
for (int32_t px = 0; px < pattern_width; ++px) {
uint16_t pv = (uint16_t)pattern_data[px + py * pattern_width + pz * pattern_width * pattern_height];
int32_t maxX = schem_width - pattern_width + px + 1;
for (int32_t z = pz; z < maxZ; ++z) {
int32_t sourceOffsetZ = z * schem_width * schem_height;
int32_t resultOffsetZ = (z - pz) * schem_width * schem_height - py * schem_width;
for (int32_t y = py; y < maxY; ++y) {
int32_t sourceOffsetY = sourceOffsetZ + y * schem_width;
int32_t resultOffsetY = resultOffsetZ + y * schem_width - px;
for (int32_t x = px; x < maxX; ++x) {
sresult[resultOffsetY + x] += sschem_data[sourceOffsetY + x] == pv;
}
}
}
}
}
}
for(size_t i = 0; i < schem_size; i++) {
result[i] = sresult[i];
}
free(sschem_data);
free(sresult);
}
}

163
schemsearch-lib/src/lib.rs Executable file → Normal file
View File

@@ -16,10 +16,12 @@
*/ */
pub mod pattern_mapper; pub mod pattern_mapper;
pub mod search;
pub mod nbt_search;
use serde::{Serialize, Deserialize}; 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)] #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct SearchBehavior { pub struct SearchBehavior {
@@ -29,7 +31,149 @@ pub struct SearchBehavior {
pub air_as_any: bool, pub air_as_any: bool,
pub ignore_entities: bool, pub ignore_entities: bool,
pub threshold: f32, pub threshold: f32,
pub invalid_nbt: bool, }
extern "C" {
pub fn isMatching(
schem_data: *const i32,
pattern_data: *const i32,
pattern_data_length: usize,
x: usize,
y: usize,
z: usize,
schem_width: usize,
schem_length: usize,
pattern_width: usize,
pattern_height: usize,
pattern_length: usize,
w_ptr: *mut i32,
) -> i32;
pub fn is_matching_all(
schem_data: *const i32,
pattern_data: *const i32,
schem_width: i32,
schem_height: i32,
schem_length: i32,
pattern_width: i32,
pattern_height: i32,
pattern_length: i32,
result: *mut i32
);
}
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_usize = pattern_schem.block_data.len();
let pattern_blocks = pattern_blocks_usize 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 matching_count;
unsafe {
matching_count = isMatching(
schem_data,
pattern_data,
pattern_blocks_usize,
x,
y,
z,
schem_width,
schem_length,
pattern_width,
pattern_height,
pattern_length,
w_ptr,
);
};
if matching_count >= i_pattern_blocks - skip_amount {
let percent = matching_count as f32 / pattern_blocks;
if percent >= search_behavior.threshold {
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent,
});
}
}
}
}
}*/
let mut result = Vec::<i32>::with_capacity(schem_width * schem_height * schem_length);
result.resize(schem_width * schem_height * schem_length, 0);
unsafe {
is_matching_all(
schem_data,
pattern_data,
schem_width as i32,
schem_height as i32,
schem_length as i32,
pattern_width as i32,
pattern_height as i32,
pattern_length as i32,
result.as_mut_ptr()
);
}
result.into_iter().enumerate().filter(|(_, matching_count)| *matching_count >= i_pattern_blocks - skip_amount).for_each(|(i, matching_count)| {
let percent = matching_count as f32 / pattern_blocks;
let x = i % schem_width;
let y = (i / schem_width) % schem_height;
let z = i / (schem_width * schem_height);
matches.push(Match {
x: x as u16,
y: y as u16,
z: z as u16,
percent,
});
});
return matches;
} }
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)] #[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)]
@@ -53,9 +197,7 @@ pub fn normalize_data(data: &str, ignore_data: bool) -> &str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use schemsearch_files::SpongeSchematic; use crate::pattern_mapper::strip_data;
use crate::pattern_mapper::{match_palette, strip_data};
use crate::search::search;
use super::*; use super::*;
#[test] #[test]
@@ -109,8 +251,7 @@ mod tests {
ignore_entities: true, ignore_entities: true,
ignore_air: false, ignore_air: false,
air_as_any: false, air_as_any: false,
threshold: 0.9, threshold: 0.9
invalid_nbt: false
}); });
} }
@@ -125,8 +266,7 @@ mod tests {
ignore_entities: true, ignore_entities: true,
ignore_air: false, ignore_air: false,
air_as_any: false, air_as_any: false,
threshold: 0.9, threshold: 0.9
invalid_nbt: false
}); });
assert_eq!(matches.len(), 1); assert_eq!(matches.len(), 1);
@@ -147,8 +287,7 @@ mod tests {
ignore_entities: false, ignore_entities: false,
ignore_air: false, ignore_air: false,
air_as_any: false, air_as_any: false,
threshold: 0.9, threshold: 0.9
invalid_nbt: false
}); });
assert_eq!(matches.len(), 1); assert_eq!(matches.len(), 1);

View File

@@ -1,110 +0,0 @@
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(|x| format!("minecraft:{}", x)).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.to_owned())).map(|(_, v)| *v).collect::<HashSet<i32>>();
for (i, block_entity) in schem.block_data.iter().enumerate() {
if nbt_blocks.contains(&*block_entity) {
// i = x + z * Width + y * Width * Length
let x = i % schem.width as usize;
let z = (i / schem.width as usize) % schem.length as usize;
let y = i / (schem.width as usize * schem.length as usize);
if schem.block_entities.iter().any(|e| !e.pos.eq(&[x as i32, y as i32, z as i32])) {
return true;
}
}
}
return false;
}
#[allow(unused_imports)]
#[cfg(test)]
mod tests {
use nbt::CompoundTag;
use schemsearch_files::{BlockEntity, SpongeSchematic};
use super::*;
#[test]
fn test_has_invalid_nbt() {
let schem = SpongeSchematic {
data_version: 1,
metadata: CompoundTag::new(),
width: 0,
height: 0,
length: 0,
offset: [0, 0, 0],
palette_max: 1,
palette: vec![("minecraft:chest".to_owned(), 1)].into_iter().collect(),
block_data: vec![1],
block_entities: vec![],
entities: None,
};
assert_eq!(has_invalid_nbt(schem), true);
}
#[test]
fn test_has_invalid_nbt_2() {
let schem = SpongeSchematic {
data_version: 1,
metadata: CompoundTag::new(),
width: 1,
height: 1,
length: 1,
offset: [0, 0, 0],
palette_max: 1,
palette: vec![("minecraft:chest".to_owned(), 1)].into_iter().collect(),
block_data: vec![1],
block_entities: vec![
BlockEntity {
id: "minecraft:chest".to_owned(),
pos: [0, 0, 0],
}
],
entities: None,
};
assert_eq!(has_invalid_nbt(schem), false);
}
#[test]
fn test_has_invalid_nbt_3() {
let schem = SpongeSchematic {
data_version: 1,
metadata: CompoundTag::new(),
width: 2,
height: 1,
length: 1,
offset: [0, 0, 0],
palette_max: 1,
palette: vec![("minecraft:chest".to_owned(), 1), ("minecraft:stone".to_owned(), 2)].into_iter().collect(),
block_data: vec![1, 2],
block_entities: vec![
BlockEntity {
id: "minecraft:chest".to_owned(),
pos: [1, 0, 0],
}
],
entities: None,
};
assert_eq!(has_invalid_nbt(schem), true);
}
}

View File

@@ -1,87 +0,0 @@
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;
}

BIN
tests/Pattern.nbt Normal file

Binary file not shown.

BIN
tests/Random.nbt Normal file

Binary file not shown.