mirror of
https://github.com/Chaoscaot/schemsearch.git
synced 2025-11-05 22:04:02 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7c1fd1ef7 | |||
| 80eeaad5d5 | |||
| 64158cf45b | |||
| e4b26755ea | |||
| 5607dcc72c | |||
| 5c9bcfc2ec | |||
| 1df33249c4 | |||
| ef2755115c | |||
| b32aac0aba | |||
| a9a3e70aef | |||
| c477a52f92 | |||
| 818de6be47 | |||
| 8f15b42146 | |||
| b8d912881d |
40
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
40
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Bug Report
|
||||
description: Create a report to fix a bug
|
||||
labels: [bug]
|
||||
title: "[BUG] <title>"
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Steps to reproduce the behavior.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: actual-behavior
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: A clear and concise description of what actually happened.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context about the problem here.
|
||||
validations:
|
||||
required: false
|
||||
35
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
|
||||
title: "[FEATURE] <title>"
|
||||
labels: [enhancement]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Proposed Solution
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
4
.github/workflows/master-build.yml
vendored
4
.github/workflows/master-build.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
run: make debug
|
||||
- name: Run tests
|
||||
run: cargo test --verbose -p schemsearch-lib
|
||||
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose --release -p schemsearch-cli
|
||||
run: make
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
|
||||
2
.github/workflows/release-build.yml
vendored
2
.github/workflows/release-build.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ env.cache-name }}-
|
||||
- name: Build
|
||||
run: cargo build --verbose --release -p schemsearch-cli
|
||||
run: make
|
||||
- name: Create Tarball
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: tar -czvf schemsearch-cli-${{ matrix.os }}.tar.gz -C target/release schemsearch-cli
|
||||
|
||||
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
||||
default:
|
||||
@echo "Building (Release)...";
|
||||
cargo rustc --release --color=always -p schemsearch-cli -- -C target-feature=+avx2
|
||||
|
||||
sql:
|
||||
@echo "Building (Release)...";
|
||||
cargo rustc --release --color=always -p schemsearch-cli --features sql -- -C target-feature=+avx2
|
||||
|
||||
debug:
|
||||
@echo "Building (Debug)...";
|
||||
cargo build -p schemsearch-cli
|
||||
|
||||
install: default
|
||||
@echo "Installing...";
|
||||
install -Dm755 target/release/schemsearch-cli /usr/bin/schemsearch
|
||||
|
||||
uninstall:
|
||||
@echo "Uninstalling...";
|
||||
rm -f /usr/bin/schemsearch
|
||||
|
||||
java:
|
||||
@echo "Building Java...";
|
||||
@echo "WARNING: This is WORK IN PROGRESS!";
|
||||
javac SchemSearch.java
|
||||
|
||||
clean:
|
||||
@echo "Cleaning...";
|
||||
cargo clean
|
||||
21
README.md
21
README.md
@ -4,12 +4,12 @@
|
||||
---
|
||||
|
||||
## WARNING: This is a work in progress and is really simple right now. It will be improved in the future.
|
||||
| Feature | Status |
|
||||
|------------------------|--------|
|
||||
| Block search | ✅ |
|
||||
| Block data less search | ✅ |
|
||||
| Tile entities search | ❌ |
|
||||
| Entities search | ❌ |
|
||||
| Feature | Status |
|
||||
|---------------------------|--------|
|
||||
| Block search | ✅ |
|
||||
| Block data less search | ✅ |
|
||||
| Tile entities data search | ❌ |
|
||||
| Entities search | ❌ |
|
||||
|
||||
---
|
||||
|
||||
@ -41,12 +41,13 @@ schemsearch-cli --help
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
A list of features that are planned to be implemented in the future. In order of priority.
|
||||
|
||||
- [ ] Tile entities search
|
||||
- [ ] Full JSON output (Progressbar)
|
||||
- [ ] Use AVX2 for faster search
|
||||
- [ ] Tile entities data search
|
||||
- [ ] Entities search
|
||||
- [ ] Better error handling
|
||||
- [ ] Web interface
|
||||
- [ ] McEdit search
|
||||
- [ ] McEdit Schematic support
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemsearch_lib::SearchBehavior;
|
||||
use schemsearch_lib::{Match, SearchBehavior};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(tag = "event")]
|
||||
@ -12,10 +12,8 @@ pub enum JsonEvent {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FoundEvent {
|
||||
pub name: String,
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub z: u16,
|
||||
pub percent: f32,
|
||||
#[serde(flatten, rename = "match")]
|
||||
pub match_: Match,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
mod types;
|
||||
mod json_output;
|
||||
mod sinks;
|
||||
mod stderr;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::io::Write;
|
||||
@ -25,7 +26,7 @@ use clap::{command, Arg, ArgAction, ValueHint};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use clap::error::ErrorKind;
|
||||
use schemsearch_lib::{search, SearchBehavior};
|
||||
use schemsearch_lib::{Match, search, SearchBehavior};
|
||||
use crate::types::{PathSchematicSupplier, SchematicSupplierType};
|
||||
#[cfg(feature = "sql")]
|
||||
use futures::executor::block_on;
|
||||
@ -37,9 +38,10 @@ use schemsearch_sql::filter::SchematicFilter;
|
||||
use schemsearch_sql::load_all_schematics;
|
||||
#[cfg(feature = "sql")]
|
||||
use crate::types::SqlSchematicSupplier;
|
||||
use indicatif::{ParallelProgressIterator, ProgressStyle};
|
||||
use indicatif::*;
|
||||
use schemsearch_files::Schematic;
|
||||
use crate::sinks::{OutputFormat, OutputSink};
|
||||
use crate::stderr::MaschineStdErr;
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
@ -94,7 +96,7 @@ fn main() {
|
||||
)
|
||||
.arg(
|
||||
Arg::new("output")
|
||||
.help("The output format and path [Format:Path] available formats: text, json, csv; available paths: std, (file path)")
|
||||
.help("The output format and path [Format:Path] available formats: text, json, csv; available paths: std, err, (file path)")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.action(ArgAction::Append)
|
||||
@ -139,6 +141,15 @@ fn main() {
|
||||
.default_value("0")
|
||||
.value_parser(|s: &str| s.parse::<usize>().map_err(|e| e.to_string())),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("machine")
|
||||
.help("Output for machines")
|
||||
.short('m')
|
||||
.long("machine")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("0")
|
||||
.value_parser(|s: &str| s.parse::<u16>().map_err(|e| e.to_string()))
|
||||
)
|
||||
.about("Searches for a pattern in a schematic")
|
||||
.bin_name("schemsearch");
|
||||
|
||||
@ -246,14 +257,21 @@ fn main() {
|
||||
|
||||
ThreadPoolBuilder::new().num_threads(*matches.get_one::<usize>("threads").expect("Could not get threads")).build_global().unwrap();
|
||||
|
||||
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| {
|
||||
let bar = ProgressBar::new(schematics.len() as u64); // "maschine"
|
||||
bar.set_style(ProgressStyle::with_template("[{elapsed}, ETA: {eta}] {wide_bar} {pos}/{len} {per_sec}").unwrap());
|
||||
let term_size = *matches.get_one::<u16>("machine").expect("Could not get machine");
|
||||
if term_size != 0 {
|
||||
bar.set_draw_target(ProgressDrawTarget::term_like(Box::new(MaschineStdErr { size: term_size })))
|
||||
}
|
||||
|
||||
let matches: Vec<SearchResult> = schematics.par_iter().progress_with(bar).map(|schem| {
|
||||
match schem {
|
||||
SchematicSupplierType::PATH(schem) => {
|
||||
let schematic = match load_schem(&schem.path) {
|
||||
Some(x) => x,
|
||||
None => return SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: vec![]
|
||||
matches: Vec::default()
|
||||
}
|
||||
};
|
||||
SearchResult {
|
||||
@ -271,12 +289,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if !output_std && !output_std_csv {
|
||||
println!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
|
||||
}
|
||||
eprintln!("Error while loading schematic ({}): {}", schem.get_name(), e.to_string());
|
||||
SearchResult {
|
||||
name: schem.get_name(),
|
||||
matches: vec![]
|
||||
matches: Vec::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,6 +330,6 @@ fn load_schem(schem_path: &PathBuf) -> Option<Schematic> {
|
||||
#[derive(Debug, Clone)]
|
||||
struct SearchResult {
|
||||
name: String,
|
||||
matches: Vec<(u16, u16, u16, f32)>,
|
||||
matches: Vec<Match>,
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,15 @@ use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::str::FromStr;
|
||||
use std::io::Write;
|
||||
use schemsearch_lib::SearchBehavior;
|
||||
use std::time::Duration;
|
||||
use indicatif::HumanDuration;
|
||||
use schemsearch_lib::{Match, SearchBehavior};
|
||||
use crate::json_output::{EndEvent, FoundEvent, InitEvent, JsonEvent};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputSink {
|
||||
Stdout,
|
||||
Stderr,
|
||||
File(String),
|
||||
}
|
||||
|
||||
@ -37,6 +40,7 @@ impl FromStr for OutputSink {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"std" => Ok(OutputSink::Stdout),
|
||||
"err" => Ok(OutputSink::Stderr),
|
||||
_ => Ok(OutputSink::File(s.to_string()))
|
||||
}
|
||||
}
|
||||
@ -45,23 +49,21 @@ impl FromStr for OutputSink {
|
||||
impl OutputSink {
|
||||
pub fn output(&self) -> Box<dyn Write> {
|
||||
match self {
|
||||
OutputSink::Stdout => Box::new(std::io::stdout().lock()),
|
||||
OutputSink::Stdout => Box::new(std::io::stdout()),
|
||||
OutputSink::Stderr => Box::new(std::io::stderr()),
|
||||
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 {
|
||||
pub fn found_match(&self, name: &String, pos: Match) -> 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::Text => format!("Found match in '{}' at x: {}, y: {}, z: {}, % = {}\n", name, pos.x, pos.y, pos.z, pos.percent),
|
||||
OutputFormat::CSV => format!("{},{},{},{},{}\n", name, pos.x, pos.y, pos.z, pos.percent),
|
||||
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,
|
||||
match_: pos,
|
||||
})).unwrap())
|
||||
}
|
||||
}
|
||||
@ -80,7 +82,7 @@ impl OutputFormat {
|
||||
|
||||
pub fn end(&self, end_time: u128) -> String {
|
||||
match self {
|
||||
OutputFormat::Text => format!("Search complete in {}s\n", end_time / 1000),
|
||||
OutputFormat::Text => format!("Search complete in {}\n", HumanDuration(Duration::from_millis(end_time as u64))),
|
||||
OutputFormat::CSV => format!("{}\n", end_time),
|
||||
OutputFormat::JSON => format!("{}\n", serde_json::to_string(&JsonEvent::End(EndEvent{ end_time })).unwrap())
|
||||
}
|
||||
|
||||
44
schemsearch-cli/src/stderr.rs
Normal file
44
schemsearch-cli/src/stderr.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use std::fmt::Debug;
|
||||
use std::io::Write;
|
||||
use indicatif::TermLike;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MaschineStdErr { pub(crate) size: u16}
|
||||
|
||||
impl TermLike for MaschineStdErr {
|
||||
fn width(&self) -> u16 {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn move_cursor_up(&self, _: usize) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_cursor_down(&self, _: usize) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_cursor_right(&self, _: usize) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_cursor_left(&self, _: usize) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_line(&self, s: &str) -> std::io::Result<()> {
|
||||
writeln!(std::io::stderr(), "{}", s)
|
||||
}
|
||||
|
||||
fn write_str(&self, s: &str) -> std::io::Result<()> {
|
||||
write!(std::io::stderr(), "{}", s)
|
||||
}
|
||||
|
||||
fn clear_line(&self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&self) -> std::io::Result<()> {
|
||||
std::io::stderr().flush()
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,8 @@
|
||||
use std::path::PathBuf;
|
||||
#[cfg(feature = "sql")]
|
||||
use futures::executor::block_on;
|
||||
#[allow(unused_imports)]
|
||||
use schemsearch_files::Schematic;
|
||||
#[cfg(feature = "sql")]
|
||||
use schemsearch_sql::{load_schemdata, SchematicNode};
|
||||
|
||||
|
||||
@ -45,8 +45,8 @@ pub extern "system" fn Java_SchemSearch_search<'local>(mut env: JNIEnv<'local>,
|
||||
});
|
||||
|
||||
let mut result = String::new();
|
||||
for (x, y, z, p) in matches {
|
||||
result.push_str(&format!("{}, {}, {}, {};", x, y, z, p));
|
||||
for m in matches {
|
||||
result.push_str(&format!("{}, {}, {}, {};", m.x, m.y, m.z, m.percent));
|
||||
}
|
||||
result.remove(result.len() - 1);
|
||||
let output = env.new_string(result).expect("Couldn't create java string!");
|
||||
|
||||
@ -36,7 +36,7 @@ pub fn search(
|
||||
schem: Schematic,
|
||||
pattern_schem: &Schematic,
|
||||
search_behavior: SearchBehavior,
|
||||
) -> Vec<(u16, u16, u16, f32)> {
|
||||
) -> Vec<Match> {
|
||||
if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
|
||||
return vec![];
|
||||
}
|
||||
@ -47,7 +47,7 @@ pub fn search(
|
||||
|
||||
let pattern_schem = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data);
|
||||
|
||||
let mut matches: Vec<(u16, u16, u16, f32)> = Vec::new();
|
||||
let mut matches: Vec<Match> = Vec::new();
|
||||
|
||||
let pattern_data = pattern_schem.block_data.as_slice();
|
||||
|
||||
@ -61,7 +61,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_data.len() as f32;
|
||||
|
||||
let pattern_width = pattern_schem.width as usize;
|
||||
let pattern_height = pattern_schem.height as usize;
|
||||
@ -90,7 +90,12 @@ pub fn search(
|
||||
}
|
||||
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));
|
||||
matches.push(Match {
|
||||
x: x as u16,
|
||||
y: y as u16,
|
||||
z: z as u16,
|
||||
percent: matching_percent,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,6 +104,25 @@ pub fn search(
|
||||
return matches;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
pub struct Match {
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub z: u16,
|
||||
pub percent: f32,
|
||||
}
|
||||
|
||||
impl Default for Match {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
percent: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize_data(data: &str, ignore_data: bool) -> &str {
|
||||
if ignore_data {
|
||||
@ -196,7 +220,10 @@ mod tests {
|
||||
|
||||
println!("{:?}", matches);
|
||||
assert_eq!(matches.len(), 1);
|
||||
assert_eq!(matches[0], (1, 0, 3, 1.0));
|
||||
assert_eq!(matches[0].x, 1);
|
||||
assert_eq!(matches[0].y, 0);
|
||||
assert_eq!(matches[0].z, 3);
|
||||
assert_eq!(matches[0].percent, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user