diff --git a/.clang-format b/.clang-format index ef06637..0a9188e 100644 --- a/.clang-format +++ b/.clang-format @@ -13,10 +13,8 @@ BinPackArguments: false AllowAllParametersOfDeclarationOnNextLine: false BinPackParameters: true -ColumnLimit: 0 AlignAfterOpenBracket: DontAlign -ContinuationIndentWidth: 4 ColumnLimit: 100 SpaceBeforeParens: ControlStatements diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c8458e..a7c479b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,26 +1,41 @@ -name: lootinator-release-builds +name: lootinator-release-build -on: [push] +on: + push: + branches: + - main jobs: linux: name: build/release/linux runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v4 - - - name: setup - run: | - sudo apt update - sudo apt install build-essentials - - - name: build - mkdir build && cd build && cmake .. - make -B -j8 - - - uses: actions/upload-artifact@v4 - with: - name: lootinator-cli-linux - path: ./${{ github.event.repository.name }}/build/cli/lootinator-cli - - + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: setup + run: | + sudo apt update + - name: build + run: | + mkdir build && cd build && cmake .. + make -B -j8 + - uses: actions/upload-artifact@v4 + with: + name: lootinator-cli-linux + path: ./build/cli/lootinator-cli + windows: + name: build/release/windows + runs-on: [windows-latest] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: build + run: | + cmake -S . -B build + cmake --build build --config Release + - uses: actions/upload-artifact@v4 + with: + name: lootinator-cli-windows.exe + path: .\build\cli\Release\lootinator-cli.exe diff --git a/.gitmodules b/.gitmodules index fb0796c..b95e913 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,6 @@ [submodule "external/json"] path = external/json url = https://github.com/nlohmann/json.git -[submodule "external/flag.h"] - path = external/flag.h - url = https://github.com/tsoding/flag.h -[submodule "external/CLI11"] - path = external/CLI11 - url = https://github.com/CLIUtils/CLI11 [submodule "external/clipp"] path = external/clipp url = https://github.com/muellan/clipp diff --git a/CMakeLists.txt b/CMakeLists.txt index ac25c2a..7b70444 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.11`) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -16,5 +16,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(lootinator) -add_subdirectory(example) -add_subdirectory(cli) \ No newline at end of file + +if(EMSCRIPTEN) + add_subdirectory(web) +else() + add_subdirectory(example) + add_subdirectory(cli) +endif() diff --git a/cli/constraints.json b/cli/constraints.json index 1fd886d..07823e2 100644 --- a/cli/constraints.json +++ b/cli/constraints.json @@ -2,7 +2,7 @@ { "item": "minecraft:gold_block", "range": { - "min": 5, + "min": 50, "max": 1000 }, "slot": 0 diff --git a/cli/src/main.cpp b/cli/src/main.cpp index 4e0f1c9..5ae7cae 100644 --- a/cli/src/main.cpp +++ b/cli/src/main.cpp @@ -13,38 +13,56 @@ int main(int argc, char** argv) { std::string constraint_file = ""; std::string version_str = ""; std::string output_file = "a.cu"; + std::string seed_output_file = "loot_seeds.txt"; - auto cli = ( - (clipp::required("--loot-table") & clipp::value("file_path.json", loot_table), - clipp::required("--constraint-file") & clipp::value("file_path.json", constraint_file), - clipp::required("-o", "--output") & clipp::value("filepath", output_file), - clipp::option("-sc", "--seedcracking").set(seedcracking).doc("seedcracking mode"), - clipp::option("-sk", "--single-kernel").set(single_kernel).doc("single kernel mode"), - clipp::option("-v", "--version") - .doc("the target Minecraft version e.g. 1.16 or latest, currently latest is 26.1") & - clipp::value("version", version_str)) - | clipp::required("--list-versions").set(list_versions).doc("list supported Minecraft versions") - ); - static_assert(mc::VersionRange::MC_LATEST == mc::VersionRange::MC_1_21_11_TO_26_1, + auto cli = + ((clipp::required("--loot-table") & + clipp::value("file_path.json", loot_table).if_missing([] { + std::cout << "Failed: you need to specify a loot table\n"; + }), + clipp::required("--constraints") & + clipp::value("file_path.json", constraint_file).if_missing([] { + std::cout << "Failed: you need to specify a constraint file\n"; + }), + clipp::option("-o", "--output") & + clipp::value("filepath.cu", output_file) + .doc("specify the output file for cuda, default is " + output_file), + clipp::option("-sc", "--seedcracking").set(seedcracking).doc("seedcracking mode"), + clipp::option("-sk", "--single-kernel").set(single_kernel).doc("single kernel mode"), + clipp::option("-so", "--seed-output") & + clipp::value("file_path.txt", seed_output_file) + .doc("specify the output file for seeds, default is " + seed_output_file), + clipp::required("-v", "--version") + .doc("the target Minecraft version e.g. 1.16 or latest, currently latest is " + "26.2") & + clipp::value("version", version_str).if_missing([] { + std::cout << "Failed: you need to specify a minecraft version\n"; + })) + + | clipp::required("--list-versions") + .set(list_versions) + .doc("list supported Minecraft versions")); + static_assert(mc::VersionRange::MC_LATEST == mc::VersionRange::MC_1_21_11_TO_26_2, "update the doc of version to reflect the latest version"); if (!clipp::parse(argc, argv, cli)) { std::cout << clipp::make_man_page(cli, argv[0]); - exit(1); + exit(loot::LootinatorErrorKind::INVALID_ARGUMENTS); } if (list_versions) { std::cout << "Supported Minecraft Versions:\n"; - for (const auto& v : mc::get_supported_versions()) { - std::cout << v << "\n"; + for (const auto& version : mc::get_supported_versions()) { + std::cout << version << "\n"; } - exit(0); + exit(loot::LootinatorErrorKind::SUCCESS); } mc::VersionRange version = mc::parse_version(version_str); if (version == mc::MC_UNDEFINED) { - fprintf(stderr, "Lootinator failed: version is undefined!\n"); - exit(1); + int error_code = loot::LootinatorErrorKind::INVALID_ARGUMENTS; + std::cout << "Lootinator failed (" << error_code << "): version is undefined.\n"; + exit(error_code); } std::ofstream fout(output_file); @@ -54,23 +72,23 @@ int main(int argc, char** argv) { loot::LootinatorError err = loot::LootinatorError(loot::LootinatorErrorKind::SUCCESS); if (single_kernel) { err = loot::generate_best_pipeline_heur( - loot_table, constraint_file, version, seedcracking, &s); + loot_table, constraint_file, version, seedcracking, &s, seed_output_file); } else { - err = - loot::generate_benchmark_source(loot_table, constraint_file, version, seedcracking, &s); + err = loot::generate_benchmark_source( + loot_table, constraint_file, version, seedcracking, &s, seed_output_file); } if (err.kind == loot::LootinatorErrorKind::SUCCESS) { fout << s; - std::cout << "Cuda generation was successful.\n"; - std::cout << "==== Selected Options ====\n"; + std::cout << "========= Results =========\n"; + std::cout << " Output File: " << output_file << "\n"; std::cout << " Version Range: " << mc::get_version_from_enum(version) << "\n"; std::cout << " Constraints: " << constraint_file << "\n"; std::cout << " Seedcracking: " << (seedcracking ? "true\n" : "false\n"); std::cout << " Single Kernel: " << (single_kernel ? "true\n" : "false\n"); - std::cout << " Output File: " << output_file << "\n"; - std::cout << "==========================\n"; + std::cout << "===========================\n"; } else { - std::cout << "Lootinator failed: " << err.message << '\n'; + std::cout << "Lootinator failed (" << err.kind << "): " << err.message << '\n'; + exit(err.kind); } } \ No newline at end of file diff --git a/example/src/main.cpp b/example/src/main.cpp index ac16544..1f6c391 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -21,7 +21,7 @@ int main() { std::string s; loot::LootinatorError err = loot::generate_best_pipeline_heur( - loot_table_file, constraints_file, mc::MC_1_21_TO_1_21_10, true, &s); + loot_table_file, constraints_file, mc::MC_1_21_TO_1_21_10, true, &s, "loot_seeds.txt"); if (err.kind == loot::LootinatorErrorKind::SUCCESS) { fout << s; diff --git a/external/CLI11 b/external/CLI11 deleted file mode 160000 index 30d783e..0000000 --- a/external/CLI11 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30d783eb61a2bf83ae29fcde70691eac852a7b7d diff --git a/external/flag.h b/external/flag.h deleted file mode 160000 index 7d36992..0000000 --- a/external/flag.h +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7d3699298551080678d7763adcdd22e78873f4c4 diff --git a/lootinator/CMakeLists.txt b/lootinator/CMakeLists.txt index 2dd5364..33400d1 100644 --- a/lootinator/CMakeLists.txt +++ b/lootinator/CMakeLists.txt @@ -12,10 +12,6 @@ add_library(lootinator include/lootinator/lootinator.h include/lootinator/mc/minecraft.hpp include/lootinator/probability/loot_prob.h - include/lootinator/template/helpers.h - include/lootinator/template/kernel/naive_bruteforce_template.h - include/lootinator/template/kernel_template.h - include/lootinator/template/template.h include/lootinator/utility/debug.h include/lootinator/utility/enum_bimap.hpp include/lootinator/utility/mth.h @@ -33,17 +29,14 @@ add_library(lootinator src/lootinator.cpp src/mc/minecraft.cpp src/probability/loot_prob.cpp - src/template/helpers.cpp - src/template/kernel/naive_bruteforce_template.cpp - src/template/kernel_template.cpp - src/template/template.cpp src/utility/debug.cpp ) target_include_directories(lootinator PUBLIC include) set(TEST_FILES - tests/smoke_tests.cpp + tests/function_order_dfs.cpp + tests/test_kgen_config.cpp ) # TODO: compiler flags, (-O3, warnings, etc) diff --git a/lootinator/include/lootinator/kgen/kernel.hpp b/lootinator/include/lootinator/kgen/kernel.hpp index 8ca4029..77a4abc 100644 --- a/lootinator/include/lootinator/kgen/kernel.hpp +++ b/lootinator/include/lootinator/kgen/kernel.hpp @@ -21,9 +21,10 @@ namespace kgen { // any extra scuffed stuff goes here bool using_nvrtc = false; + std::string seeds_output; KernelGenConfig(mc::VersionRange version, std::string loot_table, std::string constraints, - bool seedcracking); + bool seedcracking, std::string seeds_output); void traverse_and_derive(data::LootTreeNode* root, bool** edges); void construct_order(bool** edges, const int num_functions, data::LootTreeNode* root); @@ -32,6 +33,7 @@ namespace kgen { struct ConfiguredKernel { std::string kernel_name; + std::string seeds_output; std::string code; uint64_t total_threads; diff --git a/lootinator/include/lootinator/lootinator.h b/lootinator/include/lootinator/lootinator.h index 7103065..cdf1724 100644 --- a/lootinator/include/lootinator/lootinator.h +++ b/lootinator/include/lootinator/lootinator.h @@ -24,7 +24,9 @@ namespace loot { BAD_CONSTRAINT_FILE, BAD_LOOT_TABLE, RANGE_PARSE, - INTERNAL_ERROR + INTERNAL_ERROR, + MC_VERSION_UNDEFINED, + INVALID_ARGUMENTS }; std::string parse_errno(LootinatorErrorKind error); @@ -46,19 +48,19 @@ namespace loot { LootinatorError generate_best_pipeline_heur(const std::string loot_table_filepath, const std::string constraint_filepath, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result); + const bool use_seedcracking_mode, std::string* result, std::string seeds_output); LootinatorError generate_benchmark_source(const std::string loot_table_filepath, const std::string constraint_filepath, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result); + const bool use_seedcracking_mode, std::string* result, std::string seeds_output); LootinatorError generate_best_pipeline_heur_from_string(const std::string loot_table, const std::string constraints, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result); + const bool use_seedcracking_mode, std::string* result, std::string seeds_output); LootinatorError generate_benchmark_source_from_string(const std::string loot_table, const std::string constraints, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result); + const bool use_seedcracking_mode, std::string* result, std::string seeds_output); } // namespace loot #endif diff --git a/lootinator/include/lootinator/mc/minecraft.hpp b/lootinator/include/lootinator/mc/minecraft.hpp index b1b912e..2df9618 100644 --- a/lootinator/include/lootinator/mc/minecraft.hpp +++ b/lootinator/include/lootinator/mc/minecraft.hpp @@ -13,7 +13,7 @@ namespace mc { X(MC_1_14_TO_1_15) \ X(MC_1_16_TO_1_20) \ X(MC_1_21_TO_1_21_10) \ - X(MC_1_21_11_TO_26_1) + X(MC_1_21_11_TO_26_2) enum VersionRange { #define X(v) v, diff --git a/lootinator/include/lootinator/template/helpers.h b/lootinator/include/lootinator/template/helpers.h deleted file mode 100644 index f8e3939..0000000 --- a/lootinator/include/lootinator/template/helpers.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef LOOTINATOR_TEMPLATE_HELPERS_H -#define LOOTINATOR_TEMPLATE_HELPERS_H - -#include -#include -#include - -namespace loot { - void generate_set_seed( - std::ostream& out, const std::string& prng_var_pointer, const std::string& seed_var); - void generate_skip_n(std::ostream& out, const std::string& prng_var_pointer, const int64_t n); - void generate_set_count(std::ostream& out, const std::string& prng_var_pointer, - const int min_inclusive, const int max_inclusive); - void generate_rarity_filter(std::ostream& out, const std::string& prng_var_pointer, - const std::string& fail_operation, const float rarity); -} // namespace loot - -#endif \ No newline at end of file diff --git a/lootinator/include/lootinator/template/kernel/naive_bruteforce_template.h b/lootinator/include/lootinator/template/kernel/naive_bruteforce_template.h deleted file mode 100644 index 074ebf1..0000000 --- a/lootinator/include/lootinator/template/kernel/naive_bruteforce_template.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef LOOTINATOR_TEMPLATE_KERNEL_NAIVE_BRUTEFORCE_TEMPLATE_H -#define LOOTINATOR_TEMPLATE_KERNEL_NAIVE_BRUTEFORCE_TEMPLATE_H - -#include "lootinator/template/kernel_template.h" - -namespace loot { - class NaiveBruteforceTemplate : KernelTemplate { - protected: - virtual void generate_kernel_body(std::ostream& out) const = 0; - }; -} // namespace loot - -#endif \ No newline at end of file diff --git a/lootinator/include/lootinator/template/kernel_template.h b/lootinator/include/lootinator/template/kernel_template.h deleted file mode 100644 index 47a9d2a..0000000 --- a/lootinator/include/lootinator/template/kernel_template.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef LOOTINATOR_TEMPLATE_KERNEL_TEMPLATE_H -#define LOOTINATOR_TEMPLATE_KERNEL_TEMPLATE_H - -#include "lootinator/template/template.h" - -namespace loot { - class KernelTemplate : Template { - protected: - std::string kernel_name; - - // these generators will be shared by all KernelTemplate objects - void generate_loot_processors(std::ostream& out) const; - void generate_device_helpers(std::ostream& out) const; - - // these generators can, but may not be modified by each individual KernelTemplate subclass - virtual void generate_kernel_header(std::ostream& out) const; - void generate_kernel_terminator(std::ostream& out) const; - - // this generator defines the kernel structure and must be overriden by KernelTemplate - // subclasses. - virtual void generate_kernel_body(std::ostream& out) const = 0; - - public: - KernelTemplate(const TemplateParameters& params, const std::string& kernel_name); - virtual void generate(std::ostream& out) const override; - }; -} // namespace loot - -#endif \ No newline at end of file diff --git a/lootinator/include/lootinator/template/template.h b/lootinator/include/lootinator/template/template.h deleted file mode 100644 index 2357646..0000000 --- a/lootinator/include/lootinator/template/template.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef LOOTINATOR_TEMPLATE_TEMPLATE_H -#define LOOTINATOR_TEMPLATE_TEMPLATE_H - -#include "lootinator/constraint/constraint.h" -#include -#include - -namespace loot { - struct TemplateParameters { - std::vector constraints; - std::map itemname_to_id; // TODO maybe reverse mapping and use a - // vector instead? LootTable loot_table; - // MinecraftVersion version; - }; - - // Template objects are the main building blocks for Lootinator's automated code generation. - class Template { - protected: - const TemplateParameters& params; - // here is the right spot for helper functions shared by subclasses of Template: - // - add array of constants to memory: either CPU, global, global-shared, managed, or - // constant - // - save loot seed result - // ... - - public: - Template(const TemplateParameters& params); - - // has to be defined by all subclasses of Template; generates and outputs - // the entire piece of code defined by this template to the 'out' stream. - virtual void generate(std::ostream& out) const = 0; - - // returns the result of generate(std::ostream&) as a string. - std::string generate() const; - }; -} // namespace loot - -#endif \ No newline at end of file diff --git a/lootinator/src/kgen/bruteforce_kernel.cpp b/lootinator/src/kgen/bruteforce_kernel.cpp index 1b1ae42..ca4639e 100644 --- a/lootinator/src/kgen/bruteforce_kernel.cpp +++ b/lootinator/src/kgen/bruteforce_kernel.cpp @@ -45,6 +45,7 @@ namespace kgen { ConfiguredKernel BruteforceKernel::generate() { return ConfiguredKernel{ this->name, + this->kgen_config.seeds_output, to_string(), UINT64_C(1) << 48, UINT64_C(1) << 32, @@ -54,7 +55,7 @@ namespace kgen { 0, UINT32_C(1) << 16, UINT32_C(1) << 19, - 0, + this->heuristic(), }; } @@ -172,12 +173,13 @@ namespace kgen { << R"(]; // min_max_count__counter_index__enchantment_count u64 enchantment_mask = data[)" << pool_off << R"( + item * )" << bpe << R"( + 1]; -u32 item_idx = entry_data >> 24; // [8b][6b][6b][4b][8b] u32 min_count = (entry_data >> 18) & 0x3f; u32 max_count = (entry_data >> 12) & 0x3f; u32 counter_idx = (entry_data >> 8) & 0xf;)"; if (this->kgen_config.seedcracking && !disable_seedcracking) { + out << "u32 item_idx = entry_data >> 24; // [8b][6b][6b][4b][8b]\n"; + std::string one = pool->children.size() > 32 ? "((u64)1)" : "((u32)1)"; std::string forbidden_bitmask = create_forbidden_item_mask(pool); diff --git a/lootinator/src/kgen/cuda_source_gen.cpp b/lootinator/src/kgen/cuda_source_gen.cpp index f18f4af..dcb4086 100644 --- a/lootinator/src/kgen/cuda_source_gen.cpp +++ b/lootinator/src/kgen/cuda_source_gen.cpp @@ -21,6 +21,7 @@ namespace kgen { #include #include #include +#include // start of shared definitions block #define SHARED_DEFINITIONS @@ -37,6 +38,7 @@ void gpuAssert(cudaError_t code, const char *file, int line) { struct ConfiguredKernel { std::string kernel_name; + std::string seeds_output; std::vector shared_memory; u64 total_threads; u64 threads_per_batch; @@ -79,7 +81,7 @@ struct BenchmarkResults { typedef std::vector KernelPipeline; typedef void (*launch_function)(const KernelPipeline&, u32, u64); -void launch_configured_kernel(launch_function lf, const KernelPipeline& pipeline, bool print_results) { +void launch_configured_kernel(launch_function lf, const KernelPipeline& pipeline, bool print_results, std::ostream &out) { u64* h_result_array = (u64*)malloc(pipeline.back()->max_results * sizeof(u64)); const u32 num_blocks = pipeline[0]->threads_per_batch / pipeline[0]->threads_per_block; @@ -97,7 +99,7 @@ void launch_configured_kernel(launch_function lf, const KernelPipeline& pipeline if (!print_results) continue; for (u32 i = 0; i < h_result_count; i++) { - std::cout << h_result_array[i] << '\n'; + out << h_result_array[i] << '\n'; } std::cout << std::flush; } @@ -197,6 +199,7 @@ int main() { )"; for (int i = 0; i < static_cast(kp.size()); i++) { fout << "ConfiguredKernel ck" << i << " {\"" << kp[i].kernel_name << "\", " + << "\"" << kp[i].seeds_output << "\", " << "std::vector(), " << kp[i].total_threads << "ULL, " << kp[i].threads_per_batch << "ULL, " << kp[i].threads_per_block << "U, " << kp[i].device_id << "U, " << kp[i].start_batch << ", " << kp[i].end_batch << ", " @@ -212,7 +215,11 @@ int main() { } fout << R"( - launch_configured_kernel(kernel0::launch, pipeline, true); + + std::ofstream seeds_file(")" + << kp[0].seeds_output << R"("); + + launch_configured_kernel(kernel0::launch, pipeline, true, seeds_file); return 0; } )"; @@ -242,6 +249,7 @@ int main() { int index = (j << 8) | i; out << "ConfiguredKernel ck" << index << " {\"" << pipeline[i].kernel_name << "\", " + << "\"" << pipeline[i].seeds_output << "\", " << "std::vector(), " << pipeline[i].total_threads << "ULL, " << pipeline[i].threads_per_batch << "ULL, " << pipeline[i].threads_per_block << "U, " << pipeline[i].device_id << "U, " << pipeline[i].start_batch << ", " @@ -282,7 +290,7 @@ int main() { CUDA_CHECK(cudaDeviceSynchronize()); for (u32 i = 0; i < 3; i++) { - launch_configured_kernel(launchers[k], *pipeline, false); + launch_configured_kernel(launchers[k], *pipeline, false, std::cout); } // benchmarking with auto-tuning @@ -292,7 +300,7 @@ int main() { work_config->end_batch = work_config->start_batch + batches; auto t0 = std::chrono::high_resolution_clock::now(); - launch_configured_kernel(launchers[k], *pipeline, false); + launch_configured_kernel(launchers[k], *pipeline, false, std::cout); auto t1 = std::chrono::high_resolution_clock::now(); elapsed_ms = (t1-t0).count() * 1e-6f; if (elapsed_ms < BENCH_CUTOFF) @@ -311,7 +319,7 @@ int main() { } int best_kernel = -1; - double best_perf = result_array[0].ms_total_estimate - 1.0; + double best_perf = result_array[0].ms_total_estimate + 1.0; for (int k = 0; k < num_kernels; k++) { if (result_array[k].success && result_array[k].ms_total_estimate < best_perf) { best_perf = result_array[k].ms_total_estimate; @@ -331,8 +339,10 @@ int main() { std::cerr << "Running kernel " << (*(configs[best_kernel]))[0]->kernel_name << "...\n"; { + std::ofstream seeds_file((*(configs[best_kernel]))[0]->seeds_output); + const KernelPipeline* config = configs[best_kernel]; - launch_configured_kernel(launchers[best_kernel], *config, true); + launch_configured_kernel(launchers[best_kernel], *config, true, seeds_file); std::cerr << "Finished.\n"; } return 0; diff --git a/lootinator/src/kgen/kernel.cpp b/lootinator/src/kgen/kernel.cpp index 59f12ba..507efeb 100644 --- a/lootinator/src/kgen/kernel.cpp +++ b/lootinator/src/kgen/kernel.cpp @@ -4,11 +4,12 @@ #include #include +#include namespace kgen { KernelGenConfig::KernelGenConfig(mc::VersionRange version, std::string loot_table, - std::string constraint_string, bool seedcracking) - : version(version), seedcracking(seedcracking) { + std::string constraint_string, bool seedcracking, std::string seeds_output) + : seeds_output(seeds_output), version(version), seedcracking(seedcracking) { try { loot_table_json = nlohmann::json::parse(loot_table); @@ -23,7 +24,7 @@ namespace kgen { } } } - } catch (std::runtime_error& err) { + } catch (std::exception& err) { throw loot::LootinatorError(loot::LootinatorErrorKind::BAD_LOOT_TABLE); } @@ -35,7 +36,7 @@ namespace kgen { return a.item_equal(b); }; merge_contraints(constr, constraints, cmp_func); - } catch (std::runtime_error& err) { + } catch (std::exception& err) { throw loot::LootinatorError(loot::LootinatorErrorKind::BAD_LOOT_TABLE); } @@ -77,14 +78,21 @@ namespace kgen { } } - static bool check_cycles_dfs(int vertex, const int num_functions, bool** edges, bool* visited) { + static bool check_cycles_dfs( + int vertex, const int num_functions, bool** edges, bool* visited, bool from_root) { for (int v = 0; v < num_functions; v++) { + if (from_root) { + for (int v2 = 0; v2 < num_functions; v2++) + visited[v2] = false; + visited[vertex] = true; + } + if (edges[vertex][v]) { if (visited[v]) { return true; } visited[v] = true; - if (check_cycles_dfs(v, num_functions, edges, visited)) { + if (check_cycles_dfs(v, num_functions, edges, visited, false)) { return true; } } @@ -172,10 +180,7 @@ namespace kgen { // "DFS" bool* visited = new bool[NUM_FUNCTIONS]; for (int v = 0; v < NUM_FUNCTIONS; v++) { - for (int v2 = 0; v2 < NUM_FUNCTIONS; v2++) - visited[v2] = false; - visited[v] = true; - if (check_cycles_dfs(v, NUM_FUNCTIONS, edges, visited)) { + if (check_cycles_dfs(v, NUM_FUNCTIONS, edges, visited, true)) { no_fast_filter = true; } } diff --git a/lootinator/src/kgen/secondary_bruteforce_kernel.cpp b/lootinator/src/kgen/secondary_bruteforce_kernel.cpp index 140a2ca..3502987 100644 --- a/lootinator/src/kgen/secondary_bruteforce_kernel.cpp +++ b/lootinator/src/kgen/secondary_bruteforce_kernel.cpp @@ -40,6 +40,7 @@ namespace kgen { return ConfiguredKernel{ this->name, + this->kgen_config.seeds_output, to_string(), UINT64_C(1) << 48, UINT64_C(1) << 32, diff --git a/lootinator/src/kgen/statepred_kernel.cpp b/lootinator/src/kgen/statepred_kernel.cpp index 63904f4..93276cd 100644 --- a/lootinator/src/kgen/statepred_kernel.cpp +++ b/lootinator/src/kgen/statepred_kernel.cpp @@ -78,6 +78,7 @@ namespace kgen { uint32_t end_batch = total_threads / threads_per_batch; return ConfiguredKernel{ this->name, + this->kgen_config.seeds_output, to_string(), total_threads, threads_per_batch, diff --git a/lootinator/src/lootinator.cpp b/lootinator/src/lootinator.cpp index 915f4d3..b83c420 100644 --- a/lootinator/src/lootinator.cpp +++ b/lootinator/src/lootinator.cpp @@ -1,15 +1,40 @@ #include "lootinator/lootinator.h" +#include "lootinator/kgen/kernel.hpp" #include "lootinator/kgen/pipeline_generator.hpp" #include "lootinator/probability/loot_prob.h" +#include #include #include #include namespace loot { + LootinatorError generate_pipeline( + kgen::KernelGenConfig kgen_config, std::string* result, bool single_kernel) { + double config_probability = prob::get_probability_of_config(kgen_config); + if (config_probability == 0.0) { + return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_NOT_POSSIBLE); + } + if (config_probability > CUTOFF_PROBABILIY) { + return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK); + } + + kgen::PipelineGenerator pipeline_gen(kgen_config); + auto pipelines = pipeline_gen.add_state_prediction().add_bruteforce().build(); + + std::stringstream ss; + if (single_kernel) { + kgen::generate_runner_source(pipelines[0], ss); + } else { + kgen::generate_benchmarker_source(pipelines, ss); + } + *result = ss.str(); + return loot::LootinatorError(loot::LootinatorErrorKind::SUCCESS); + } + LootinatorError generate_best_pipeline_heur(const std::string loot_table_filepath, const std::string constraint_filepath, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result) { + const bool use_seedcracking_mode, std::string* result, std::string seeds_output) { try { std::ifstream loot_table_f(loot_table_filepath); std::ifstream constraints_f(constraint_filepath); @@ -20,23 +45,17 @@ namespace loot { std::stringstream constraints; constraints << constraints_f.rdbuf(); - kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig( - version_range, loot_table.str(), constraints.str(), use_seedcracking_mode); - - double config_probability = prob::get_probability_of_config(kgen_config); - if (config_probability > CUTOFF_PROBABILIY) { - return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK); - } + kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig(version_range, + loot_table.str(), + constraints.str(), + use_seedcracking_mode, + seeds_output); - kgen::PipelineGenerator pipeline_gen(kgen_config); - auto pipelines = pipeline_gen.add_state_prediction().add_bruteforce().build(); - - std::stringstream ss; - kgen::generate_runner_source(pipelines[0], ss); - *result = ss.str(); + // peak error handling + return generate_pipeline(kgen_config, result, true); } catch (loot::LootinatorError err) { return err; - } catch (std::runtime_error& err) { + } catch (std::exception& err) { return loot::LootinatorError(loot::LootinatorErrorKind::INTERNAL_ERROR); } return loot::LootinatorError(loot::LootinatorErrorKind::SUCCESS); @@ -44,7 +63,7 @@ namespace loot { LootinatorError generate_benchmark_source(const std::string loot_table_filepath, const std::string constraint_filepath, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result) { + const bool use_seedcracking_mode, std::string* result, std::string seeds_output) { try { std::ifstream loot_table_f(loot_table_filepath); std::ifstream constraints_f(constraint_filepath); @@ -55,23 +74,16 @@ namespace loot { std::stringstream constraints; constraints << constraints_f.rdbuf(); - kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig( - version_range, loot_table.str(), constraints.str(), use_seedcracking_mode); - - double config_probability = prob::get_probability_of_config(kgen_config); - if (config_probability > CUTOFF_PROBABILIY) { - return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK); - } - - kgen::PipelineGenerator pipeline_gen(kgen_config); - auto pipelines = pipeline_gen.add_state_prediction().add_bruteforce().build(); + kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig(version_range, + loot_table.str(), + constraints.str(), + use_seedcracking_mode, + seeds_output); - std::stringstream ss; - kgen::generate_benchmarker_source(pipelines, ss); - *result = ss.str(); + return generate_pipeline(kgen_config, result, false); } catch (loot::LootinatorError err) { return err; - } catch (std::runtime_error& err) { + } catch (std::exception& err) { return loot::LootinatorError(loot::LootinatorErrorKind::INTERNAL_ERROR); } @@ -80,25 +92,15 @@ namespace loot { LootinatorError generate_best_pipeline_heur_from_string(const std::string loot_table, const std::string constraints, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result) { + const bool use_seedcracking_mode, std::string* result, std::string seeds_output) { try { kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig( - version_range, loot_table, constraints, use_seedcracking_mode); + version_range, loot_table, constraints, use_seedcracking_mode, seeds_output); - double config_probability = prob::get_probability_of_config(kgen_config); - if (config_probability > CUTOFF_PROBABILIY) { - return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK); - } - - kgen::PipelineGenerator pipeline_gen(kgen_config); - auto pipelines = pipeline_gen.add_state_prediction().add_bruteforce().build(); - - std::stringstream ss; - kgen::generate_runner_source(pipelines[0], ss); - *result = ss.str(); + return generate_pipeline(kgen_config, result, true); } catch (loot::LootinatorError err) { return err; - } catch (std::runtime_error& err) { + } catch (std::exception& err) { return loot::LootinatorError(loot::LootinatorErrorKind::INTERNAL_ERROR); } return loot::LootinatorError(loot::LootinatorErrorKind::SUCCESS); @@ -106,25 +108,15 @@ namespace loot { LootinatorError generate_benchmark_source_from_string(const std::string loot_table, const std::string constraints, const mc::VersionRange version_range, - const bool use_seedcracking_mode, std::string* result) { + const bool use_seedcracking_mode, std::string* result, std::string seeds_output) { try { kgen::KernelGenConfig kgen_config = kgen::KernelGenConfig( - version_range, loot_table, constraints, use_seedcracking_mode); + version_range, loot_table, constraints, use_seedcracking_mode, seeds_output); - double config_probability = prob::get_probability_of_config(kgen_config); - if (config_probability > CUTOFF_PROBABILIY) { - return loot::LootinatorError(loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK); - } - - kgen::PipelineGenerator pipeline_gen(kgen_config); - auto pipelines = pipeline_gen.add_state_prediction().add_bruteforce().build(); - - std::stringstream ss; - kgen::generate_benchmarker_source(pipelines, ss); - *result = ss.str(); + return generate_pipeline(kgen_config, result, true); } catch (loot::LootinatorError err) { return err; - } catch (std::runtime_error& err) { + } catch (std::exception& err) { return loot::LootinatorError(loot::LootinatorErrorKind::INTERNAL_ERROR); } return loot::LootinatorError(loot::LootinatorErrorKind::SUCCESS); diff --git a/lootinator/src/mc/minecraft.cpp b/lootinator/src/mc/minecraft.cpp index 15bdc84..71549c6 100644 --- a/lootinator/src/mc/minecraft.cpp +++ b/lootinator/src/mc/minecraft.cpp @@ -297,8 +297,9 @@ namespace mc { {mc::VersionRange::MC_1_21_TO_1_21_10, "1.21.9"}, {mc::VersionRange::MC_1_21_TO_1_21_10, "1.21.10"}, - {mc::VersionRange::MC_1_21_11_TO_26_1, "1.21.11"}, - {mc::VersionRange::MC_1_21_11_TO_26_1, "26.1"}, + {mc::VersionRange::MC_1_21_11_TO_26_2, "1.21.11"}, + {mc::VersionRange::MC_1_21_11_TO_26_2, "26.1"}, + {mc::VersionRange::MC_1_21_11_TO_26_2, "26.2"}, }); std::string get_version_from_enum(mc::VersionRange version) { @@ -858,7 +859,7 @@ namespace mc { CURSE_OF_VANISHING, FROST_WALKER, MENDING}}); - } else if (version_range == mc::VersionRange::MC_1_21_11_TO_26_1) { + } else if (version_range == mc::VersionRange::MC_1_21_11_TO_26_2) { return std::vector({{ PROTECTION, FIRE_PROTECTION, diff --git a/lootinator/src/template/component/TODO b/lootinator/src/template/component/TODO deleted file mode 100644 index 3d5bb9d..0000000 --- a/lootinator/src/template/component/TODO +++ /dev/null @@ -1,3 +0,0 @@ -- loot pool filter generator (for pools that contain filtered items) -- loot pool skip generator (for pools that don't contain filtered items) -- item layout filter generator diff --git a/lootinator/src/template/function/TODO b/lootinator/src/template/function/TODO deleted file mode 100644 index d5bdd97..0000000 --- a/lootinator/src/template/function/TODO +++ /dev/null @@ -1,8 +0,0 @@ -- set count code generator -- enchant randomly code generator -- enchant with levels code generator -- apply damage code generator -- set effect code generator -- rarity filter code generator -- code generators for early-version loot tables - diff --git a/lootinator/src/template/helpers.cpp b/lootinator/src/template/helpers.cpp deleted file mode 100644 index 12be94d..0000000 --- a/lootinator/src/template/helpers.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "lootinator/template/helpers.h" - -namespace loot { - void generate_set_seed( - std::ostream& out, const std::string& prng_var_pointer, const std::string& seed_var) { - out << '*' << prng_var_pointer << " = (0x5deece66dULL ^ " << seed_var - << ") & ((1ULL<<48)-1);\n"; - } - - void generate_skip_n(std::ostream& out, const std::string& prng_var_pointer, const int64_t n) { - uint64_t m = 1; - uint64_t a = 0; - uint64_t im = 0x5deece66dULL; - uint64_t ia = 0xb; - uint64_t k; - - for (k = static_cast(n); k; k >>= 1) { - if (k & 1) { - m *= im; - a = im * a + ia; - } - ia = (im + 1) * ia; - im *= im; - } - - m &= (1ULL << 48) - 1; - a &= (1ULL << 48) - 1; - - out << '*' << prng_var_pointer << " = ((*" << prng_var_pointer << ") * " << m << "ULL + " - << a << "ULL) & ((1ULL<<48)-1);\n"; - } - - void generate_set_count(std::ostream& out, const std::string& prng_var_pointer, - const int min_inclusive, const int max_inclusive) { - if (min_inclusive == max_inclusive) { - out << min_inclusive << ";\n"; - } else { - out << min_inclusive << " + " - << "nextInt(" << prng_var_pointer << ", " << (max_inclusive - min_inclusive + 1) - << ");\n"; - } - } - - void generate_rarity_filter(std::ostream& out, const std::string& prng_var_pointer, - const std::string& fail_operation, const float rarity) { - (void)out; - (void)prng_var_pointer; - (void)fail_operation; - (void)rarity; - - // TODO - } -} // namespace loot \ No newline at end of file diff --git a/lootinator/src/template/kernel/TODO b/lootinator/src/template/kernel/TODO deleted file mode 100644 index d070763..0000000 --- a/lootinator/src/template/kernel/TODO +++ /dev/null @@ -1,8 +0,0 @@ -- base template for all kernels (precomputed loot tables, other shared code) -- naive bruteforce kernel template -- roll count state prediction template -- item type state prediction template -- random enchantment state prediction template -- random enchantment advanced reversal template -- bit lifting template (is it even worth it?) -- other options? diff --git a/lootinator/src/template/kernel/naive_bruteforce_template.cpp b/lootinator/src/template/kernel/naive_bruteforce_template.cpp deleted file mode 100644 index dfee9f3..0000000 --- a/lootinator/src/template/kernel/naive_bruteforce_template.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "lootinator/template/kernel/naive_bruteforce_template.h" - -namespace loot { - void NaiveBruteforceTemplate::generate_kernel_body(std::ostream& out) const { - (void)out; - } -} // namespace loot \ No newline at end of file diff --git a/lootinator/src/template/kernel_template.cpp b/lootinator/src/template/kernel_template.cpp deleted file mode 100644 index b28e989..0000000 --- a/lootinator/src/template/kernel_template.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "lootinator/template/kernel_template.h" - -namespace loot { - KernelTemplate::KernelTemplate(const TemplateParameters& params, const std::string& kernel_name) - : Template(params) { - this->kernel_name = kernel_name; - } - - void KernelTemplate::generate(std::ostream& out) const { - generate_device_helpers(out); - generate_loot_processors(out); - generate_kernel_header(out); - generate_kernel_body(out); - } - - // Generates PRNG functions for Java Random that will be shared by - // all loot finding / loot cracking kernels: - void KernelTemplate::generate_device_helpers(std::ostream& out) const { - out << - R"(__device__ inline void setSeed(uint64_t* rand, uint64_t value){ *rand = (value ^ 0x5deece66d) & ((1ULL << 48) - 1); } -__device__ inline int next(uint64_t* rand, const int bits){ *rand = (*rand * 0x5deece66d + 0xb) & ((1ULL << 48) - 1); return (int)((int64_t)*rand >> (48 - bits)); } -__device__ inline int nextInt(uint64_t* rand, const int n){ if ((n-1 & n) == 0) {uint64_t x = n * (uint64_t)next(rand, 31); return (int)((int64_t)x >> 31);} else {return (int)(next(rand, 31) % n);} } -__device__ inline float nextFloat(uint64_t* rand){ return next(rand, 24) / (float)(1 << 24); -__device__ inline int nextIntBounded(uint64_t* rand, const int min, const int max) {if (min >= max) {return min;} return nextInt(rand, max - min + 1) + min;} -__device__ inline int nextIntNoAdvance(uint64_t *rand, const int n) {if ((n-1 & n) == 0) {uint64_t x = n * *rand; return (int)((int64_t)x >> 31);} else {return (int)(*rand % n);}} -} -)"; - } - - void KernelTemplate::generate_loot_processors(std::ostream& out) const { - (void)out; - // TODO - // Based on the loot functions listed in the computed loot table, generate CUDA - // __device__ function representations of all the functions. - } - - // Generates a kernel header - copies necessary data to __shared__ memory and creates unique - // thread index tid. - void KernelTemplate::generate_kernel_header(std::ostream& out) const { - out << - R"( -extern "C" { - typedef unsigned long long u64; - typedef unsigned int u32; - typedef int i32; - - __global__ void)" - << kernel_name << R"(( - u64* result_array, u32* result_count, - u32* shared_mem_contents, u32 shared_mem_contents_length, - u64 offset) - { - extern __shared__ u32 data[]; - if (threadIdx.x < shared_mem_contents_length) { - for (int i = threadIdx.x; i < shared_mem_contents_length; i += blockDim.x) { - data[i] = shared_mem_contents[i]; - } - } - __syncthreads(); - - const u64 tid = blockIdx.x * blockDim.x + threadIdx.x + offset; -)"; - } - - void KernelTemplate::generate_kernel_terminator(std::ostream& out) const { - out << - R"( - } //end kernel -} //end extern "C" -)"; - } -} // namespace loot \ No newline at end of file diff --git a/lootinator/src/template/template.cpp b/lootinator/src/template/template.cpp deleted file mode 100644 index be39dd7..0000000 --- a/lootinator/src/template/template.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "lootinator/template/template.h" - -#include - -namespace loot { - Template::Template(const TemplateParameters& params) : params(params) { - } - - std::string Template::generate() const { - std::ostringstream oss; - generate(oss); - return oss.str(); - } -} // namespace loot diff --git a/lootinator/tests/data/constraints.json b/lootinator/tests/data/constraints.json deleted file mode 100644 index 982bb27..0000000 --- a/lootinator/tests/data/constraints.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "item": 10, - "range": { - "min": 0, - "max": 15 - }, - "slot": 1, - "attributes": [ - { - "type": 1, - "level": 17 - }, - { - "type": 2, - "level": 21 - } - ] - }, - { - "item": 21, - "range": { - "min": 4, - "max": 20 - }, - "slot": 13, - "attributes": [ - { - "type": 18, - "level": 7 - } - ] - } -] \ No newline at end of file diff --git a/lootinator/tests/data/item_map_bastion.txt b/lootinator/tests/data/item_map_bastion.txt deleted file mode 100644 index 1082b2e..0000000 --- a/lootinator/tests/data/item_map_bastion.txt +++ /dev/null @@ -1,33 +0,0 @@ -minecraft:netherite_scrap -minecraft:gold_nugget -minecraft:music_disc_pigstep -minecraft:iron_ingot -minecraft:golden_carrot -minecraft:golden_axe -minecraft:ancient_debris -minecraft:book -minecraft:magma_cream -minecraft:golden_chestplate -minecraft:iron_sword -minecraft:golden_boots -minecraft:gold_ingot -minecraft:iron_block -minecraft:iron_nugget -minecraft:crossbow -minecraft:golden_helmet -minecraft:crying_obsidian -minecraft:bone_block -minecraft:diamond_shovel -minecraft:chain -minecraft:golden_sword -minecraft:obsidian -minecraft:golden_apple -minecraft:golden_leggings -minecraft:cooked_porkchop -minecraft:arrow -minecraft:piglin_banner_pattern -minecraft:string -minecraft:gilded_blackstone -minecraft:spectral_arrow -minecraft:diamond_pickaxe -minecraft:gold_block \ No newline at end of file diff --git a/lootinator/tests/data/item_map_end_city.txt b/lootinator/tests/data/item_map_end_city.txt deleted file mode 100644 index ab11ef7..0000000 --- a/lootinator/tests/data/item_map_end_city.txt +++ /dev/null @@ -1,24 +0,0 @@ -minecraft:gold_ingot -minecraft:iron_ingot -minecraft:iron_leggings -minecraft:diamond_chestplate -minecraft:spire_armor_trim_smithing_template -minecraft:diamond_leggings -minecraft:iron_shovel -minecraft:saddle -minecraft:diamond_shovel -minecraft:iron_pickaxe -minecraft:golden_horse_armor -minecraft:emerald -minecraft:diamond_helmet -minecraft:iron_sword -minecraft:diamond_sword -minecraft:diamond_pickaxe -minecraft:diamond_horse_armor -minecraft:beetroot_seeds -minecraft:iron_horse_armor -minecraft:iron_chestplate -minecraft:diamond_boots -minecraft:iron_boots -minecraft:diamond -minecraft:iron_helmet \ No newline at end of file diff --git a/lootinator/tests/data/item_map_rp.txt b/lootinator/tests/data/item_map_rp.txt deleted file mode 100644 index 234f1ab..0000000 --- a/lootinator/tests/data/item_map_rp.txt +++ /dev/null @@ -1,26 +0,0 @@ -minecraft:golden_leggings -minecraft:glistering_melon_slice -minecraft:flint_and_steel -minecraft:gold_ingot -minecraft:golden_pickaxe -minecraft:golden_axe -minecraft:golden_carrot -minecraft:iron_nugget -minecraft:enchanted_golden_apple -minecraft:golden_horse_armor -minecraft:golden_helmet -minecraft:golden_boots -minecraft:golden_shovel -minecraft:golden_hoe -minecraft:clock -minecraft:golden_chestplate -minecraft:light_weighted_pressure_plate -minecraft:gold_nugget -minecraft:gold_block -minecraft:flint -minecraft:fire_charge -minecraft:obsidian -minecraft:golden_sword -minecraft:golden_apple -minecraft:bell -minecraft:lodestone \ No newline at end of file diff --git a/lootinator/tests/data/ruined_portal_constraints.json b/lootinator/tests/data/ruined_portal_constraints.json deleted file mode 100644 index 014cede..0000000 --- a/lootinator/tests/data/ruined_portal_constraints.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "item": 2, - "range": { - "min": 56, - "max": 80 - }, - "slot": 0, - "attributes": [] - }, - { - "item": 2, - "range": { - "min": 4, - "max": 10 - }, - "slot": 0, - "attributes": [] - }, - { - "item": 2, - "range": { - "min": 14, - "max": 24 - }, - "slot": 0, - "attributes": [] - } -] \ No newline at end of file diff --git a/lootinator/tests/function_order_dfs.cpp b/lootinator/tests/function_order_dfs.cpp new file mode 100644 index 0000000..c880583 --- /dev/null +++ b/lootinator/tests/function_order_dfs.cpp @@ -0,0 +1,124 @@ +#include + +#include "lootinator/assertions.h" +#include "lootinator/kgen/kernel.hpp" + +int LOOTINATOR_EXTERN tests_function_order_dfs(int argc, char** const argv) { + (void)argc; + (void)argv; + + try { + + kgen::KernelGenConfig conf(mc::VersionRange::MC_1_21_TO_1_21_10, + + // loot table + R"( +{ + "type": "minecraft:chest", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:golden_boots", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_damage" + }, + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:flint", + "weight": 40 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:set_damage" + }, + { + "function": "minecraft:enchant_randomly" + } + ], + "name": "minecraft:iron_helmet", + "weight": 40 + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 10.0, + "min": 4.0 + } + } + ], + "random_sequence": "minecraft:chests/ruined_portal" +} + )", + + // constraints + R"( +[ + { + "item": "minecraft:golden_boots", + "range": { + "min": 10, + "max": 10000 + }, + "slot": 0 + } +] + )", + false, + ""); + + // there should exist a good ordering here + ASSERT_EQ(conf.no_fast_filter, false); + + // the only ordering that works + std::vector target_order({data::LootFunctionType::APPLY_DAMAGE, + data::LootFunctionType::SET_COUNT, + data::LootFunctionType::ENCHANT_RANDOMLY}); + + ASSERT_EQ(target_order.size(), conf.function_order.size()); + + bool target_matches_result = + std::equal(target_order.begin(), target_order.end(), conf.function_order.begin()); + + ASSERT_EQ(target_matches_result, true); + } catch (loot::LootinatorError& err) { + std::cerr << "Caught LootinatorError: " << err.message << '\n'; + return 1; + } catch (std::exception& ex) { + std::cerr << ex.what() << '\n'; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/lootinator/tests/smoke_tests.cpp b/lootinator/tests/smoke_tests.cpp deleted file mode 100644 index 4fc2e4a..0000000 --- a/lootinator/tests/smoke_tests.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "lootinator/assertions.h" - -int LOOTINATOR_EXTERN tests_smoke_tests(int argc, char** const argv) { - (void)argc; - (void)argv; - return 0; -} \ No newline at end of file diff --git a/lootinator/tests/test_kgen_config.cpp b/lootinator/tests/test_kgen_config.cpp new file mode 100644 index 0000000..82438db --- /dev/null +++ b/lootinator/tests/test_kgen_config.cpp @@ -0,0 +1,397 @@ +#include +#include +#include + +#include "lootinator/assertions.h" +#include "lootinator/mc/minecraft.hpp" +#include "lootinator/probability/loot_prob.h" + +int LOOTINATOR_EXTERN tests_test_kgen_config(int argc, char** const argv) { + (void)argc; + (void)argv; + + std::string simple_constraints = R"( + [ + { + "item": "minecraft:obsidian", + "range": { + "min": 7, + "max": 100 + }, + "slot": 0 + } + ] + )"; + + std::string simple_constraints2 = R"( + [ + { + "item": "minecraft:gold_block", + "range": { + "min": 2, + "max": 100 + }, + "slot": 0 + } + ] + )"; + + std::string constraints = R"( + [ + { + "item": "minecraft:golden_sword", + "range": { + "min": 1, + "max": 1 + }, + "slot": 0, + "attributes": [ + {"type": "looting", "level": 3} + ] + }, + { + "item": "minecraft:golden_carrot", + "range": { + "min": 10, + "max": 100 + }, + "slot": 0 + }, + { + "item": "minecraft:gold_block", + "range": { + "min": 4, + "max": 100 + }, + "slot": 0 + }, + { + "item": "minecraft:flint_and_steel", + "range": { + "min": 1, + "max": 1 + }, + "slot": 0 + }, + { + "item": "minecraft:golden_axe", + "range": { + "min": 1, + "max": 1 + }, + "slot": 0, + "attributes": [ + {"type": "efficiency"} + ] + } + ] + )"; + + std::string loot_table = R"( + { + "type": "minecraft:chest", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:obsidian", + "weight": 40 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 4.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:flint", + "weight": 40 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 18.0, + "min": 9.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:iron_nugget", + "weight": 40 + }, + { + "type": "minecraft:item", + "name": "minecraft:flint_and_steel", + "weight": 40 + }, + { + "type": "minecraft:item", + "name": "minecraft:fire_charge", + "weight": 40 + }, + { + "type": "minecraft:item", + "name": "minecraft:golden_apple", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 24.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_nugget", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_sword", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_axe", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_hoe", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_shovel", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_pickaxe", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_boots", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_chestplate", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_helmet", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:enchant_randomly", + "options": "#minecraft:on_random_loot" + } + ], + "name": "minecraft:golden_leggings", + "weight": 15 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 12.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:glistering_melon_slice", + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:golden_horse_armor", + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:light_weighted_pressure_plate", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 12.0, + "min": 4.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:golden_carrot", + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:clock", + "weight": 5 + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 2.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_ingot", + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:bell" + }, + { + "type": "minecraft:item", + "name": "minecraft:enchanted_golden_apple" + }, + { + "type": "minecraft:item", + "functions": [ + { + "add": false, + "count": { + "type": "minecraft:uniform", + "max": 2.0, + "min": 1.0 + }, + "function": "minecraft:set_count" + } + ], + "name": "minecraft:gold_block" + } + ], + "rolls": { + "type": "minecraft:uniform", + "max": 8.0, + "min": 4.0 + } + } + ], + "random_sequence": "minecraft:chests/ruined_portal" + } + )"; + +#define EPS 1e-6 +#define ASSERT_FLOAT_EQ(a, b) ASSERT_LE(fabs((a) - (b)), EPS) + + double config_probability; + + kgen::KernelGenConfig kgen_config = + kgen::KernelGenConfig(mc::VersionRange::MC_1_21_TO_1_21_10, loot_table, constraints, false, ""); + config_probability = prob::get_probability_of_config(kgen_config); + ASSERT_FLOAT_EQ(config_probability, 1.77305e-09); + + kgen_config = kgen::KernelGenConfig( + mc::VersionRange::MC_1_21_TO_1_21_10, loot_table, simple_constraints, true, ""); + config_probability = prob::get_probability_of_config(kgen_config); + ASSERT_FLOAT_EQ(config_probability, 0.000680201); + + kgen_config = kgen::KernelGenConfig( + mc::VersionRange::MC_1_21_TO_1_21_10, loot_table, simple_constraints2, false, ""); + config_probability = prob::get_probability_of_config(kgen_config); + ASSERT_FLOAT_EQ(config_probability, 0.007537); + +#undef EPS +#undef ASSERT_FLOAT_EQ + + return 0; +} \ No newline at end of file diff --git a/web/CMakeLists.txt b/web/CMakeLists.txt new file mode 100644 index 0000000..0dbc830 --- /dev/null +++ b/web/CMakeLists.txt @@ -0,0 +1,27 @@ +if(EMSCRIPTEN) + + add_executable(lootinator_wasm + ${CMAKE_CURRENT_SOURCE_DIR}/lootinator_bindings.cpp + ) + + target_link_libraries(lootinator_wasm PRIVATE lootinator) + + target_include_directories(lootinator_wasm PRIVATE + ${CMAKE_SOURCE_DIR} + ) + + target_link_options(lootinator_wasm PRIVATE + --bind + -sMODULARIZE=1 + -sEXPORT_ES6=1 + -sEXPORT_NAME=Lootinator + -sALLOW_MEMORY_GROWTH=1 + -sENVIRONMENT=web + ) + + set_target_properties(lootinator_wasm PROPERTIES + OUTPUT_NAME "lootinator" + SUFFIX ".js" + ) + +endif() diff --git a/web/lootinator_bindings.cpp b/web/lootinator_bindings.cpp new file mode 100644 index 0000000..8d7ae48 --- /dev/null +++ b/web/lootinator_bindings.cpp @@ -0,0 +1,87 @@ +#include +#include "lootinator/lootinator.h" +#include "lootinator/mc/minecraft.hpp" + +using namespace emscripten; + +namespace { + +val generate_best_pipeline_heur( + std::string loot_table, + std::string constraints, + mc::VersionRange version_range, + bool seedcracking_mode, + std::string output_filename +) { + std::string result; + + loot::LootinatorError err = loot::generate_best_pipeline_heur_from_string( + loot_table, + constraints, + version_range, + seedcracking_mode, + &result, + output_filename + ); + + val error = val::object(); + error.set("kind", err.kind); + error.set("message", err.message); + + val out = val::object(); + out.set("result", result); + out.set("error", error); + + return out; +} + +val generate_benchmark_source( + std::string loot_table, + std::string constraints, + mc::VersionRange version_range, + bool seedcracking_mode, + std::string output_filename +) { + std::string result; + + loot::LootinatorError err = loot::generate_benchmark_source_from_string( + loot_table, + constraints, + version_range, + seedcracking_mode, + &result, + output_filename + ); + + val error = val::object(); + error.set("kind", err.kind); + error.set("message", err.message); + + val out = val::object(); + out.set("result", result); + out.set("error", error); + + return out; +} + +} + +EMSCRIPTEN_BINDINGS(lootinator_module) { + function("generate_best_pipeline_heur", &generate_best_pipeline_heur); + function("generate_benchmark_source", &generate_benchmark_source); + + enum_("LootinatorErrorKind") + .value("SUCCESS", loot::LootinatorErrorKind::SUCCESS) + .value("USER_CONSTRAINT_TOO_WEAK", loot::LootinatorErrorKind::USER_CONSTRAINT_TOO_WEAK) + .value("USER_CONSTRAINT_NOT_POSSIBLE", loot::LootinatorErrorKind::USER_CONSTRAINT_NOT_POSSIBLE) + .value("BAD_CONSTRAINT_FILE", loot::LootinatorErrorKind::BAD_CONSTRAINT_FILE) + .value("BAD_LOOT_TABLE", loot::LootinatorErrorKind::BAD_LOOT_TABLE) + .value("RANGE_PARSE", loot::LootinatorErrorKind::RANGE_PARSE) + .value("INTERNAL_ERROR", loot::LootinatorErrorKind::INTERNAL_ERROR); + + enum_("VersionRange") + #define X(v) .value(#v, mc::v) + VersionRangeList(X) + #undef X + ; +}