mirror of
https://github.com/Chaoscaot/schemsearch.git
synced 2025-11-05 14:04:00 +01:00
Compare commits
10 Commits
v0.1.0
...
chaoscaot/
| Author | SHA1 | Date | |
|---|---|---|---|
| d48910ecb4 | |||
| b8d912881d | |||
| 02404792a5 | |||
| 59272ed3e7 | |||
| 322ba65656 | |||
| b082d6cd8d | |||
| f4bcde73f9 | |||
| 0e31714582 | |||
| 229c858d9a | |||
| abf6953172 |
27
.github/workflows/master-build.yml
vendored
27
.github/workflows/master-build.yml
vendored
@ -16,18 +16,37 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache Cargo modules
|
||||
id: cache-cargo
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cargo-target-debug
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
build-realease:
|
||||
run: cargo test --verbose -p schemsearch-lib
|
||||
|
||||
build-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache Cargo modules
|
||||
id: cache-cargo
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cargo-target-release
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose --release
|
||||
run: cargo build --verbose --release -p schemsearch-cli
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
|
||||
12
.github/workflows/release-build.yml
vendored
12
.github/workflows/release-build.yml
vendored
@ -19,8 +19,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache Cargo modules
|
||||
id: cache-cargo
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cargo-target-release
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose --release
|
||||
run: cargo build --verbose --release -p schemsearch-cli
|
||||
- name: Create Tarball
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: tar -czvf schemsearch-cli-${{ matrix.os }}.tar.gz -C target/release schemsearch-cli
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
members = [
|
||||
"schemsearch-cli",
|
||||
"schemsearch-lib",
|
||||
"schemsearch_faster",
|
||||
"schemsearch-faster",
|
||||
"schemsearch-files",
|
||||
"schemsearch-sql",
|
||||
"schemsearch-java"
|
||||
|
||||
10
README.md
10
README.md
@ -40,6 +40,16 @@ schemsearch-cli --help
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Tile entities search
|
||||
- [ ] Entities search
|
||||
- [ ] Better error handling
|
||||
- [ ] Web interface
|
||||
- [ ] McEdit search
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
This project is build using Rust for the CLI and library. It can be built using Cargo.
|
||||
```bash
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch-cli"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
@ -16,6 +16,8 @@ futures = { version = "0.3", optional = true }
|
||||
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" , "mysql" ], optional = true }
|
||||
rayon = "1.7.0"
|
||||
indicatif = { version = "0.17.3", features = ["rayon"] }
|
||||
serde = "1.0.157"
|
||||
serde_json = "1.0.94"
|
||||
|
||||
[features]
|
||||
sql = ["dep:schemsearch-sql", "dep:futures", "dep:sqlx"]
|
||||
|
||||
31
schemsearch-cli/src/json_output.rs
Normal file
31
schemsearch-cli/src/json_output.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemsearch_lib::SearchBehavior;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(tag = "event")]
|
||||
pub enum JsonEvent {
|
||||
Found(FoundEvent),
|
||||
Init(InitEvent),
|
||||
End(EndEvent),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FoundEvent {
|
||||
pub name: String,
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub z: u16,
|
||||
pub percent: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct InitEvent {
|
||||
pub total: u32,
|
||||
pub search_behavior: SearchBehavior,
|
||||
pub start_time: u128,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct EndEvent {
|
||||
pub end_time: u128,
|
||||
}
|
||||
@ -16,14 +16,14 @@
|
||||
*/
|
||||
|
||||
mod types;
|
||||
mod json_output;
|
||||
mod sinks;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::io::Write;
|
||||
use clap::{command, Arg, ArgAction, ValueHint};
|
||||
use schemsearch_files::Schematic;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use clap::error::ErrorKind;
|
||||
use schemsearch_lib::{search, SearchBehavior};
|
||||
use crate::types::{PathSchematicSupplier, SchematicSupplierType};
|
||||
@ -37,7 +37,9 @@ use schemsearch_sql::filter::SchematicFilter;
|
||||
use schemsearch_sql::load_all_schematics;
|
||||
#[cfg(feature = "sql")]
|
||||
use crate::types::SqlSchematicSupplier;
|
||||
use indicatif::{ProgressBar, ParallelProgressIterator, ProgressStyle};
|
||||
use indicatif::{ParallelProgressIterator, ProgressStyle};
|
||||
use schemsearch_files::Schematic;
|
||||
use crate::sinks::{OutputFormat, OutputSink};
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
@ -63,10 +65,17 @@ fn main() {
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ignore-block-entities")
|
||||
.help("Ignores block entities when searching [Not Implemented]")
|
||||
Arg::new("ignore-tile-entities")
|
||||
.help("Ignores tile entities when searching [Not Implemented]")
|
||||
.short('b')
|
||||
.long("ignore-block-entities")
|
||||
.long("ignore-tile-entities")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ignore-tile-entity-data")
|
||||
.help("Ignores tile entity data when searching [Not Implemented]")
|
||||
.short('B')
|
||||
.long("ignore-tile-entity-data")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
@ -92,20 +101,32 @@ fn main() {
|
||||
)
|
||||
.arg(
|
||||
Arg::new("output")
|
||||
.help("The output format")
|
||||
.help("The output format and path [Format:Path] available formats: text, json, csv; available paths: std, (file path)")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.action(ArgAction::Append)
|
||||
.default_value("std")
|
||||
.value_parser(["std_csv", "file_csv", "std", "file"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("output-file")
|
||||
.help("The output file")
|
||||
.short('O')
|
||||
.long("output-file")
|
||||
.value_hint(ValueHint::FilePath)
|
||||
.action(ArgAction::Append)
|
||||
.default_value("text:std")
|
||||
.value_parser(|s: &str| {
|
||||
let mut split = s.splitn(2, ':');
|
||||
let format = match split.next() {
|
||||
None => return Err("No format specified".to_string()),
|
||||
Some(x) => x
|
||||
};
|
||||
let path = match split.next() {
|
||||
None => return Err("No path specified".to_string()),
|
||||
Some(x) => x
|
||||
};
|
||||
let format = match OutputFormat::from_str(format) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
let path = match OutputSink::from_str(path) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
|
||||
Ok((format, path))
|
||||
}),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("threshold")
|
||||
@ -165,7 +186,8 @@ fn main() {
|
||||
|
||||
let search_behavior = SearchBehavior {
|
||||
ignore_block_data: matches.get_flag("ignore-data"),
|
||||
ignore_block_entities: matches.get_flag("ignore-block-entities"),
|
||||
ignore_tile_entities: matches.get_flag("ignore-tile-entities"),
|
||||
ignore_tile_entity_data: matches.get_flag("ignore-tile-entity-data"),
|
||||
ignore_air: matches.get_flag("ignore-air"),
|
||||
air_as_any: matches.get_flag("air-as-any"),
|
||||
ignore_entities: matches.get_flag("ignore-entities"),
|
||||
@ -223,52 +245,26 @@ fn main() {
|
||||
cmd.error(ErrorKind::MissingRequiredArgument, "No schematics specified").exit();
|
||||
}
|
||||
|
||||
let mut output_std = false;
|
||||
let mut output_std_csv = false;
|
||||
let mut output_file_csv = false;
|
||||
let mut output_file = false;
|
||||
let output: Vec<&(OutputFormat, OutputSink)> = matches.get_many::<(OutputFormat, OutputSink)>("output").expect("Error").collect();
|
||||
let mut output: Vec<(OutputFormat, Box<dyn Write>)> = output.into_iter().map(|x| (x.0.clone(), x.1.output())).collect();
|
||||
|
||||
for x in matches.get_many::<String>("output").expect("Couldn't get output") {
|
||||
match x.as_str() {
|
||||
"std" => output_std = true,
|
||||
"std_csv" => output_std_csv = true,
|
||||
"file_csv" => output_file_csv = true,
|
||||
"file" => output_file = true,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
let file: Option<File>;
|
||||
let mut file_out: Option<BufWriter<File>> = None;
|
||||
|
||||
if output_file || output_file_csv {
|
||||
let output_file_path = match matches.get_one::<String>("output-file") {
|
||||
None => {
|
||||
cmd.error(ErrorKind::MissingRequiredArgument, "No output file specified").exit();
|
||||
}
|
||||
Some(x) => x
|
||||
};
|
||||
|
||||
file = match File::create(output_file_path) {
|
||||
Ok(x) => Some(x),
|
||||
Err(e) => {
|
||||
cmd.error(ErrorKind::Io, format!("Error while creating output file: {}", e.to_string())).exit();
|
||||
}
|
||||
};
|
||||
file_out = Some(BufWriter::new(file.unwrap()));
|
||||
for x in &mut output {
|
||||
write!(x.1, "{}", x.0.start(schematics.len() as u32, &search_behavior, start.elapsed().as_millis())).unwrap();
|
||||
}
|
||||
|
||||
ThreadPoolBuilder::new().num_threads(*matches.get_one::<usize>("threads").expect("Could not get threads")).build_global().unwrap();
|
||||
|
||||
let matches: Vec<Result> = schematics.par_iter().progress_with_style(ProgressStyle::with_template("[{elapsed}, ETA: {eta}] {wide_bar} {pos}/{len} {per_sec}").unwrap()).map(|schem| {
|
||||
let matches: Vec<SearchResult> = schematics.par_iter().progress_with_style(ProgressStyle::with_template("[{elapsed}, ETA: {eta}] {wide_bar} {pos}/{len} {per_sec}").unwrap()).map(|schem| {
|
||||
match schem {
|
||||
SchematicSupplierType::PATH(schem) => {
|
||||
let schematic = match load_schem(&schem.path) {
|
||||
Some(x) => x,
|
||||
None => return Result {
|
||||
None => return SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: vec![]
|
||||
}
|
||||
};
|
||||
Result {
|
||||
SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: search(schematic, &pattern, search_behavior)
|
||||
}
|
||||
@ -277,16 +273,14 @@ fn main() {
|
||||
SchematicSupplierType::SQL(schem) => {
|
||||
match schem.get_schematic() {
|
||||
Ok(schematic) => {
|
||||
Result {
|
||||
SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: search(schematic, &pattern, search_behavior)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if !output_std && !output_std_csv {
|
||||
println!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
|
||||
}
|
||||
Result {
|
||||
println!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
|
||||
SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: vec![]
|
||||
}
|
||||
@ -296,30 +290,21 @@ fn main() {
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut lock = stdout.lock();
|
||||
|
||||
for matching in matches {
|
||||
let schem_name = matching.name;
|
||||
let matching = matching.matches;
|
||||
for x in matching {
|
||||
if output_std {
|
||||
writeln!(lock, "Found match in '{}' at x: {}, y: {}, z: {}, % = {}", schem_name, x.0, x.1, x.2, x.3).unwrap();
|
||||
}
|
||||
if output_std_csv {
|
||||
writeln!(lock, "{},{},{},{},{}", schem_name, x.0, x.1, x.2, x.3).unwrap();
|
||||
}
|
||||
if output_file {
|
||||
writeln!(file_out.as_mut().unwrap(), "Found match in '{}' at x: {}, y: {}, z: {}, % = {}", schem_name, x.0, x.1, x.2, x.3).unwrap();
|
||||
}
|
||||
if output_file_csv {
|
||||
writeln!(file_out.as_mut().unwrap(), "{},{},{},{},{}", schem_name, x.0, x.1, x.2, x.3).unwrap();
|
||||
for out in &mut output {
|
||||
write!(out.1, "{}", out.0.found_match(&schem_name, x)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let end = std::time::Instant::now();
|
||||
println!("Finished in {:.2}s! Searched in {} Schematics", end.duration_since(start).as_secs_f32(), schematics.len());
|
||||
for x in &mut output {
|
||||
write!(x.1, "{}", x.0.end(end.duration_since(start).as_millis())).unwrap();
|
||||
x.1.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn load_schem(schem_path: &PathBuf) -> Option<Schematic> {
|
||||
@ -333,7 +318,7 @@ fn load_schem(schem_path: &PathBuf) -> Option<Schematic> {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Result {
|
||||
struct SearchResult {
|
||||
name: String,
|
||||
matches: Vec<(u16, u16, u16, f32)>,
|
||||
}
|
||||
|
||||
88
schemsearch-cli/src/sinks.rs
Normal file
88
schemsearch-cli/src/sinks.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::str::FromStr;
|
||||
use std::io::Write;
|
||||
use schemsearch_lib::SearchBehavior;
|
||||
use crate::json_output::{EndEvent, FoundEvent, InitEvent, JsonEvent};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputSink {
|
||||
Stdout,
|
||||
File(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputFormat {
|
||||
Text,
|
||||
CSV,
|
||||
JSON
|
||||
}
|
||||
|
||||
impl FromStr for OutputFormat {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"text" => Ok(OutputFormat::Text),
|
||||
"csv" => Ok(OutputFormat::CSV),
|
||||
"json" => Ok(OutputFormat::JSON),
|
||||
_ => Err(format!("'{}' is not a valid output format", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for OutputSink {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"std" => Ok(OutputSink::Stdout),
|
||||
_ => Ok(OutputSink::File(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputSink {
|
||||
pub fn output(&self) -> Box<dyn Write> {
|
||||
match self {
|
||||
OutputSink::Stdout => Box::new(std::io::stdout().lock()),
|
||||
OutputSink::File(path) => Box::new(BufWriter::new(File::create(path).unwrap()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputFormat {
|
||||
pub fn found_match(&self, name: &String, pos: (u16, u16, u16, f32)) -> String {
|
||||
match self {
|
||||
OutputFormat::Text => format!("Found match in '{}' at x: {}, y: {}, z: {}, % = {}\n", name, pos.0, pos.1, pos.2, pos.3),
|
||||
OutputFormat::CSV => format!("{},{},{},{},{}\n", name, pos.0, pos.1, pos.2, pos.3),
|
||||
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Found(FoundEvent {
|
||||
name: name.clone(),
|
||||
x: pos.0,
|
||||
y: pos.1,
|
||||
z: pos.2,
|
||||
percent: pos.3,
|
||||
})).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self, total: u32, search_behavior: &SearchBehavior, start_time: u128) -> String {
|
||||
match self {
|
||||
OutputFormat::Text => format!("Starting search in {} schematics\n", total),
|
||||
OutputFormat::CSV => format!("Name,X,Y,Z,Percent\n"),
|
||||
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::Init(InitEvent {
|
||||
total,
|
||||
search_behavior: search_behavior.clone(),
|
||||
start_time,
|
||||
})).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&self, end_time: u128) -> String {
|
||||
match self {
|
||||
OutputFormat::Text => format!("Search complete in {}s\n", end_time / 1000),
|
||||
OutputFormat::CSV => format!("{}\n", end_time),
|
||||
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::End(EndEvent{ end_time })).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,6 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(feature = "sql")]
|
||||
use futures::executor::block_on;
|
||||
use schemsearch_files::Schematic;
|
||||
#[cfg(feature = "sql")]
|
||||
use schemsearch_sql::{load_schemdata, SchematicNode};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch_faster"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch-files"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -62,6 +62,9 @@ pub struct BlockEntity {
|
||||
pub id: String,
|
||||
#[serde(rename = "Pos")]
|
||||
pub pos: [i32; 3],
|
||||
|
||||
#[serde(flatten)]
|
||||
pub data: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@ -70,6 +73,9 @@ pub struct Entity {
|
||||
pub id: String,
|
||||
#[serde(rename = "Pos")]
|
||||
pub pos: [i32; 3],
|
||||
|
||||
#[serde(flatten)]
|
||||
pub data: Map<String, Value>,
|
||||
}
|
||||
|
||||
impl Schematic {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch-java"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
|
||||
@ -35,14 +35,7 @@ pub extern "system" fn Java_SchemSearch_search<'local>(mut env: JNIEnv<'local>,
|
||||
let schematic = Schematic::load(&PathBuf::from(&schematic_path)).unwrap();
|
||||
let pattern = Schematic::load(&PathBuf::from(&pattern_path)).unwrap();
|
||||
|
||||
let matches = search(schematic, &pattern, SearchBehavior {
|
||||
ignore_block_data: true,
|
||||
ignore_block_entities: true,
|
||||
ignore_entities: true,
|
||||
ignore_air: false,
|
||||
air_as_any: false,
|
||||
threshold: 0.0,
|
||||
});
|
||||
let matches = search(schematic, &pattern, SearchBehavior::default());
|
||||
|
||||
let mut result = String::new();
|
||||
for (x, y, z, p) in matches {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch-lib"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
|
||||
@ -17,20 +17,36 @@
|
||||
|
||||
pub mod pattern_mapper;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use pattern_mapper::match_palette;
|
||||
use schemsearch_files::Schematic;
|
||||
use crate::pattern_mapper::match_palette_adapt;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
pub struct SearchBehavior {
|
||||
pub ignore_block_data: bool,
|
||||
pub ignore_block_entities: bool,
|
||||
pub ignore_tile_entities: bool,
|
||||
pub ignore_tile_entity_data: bool,
|
||||
pub ignore_air: bool,
|
||||
pub air_as_any: bool,
|
||||
pub ignore_entities: bool,
|
||||
pub threshold: f32,
|
||||
}
|
||||
|
||||
impl Default for SearchBehavior {
|
||||
fn default() -> Self {
|
||||
SearchBehavior {
|
||||
ignore_block_data: true,
|
||||
ignore_tile_entities: true,
|
||||
ignore_tile_entity_data: false,
|
||||
ignore_entities: true,
|
||||
ignore_air: false,
|
||||
air_as_any: false,
|
||||
threshold: 90.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search(
|
||||
schem: Schematic,
|
||||
pattern_schem: &Schematic,
|
||||
@ -60,7 +76,7 @@ pub fn search(
|
||||
|
||||
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.width * pattern_schem.height * pattern_schem.length) as f32;
|
||||
let pattern_blocks = (pattern_schem.width * pattern_schem.height * pattern_schem.length) as f32 + if search_behavior.ignore_tile_entities { 0.0 } else { pattern_schem.block_entities.len() as f32 };
|
||||
|
||||
let pattern_width = pattern_schem.width as usize;
|
||||
let pattern_height = pattern_schem.height as usize;
|
||||
@ -87,6 +103,26 @@ pub fn search(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !search_behavior.ignore_tile_entities {
|
||||
for tile_entity in &pattern_schem.block_entities {
|
||||
for entry in &schem.block_entities {
|
||||
if tile_entity.id.as_str() == entry.id.as_str() {
|
||||
let pos = tile_entity.pos;
|
||||
let schem_pos = [pos[0] + x as i32, pos[1] + y as i32, pos[2] + z as i32];
|
||||
if schem_pos == entry.pos {
|
||||
if search_behavior.ignore_tile_entity_data {
|
||||
matching += 1;
|
||||
} else {
|
||||
if tile_entity.data == entry.data {
|
||||
matching += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let matching_percent = matching as f32 / pattern_blocks;
|
||||
if matching_percent >= search_behavior.threshold {
|
||||
matches.push((x as u16, y as u16, z as u16, matching_percent));
|
||||
@ -169,14 +205,7 @@ mod tests {
|
||||
let schematic = Schematic::load(&PathBuf::from("../tests/simple.schem")).unwrap();
|
||||
let endstone = Schematic::load(&PathBuf::from("../tests/endstone.schem")).unwrap();
|
||||
|
||||
let _ = search(schematic, &endstone, SearchBehavior {
|
||||
ignore_block_data: true,
|
||||
ignore_block_entities: true,
|
||||
ignore_entities: true,
|
||||
ignore_air: false,
|
||||
air_as_any: false,
|
||||
threshold: 0.9
|
||||
});
|
||||
let _ = search(schematic, &endstone, SearchBehavior::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -184,14 +213,7 @@ mod tests {
|
||||
let schematic = Schematic::load(&PathBuf::from("../tests/Random.schem")).unwrap();
|
||||
let pattern = Schematic::load(&PathBuf::from("../tests/Pattern.schem")).unwrap();
|
||||
|
||||
let matches = search(schematic, &pattern, SearchBehavior {
|
||||
ignore_block_data: true,
|
||||
ignore_block_entities: true,
|
||||
ignore_entities: true,
|
||||
ignore_air: false,
|
||||
air_as_any: false,
|
||||
threshold: 0.9
|
||||
});
|
||||
let matches = search(schematic, &pattern, SearchBehavior::default());
|
||||
|
||||
println!("{:?}", matches);
|
||||
assert_eq!(matches.len(), 1);
|
||||
@ -203,14 +225,7 @@ mod tests {
|
||||
let schematic = Schematic::load(&PathBuf::from("../tests/warships/GreyFly-by-Bosslar.schem")).unwrap();
|
||||
let pattern = Schematic::load(&PathBuf::from("../tests/gray_castle_complex.schem")).unwrap();
|
||||
|
||||
let matches = search(schematic, &pattern, SearchBehavior {
|
||||
ignore_block_data: false,
|
||||
ignore_block_entities: false,
|
||||
ignore_entities: false,
|
||||
ignore_air: false,
|
||||
air_as_any: false,
|
||||
threshold: 0.9
|
||||
});
|
||||
let matches = search(schematic, &pattern, SearchBehavior::default());
|
||||
|
||||
println!("{:?}", matches);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "schemsearch-sql"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
use std::sync::Mutex;
|
||||
use sqlx::{ConnectOptions, Executor, MySql, MySqlPool, Pool, Row};
|
||||
use sqlx::{Executor, MySql, Pool, Row};
|
||||
use sqlx::mysql::{MySqlConnectOptions, MySqlPoolOptions};
|
||||
use crate::filter::SchematicFilter;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user