diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cef6a14 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +* +!/Cargo.* +!/src +!/crates diff --git a/.github/workflows/MinedMap.yml b/.github/workflows/MinedMap.yml index cf75b68..2aef1ec 100644 --- a/.github/workflows/MinedMap.yml +++ b/.github/workflows/MinedMap.yml @@ -1,5 +1,14 @@ name: 'MinedMap' -on: ['push', 'pull_request', 'workflow_dispatch'] +on: + push: + branches: + - 'main' + tags: + - 'v*' + pull_request: + branches: + - 'main' + workflow_dispatch: {} env: RUSTFLAGS: -Dwarnings @@ -7,7 +16,7 @@ env: jobs: viewer: - runs-on: 'ubuntu-20.04' + runs-on: 'ubuntu-latest' steps: - name: 'Checkout' @@ -25,6 +34,7 @@ jobs: pkgdir='build/pkg/MinedMap-${{ steps.tag.outputs.tag }}-viewer' mkdir -p "$pkgdir" cp -r viewer/* "$pkgdir"/ + rm "$pkgdir"/Dockerfile - name: 'Archive' uses: 'actions/upload-artifact@v4' @@ -101,7 +111,7 @@ jobs: - os: 'windows-2019' target: 'i686-pc-windows-msvc' ext: '.exe' - - os: 'ubuntu-20.04' + - os: 'ubuntu-22.04' target: 'x86_64-unknown-linux-gnu' steps: @@ -138,3 +148,78 @@ jobs: with: name: 'MinedMap-${{ steps.tag.outputs.tag }}-${{ matrix.target }}' path: 'target/pkg' + + build-container: + runs-on: ubuntu-latest + needs: + - test + steps: + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/neocturne/minedmap/minedmap + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=branch,suffix=-{{sha}} + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/setup-buildx-action@v3 + + - name: Build + uses: docker/build-push-action@v6 + with: + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + viewer-container: + runs-on: ubuntu-latest + needs: + - test + steps: + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/neocturne/minedmap/viewer + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=branch,suffix=-{{sha}} + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/setup-buildx-action@v3 + + - name: Build + uses: docker/build-push-action@v6 + with: + context: "{{defaultContext}}:viewer" + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/CHANGELOG.md b/CHANGELOG.md index bec566e..e8fd052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ ### Added +- Added experimental watch mode + + Passing `--watch` will cause MinedMap to run continuously instead of exiting + after map generation, regenerating tiles whenever they change. + + `--watch-delay` can be used to configure the delay between detecting a change + and runing the map generation, also limiting how often the regeneration + happens. This defaults to `30s`; significantly smaller values probably don't + make sense because Minecraft writes out changes in batches anyways. + + Finally, `--jobs-initial` can be used to configure the number of parallel + generation threads for the initial cycle separately from the value used for + subsequent cycles after a change is detected (`-j`/`--jobs`). Subsequent + cycles usually need to regenerate only a small number of tiles, so setting + `--jobs` to a smaller value than `--jobs-initial` may be advantageous. + - Added jemalloc support to fix performace on musl targets The global allocator can be switched to jemalloc by enabling the `jemalloc` diff --git a/Cargo.lock b/Cargo.lock index eeaede2..039e940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -79,7 +79,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -321,7 +321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -345,6 +345,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "flate2" version = "1.0.35" @@ -362,6 +374,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -454,6 +475,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "image" version = "0.25.5" @@ -488,6 +515,26 @@ dependencies = [ "serde", ] +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.8.0", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -518,6 +565,26 @@ dependencies = [ "libc", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -530,6 +597,17 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.8.0", + "libc", + "redox_syscall", +] + [[package]] name = "libz-ng-sys" version = "1.1.21" @@ -588,6 +666,7 @@ dependencies = [ "fastnbt", "futures-util", "git-version", + "humantime", "image", "indexmap", "lru", @@ -595,6 +674,7 @@ dependencies = [ "minedmap-nbt", "minedmap-resource", "minedmap-types", + "notify", "num-integer", "num_cpus", "phf", @@ -656,6 +736,43 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +dependencies = [ + "bitflags 2.8.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.59.0", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -930,7 +1047,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -939,6 +1056,15 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1052,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1171,6 +1297,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -1187,12 +1329,30 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 998de6c..d213a56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ enum-map = "2.7.3" fastnbt = "2.3.2" futures-util = "0.3.28" git-version = "0.3.5" +humantime = "2.1.0" image = { version = "0.25.1", default-features = false, features = ["png", "webp"] } indexmap = { version = "2.0.0", features = ["serde"] } lru = "0.13.0" @@ -51,6 +52,7 @@ minedmap-default-alloc = { version = "0.1.0", path = "crates/default-alloc", opt minedmap-nbt = { version = "0.1.1", path = "crates/nbt", default-features = false } minedmap-resource = { version = "0.6.0", path = "crates/resource" } minedmap-types = { version = "0.1.4", path = "crates/types" } +notify = "8.0.0" num-integer = "0.1.45" num_cpus = "1.16.0" phf = { version = "0.11.2", features = ["macros"] } diff --git a/Dockerfile b/Dockerfile index fa3627c..e052309 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,15 @@ -FROM docker.io/library/rust:alpine AS BUILDER +FROM docker.io/library/alpine:latest AS builder WORKDIR /build -RUN apk update && apk add cmake build-base +RUN apk add --no-cache build-base cmake cargo -COPY src /build/src -COPY crates /build/crates -COPY Cargo.toml Cargo.lock /build +COPY . . RUN cargo build -r +RUN strip target/release/minedmap -FROM scratch AS RUNNER +FROM docker.io/library/alpine:latest -COPY --from=BUILDER /build/target/release/minedmap /minedmap -ENTRYPOINT [ "/minedmap" ] +RUN apk add --no-cache libgcc tini + +COPY --from=builder /build/target/release/minedmap /bin/minedmap +ENTRYPOINT [ "/sbin/tini", "--", "/bin/minedmap" ] diff --git a/src/core/common.rs b/src/core/common.rs index edefcb3..a81dbf2 100644 --- a/src/core/common.rs +++ b/src/core/common.rs @@ -135,10 +135,14 @@ pub enum TileKind { pub struct Config { /// Number of threads for parallel processing pub num_threads: usize, + /// Number of threads for initial parallel processing + pub num_threads_initial: usize, /// Path of input region directory pub region_dir: PathBuf, /// Path of input `level.dat` file pub level_dat_path: PathBuf, + /// Path of input `level.dat_old` file + pub level_dat_old_path: PathBuf, /// Base path for storage of rendered tile data pub output_dir: PathBuf, /// Path for storage of intermediate processed data files @@ -167,9 +171,13 @@ impl Config { Some(threads) => threads, None => 1, }; + let num_threads_initial = args.jobs_initial.unwrap_or(num_threads); let region_dir = [&args.input_dir, Path::new("region")].iter().collect(); let level_dat_path = [&args.input_dir, Path::new("level.dat")].iter().collect(); + let level_dat_old_path = [&args.input_dir, Path::new("level.dat_old")] + .iter() + .collect(); let processed_dir: PathBuf = [&args.output_dir, Path::new("processed")].iter().collect(); let entities_dir: PathBuf = [&processed_dir, Path::new("entities")].iter().collect(); let entities_path_final = [&entities_dir, Path::new("entities.bin")].iter().collect(); @@ -184,8 +192,10 @@ impl Config { Ok(Config { num_threads, + num_threads_initial, region_dir, level_dat_path, + level_dat_old_path, output_dir: args.output_dir.clone(), processed_dir, entities_dir, diff --git a/src/core/metadata_writer.rs b/src/core/metadata_writer.rs index 92d8566..eb5f59f 100644 --- a/src/core/metadata_writer.rs +++ b/src/core/metadata_writer.rs @@ -124,7 +124,14 @@ impl<'a> MetadataWriter<'a> { /// Reads and deserializes the `level.dat` of the Minecraft save data fn read_level_dat(&self) -> Result { - crate::nbt::data::from_file(&self.config.level_dat_path).context("Failed to read level.dat") + let res = crate::nbt::data::from_file(&self.config.level_dat_path); + if res.is_err() { + if let Ok(level_dat_old) = crate::nbt::data::from_file(&self.config.level_dat_old_path) + { + return Ok(level_dat_old); + } + } + res.context("Failed to read level.dat") } /// Generates [Spawn] data from a [de::LevelDat] diff --git a/src/core/mod.rs b/src/core/mod.rs index 5832379..202d017 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -10,7 +10,12 @@ mod tile_merger; mod tile_mipmapper; mod tile_renderer; -use std::path::PathBuf; +use std::{ + path::PathBuf, + sync::mpsc::{self, Receiver}, + thread, + time::Duration, +}; use anyhow::{Context, Result}; use clap::Parser; @@ -18,9 +23,13 @@ use git_version::git_version; use common::{Config, ImageFormat}; use metadata_writer::MetadataWriter; +use notify::{RecommendedWatcher, RecursiveMode, Watcher as _}; +use rayon::ThreadPool; use region_processor::RegionProcessor; use tile_mipmapper::TileMipmapper; use tile_renderer::TileRenderer; +use tokio::runtime::Runtime; +use tracing::{info, warn}; use self::entity_collector::EntityCollector; @@ -44,9 +53,26 @@ pub struct Args { /// use one thread per logical CPU core. #[arg(short, long)] pub jobs: Option, + /// Number of parallel threads to use for initial processing + /// + /// Passing this option only makes sense with --watch. The first run after + /// starting MinedMap will use as many parallel jobs as configured using + /// --job-initial, while subsequent regenerations of tiles will use the + /// the number configured using --jobs. + /// + /// If not given, the value from the --jobs option is used. + #[arg(long)] + pub jobs_initial: Option, /// Enable verbose messages #[arg(short, long)] pub verbose: bool, + /// Watch for file changes and regenerate tiles automatically instead of + /// exiting after generation + #[arg(long)] + pub watch: bool, + /// Minimum delay between map generation cycles in watch mode + #[arg(long, value_parser = humantime::parse_duration, default_value = "30s")] + pub watch_delay: Duration, /// Format of generated map tiles #[arg(long, value_enum, default_value_t)] pub image_format: ImageFormat, @@ -74,14 +100,73 @@ pub struct Args { pub output_dir: PathBuf, } -/// Configures the Rayon thread pool for parallel processing -fn setup_threads(num_threads: usize) -> Result<()> { +/// Configures a Rayon thread pool for parallel processing +fn setup_threads(num_threads: usize) -> Result { rayon::ThreadPoolBuilder::new() .num_threads(num_threads) - .build_global() + .build() .context("Failed to configure thread pool") } +/// Runs all MinedMap generation steps, updating all tiles as needed +fn generate(config: &Config, rt: &Runtime) -> Result<()> { + let regions = RegionProcessor::new(config).run()?; + TileRenderer::new(config, rt, ®ions).run()?; + let tiles = TileMipmapper::new(config, ®ions).run()?; + EntityCollector::new(config, ®ions).run()?; + MetadataWriter::new(config, &tiles).run() +} + +/// Creates a file watcher for the +fn create_watcher(args: &Args) -> Result<(RecommendedWatcher, Receiver<()>)> { + let (tx, rx) = mpsc::sync_channel::<()>(1); + let mut watcher = notify::recommended_watcher(move |res| { + // Ignore errors - we already have a watch trigger queued if try_send() fails + let event: notify::Event = match res { + Ok(event) => event, + Err(err) => { + warn!("Watch error: {err}"); + return; + } + }; + let notify::EventKind::Modify(modify_kind) = event.kind else { + return; + }; + if !matches!( + modify_kind, + notify::event::ModifyKind::Data(_) + | notify::event::ModifyKind::Name(notify::event::RenameMode::To) + ) { + return; + } + if !event + .paths + .iter() + .any(|path| path.ends_with("level.dat") || path.extension() == Some("mcu".as_ref())) + { + return; + } + let _ = tx.try_send(()); + })?; + watcher.watch(&args.input_dir, RecursiveMode::Recursive)?; + Ok((watcher, rx)) +} + +/// Watches the data directory for changes, returning when a change has happened +fn wait_watcher(args: &Args, watch_channel: &Receiver<()>) -> Result<()> { + info!("Watching for changes..."); + let () = watch_channel + .recv() + .context("Failed to read watch event channel")?; + info!("Change detected."); + + thread::sleep(args.watch_delay); + + let _ = watch_channel.try_recv(); + + Ok(()) +} + /// MinedMap CLI main function pub fn cli() -> Result<()> { let args = Args::parse(); @@ -96,17 +181,26 @@ pub fn cli() -> Result<()> { .with_target(false) .init(); - setup_threads(config.num_threads)?; + let mut pool = setup_threads(config.num_threads_initial)?; let rt = tokio::runtime::Builder::new_current_thread() .build() .unwrap(); - let regions = RegionProcessor::new(&config).run()?; - TileRenderer::new(&config, &rt, ®ions).run()?; - let tiles = TileMipmapper::new(&config, ®ions).run()?; - EntityCollector::new(&config, ®ions).run()?; - MetadataWriter::new(&config, &tiles).run()?; + let watch = args.watch.then(|| create_watcher(&args)).transpose()?; - Ok(()) + pool.install(|| generate(&config, &rt))?; + + let Some((_watcher, watch_channel)) = watch else { + // watch mode disabled + return Ok(()); + }; + + if config.num_threads != config.num_threads_initial { + pool = setup_threads(config.num_threads)?; + } + pool.install(move || loop { + wait_watcher(&args, &watch_channel)?; + generate(&config, &rt)?; + }) } diff --git a/src/core/tile_renderer.rs b/src/core/tile_renderer.rs index a972b78..24af234 100644 --- a/src/core/tile_renderer.rs +++ b/src/core/tile_renderer.rs @@ -134,7 +134,7 @@ impl<'a> TileRenderer<'a> { /// Hashing the value as a single u32 is more efficient than hashing /// the tuple elements separately. fn biome_key((dx, dz, index): (i8, i8, u16)) -> u32 { - (dx as u8 as u32) | (dz as u8 as u32) << 8 | (index as u32) << 16 + (dx as u8 as u32) | ((dz as u8 as u32) << 8) | ((index as u32) << 16) } /// One quadrant of the kernel used to smooth biome edges diff --git a/src/util.rs b/src/util.rs index a128ef9..ed07ba5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -39,7 +39,9 @@ pub fn to_flat_coord( chunk: ChunkCoord, block: BlockCoord, ) -> i32 { - (region as i32) << (BLOCK_BITS + CHUNK_BITS) | ((chunk.0 as i32) << BLOCK_BITS | block.0 as i32) + ((region as i32) << (BLOCK_BITS + CHUNK_BITS)) + | ((chunk.0 as i32) << BLOCK_BITS) + | (block.0 as i32) } /// Splits a flat (linear) coordinate into region, chunk and block numbers diff --git a/viewer/.dockerignore b/viewer/.dockerignore new file mode 100644 index 0000000..3af0ccb --- /dev/null +++ b/viewer/.dockerignore @@ -0,0 +1 @@ +/data diff --git a/Dockerfile.viewer b/viewer/Dockerfile similarity index 73% rename from Dockerfile.viewer rename to viewer/Dockerfile index 82d50bb..794bcf5 100644 --- a/Dockerfile.viewer +++ b/viewer/Dockerfile @@ -1,3 +1,3 @@ FROM docker.io/library/nginx:alpine -COPY viewer /usr/share/nginx/html +COPY . /usr/share/nginx/html # datadir should be mounted to: /usr/share/nginx/html/data