From b7a0c951f56d1e7bb6597ac5635d8a13afa24156 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 6 Feb 2024 13:12:08 -0600 Subject: [PATCH] v2 stash (#8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * upd: mcstructure output - Renamed typings to types/index.ts - Added .mcstructure generator - Added flipbook to GIF * upd: Manifest w/ scripts * removed: Pixel art functions * upd: WSS types import * upd: Read file in compiler * upd: Improved structure output - Successful mcstructure output - except it prints image on its side * upd: Upright structure output * upd: Rotate image to display upright * upd: Imports / Server functions * feat: BP script - Updated paths * feat: Model classes * feat: Created test structure * feat: 🍔 - Added burger pixel art * feat: Stable Diffusion support in WSS * fix: Flipbooks & structures * upd: Version change * upd: Build script * wip: Stash! - v2 dead-end code --- .github/workflows/build.yml | 2 +- .github/workflows/docs.yml | 23 + .vscode/launch.json | 10 +- .vscode/settings.json | 3 +- bds.ts | 172 ++ deno.json | 8 +- deno.lock | 412 ++- import_map.json | 22 +- serve.ts | 8 +- server/_router.ts | 2 +- server/_state.ts | 2 +- server/app.ts | 0 server/functions/astral/main.ts | 32 + server/functions/browser.ts | 50 + {src => server}/functions/chatgpt.ts | 2 +- {src => server}/functions/checkerboard.ts | 2 +- {src => server}/functions/circle.ts | 2 +- {src => server}/functions/dalle.ts | 6 +- {src => server}/functions/graph.ts | 2 +- {src => server}/functions/image.ts | 6 +- {src => server}/functions/pyramid.ts | 2 +- {src => server}/functions/sphere.ts | 2 +- server/functions/stableDiffusion.ts | 60 + server/mod.ts | 2 +- server/routes/functions.ts | 6 +- server/routes/log.ts | 2 +- src/_utils.ts | 20 + src/assets/mask/checker_mask.png | Bin 0 -> 1100 bytes src/assets/mask/poly_depth.png | Bin 0 -> 2008 bytes src/assets/mask/poly_depth1.png | Bin 0 -> 3062 bytes src/assets/mask/star_mask.png | Bin 0 -> 1431 bytes src/assets/mask/tunnel_mask.png | Bin 0 -> 1485 bytes src/assets/materials/dot_glowing_mer.png | Bin 540 -> 4155 bytes src/assets/materials/dot_mer.png | Bin 2042 -> 3239 bytes src/assets/materials/dot_multiply.png | Bin 2196 -> 1382 bytes src/assets/materials/dot_normal.png | Bin 2734 -> 6295 bytes src/assets/materials/negative_mer.png | Bin 0 -> 1870 bytes src/assets/materials/negative_normal.png | Bin 0 -> 1256 bytes src/assets/materials/negative_overlay.png | Bin 0 -> 1060 bytes src/assets/materials/positive_dodge.png | Bin 0 -> 1160 bytes src/assets/materials/positive_mer.png | Bin 0 -> 2389 bytes src/assets/materials/positive_normal.png | Bin 0 -> 1412 bytes src/assets/pixel_art/burger_1.png | Bin 0 -> 9150 bytes src/assets/pixel_art/chest.png | Bin 450 -> 0 bytes src/assets/pixel_art/diamond_pickaxe.png | Bin 1511 -> 0 bytes src/assets/pixel_art/floral_1.png | Bin 2632 -> 0 bytes src/assets/pixel_art/floral_3.png | Bin 2569 -> 0 bytes src/assets/pixel_art/nyan.gif | Bin 0 -> 8089 bytes src/assets/pixel_art/pearl_earring.png | Bin 2747 -> 0 bytes src/compile.ts | 187 ++ src/components/BlockEntry.ts | 39 +- src/components/FlipbookEntry.ts | 7 +- src/components/ImagePrinter.ts | 312 +-- src/components/_assemble.ts | 2 +- src/components/_languages.ts | 0 src/components/_render.ts | 3 +- src/components/_scripts.ts | 0 src/components/_server.ts | 27 + src/components/_setup.ts | 151 +- src/components/biomes.ts | 96 + src/components/compile.ts | 43 +- src/components/deferred.ts | 26 + src/components/deploy.ts | 96 +- src/components/editor.ts | 0 src/components/flipbook.ts | 20 +- src/components/manifest.ts | 105 +- src/components/models/index.ts | 26 + src/components/printer.ts | 156 +- src/components/sounds.test.ts | 0 src/components/sounds.ts | 217 ++ src/mod.ts | 304 +-- src/scripts/main.ts | 158 ++ src/store/_blocks.ts | 8 +- src/store/_config.ts | 23 +- src/store/_materials.ts | 136 +- src/store/db.json | 2914 +++++++++++++++++++++ src/versions.json | 4 +- test/jasonjgardner.mcstructure | Bin 0 -> 33984 bytes typings/types.ts => types/index.ts | 8 +- server/types.d.ts => types/server.ts | 0 80 files changed, 5218 insertions(+), 710 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 bds.ts create mode 100644 server/app.ts create mode 100644 server/functions/astral/main.ts create mode 100644 server/functions/browser.ts rename {src => server}/functions/chatgpt.ts (98%) rename {src => server}/functions/checkerboard.ts (95%) rename {src => server}/functions/circle.ts (94%) rename {src => server}/functions/dalle.ts (92%) rename {src => server}/functions/graph.ts (94%) rename {src => server}/functions/image.ts (89%) rename {src => server}/functions/pyramid.ts (95%) rename {src => server}/functions/sphere.ts (94%) create mode 100644 server/functions/stableDiffusion.ts create mode 100644 src/assets/mask/checker_mask.png create mode 100644 src/assets/mask/poly_depth.png create mode 100644 src/assets/mask/poly_depth1.png create mode 100644 src/assets/mask/star_mask.png create mode 100644 src/assets/mask/tunnel_mask.png create mode 100644 src/assets/materials/negative_mer.png create mode 100644 src/assets/materials/negative_normal.png create mode 100644 src/assets/materials/negative_overlay.png create mode 100644 src/assets/materials/positive_dodge.png create mode 100644 src/assets/materials/positive_mer.png create mode 100644 src/assets/materials/positive_normal.png create mode 100644 src/assets/pixel_art/burger_1.png delete mode 100644 src/assets/pixel_art/chest.png delete mode 100644 src/assets/pixel_art/diamond_pickaxe.png delete mode 100644 src/assets/pixel_art/floral_1.png delete mode 100644 src/assets/pixel_art/floral_3.png create mode 100644 src/assets/pixel_art/nyan.gif delete mode 100644 src/assets/pixel_art/pearl_earring.png create mode 100644 src/compile.ts create mode 100644 src/components/_languages.ts create mode 100644 src/components/_scripts.ts create mode 100644 src/components/_server.ts create mode 100644 src/components/biomes.ts create mode 100644 src/components/deferred.ts create mode 100644 src/components/editor.ts create mode 100644 src/components/models/index.ts create mode 100644 src/components/sounds.test.ts create mode 100644 src/components/sounds.ts create mode 100644 src/scripts/main.ts create mode 100644 src/store/db.json create mode 100644 test/jasonjgardner.mcstructure rename typings/types.ts => types/index.ts (95%) rename server/types.d.ts => types/server.ts (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf819530..b47f85b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,6 @@ jobs: - name: Upload docs uses: actions/upload-artifact@v2 with: - name: 🌈.zip + name: docs.zip path: | dist/docs.zip diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..479e0d7d --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,23 @@ +name: Publish Docs + +on: + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v2 + with: + name: docs + path: docs.zip + - uses: actions/checkout@v2 + with: + ref: docs + fetch-depth: 0 + - name: Commit docs + run: | + git config --global user.name "GitHub Actions" + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 4ac70359..86168ebf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,8 +2,16 @@ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", + "version": "0.3.0", "configurations": [ + { + "type": "minecraft-js", + "request": "attach", + "name": "Wait for Minecraft Debug Connections", + "mode": "listen", + "localRoot": "${workspaceFolder}/", + "port": 19144 + }, { "request": "launch", "name": "Build", diff --git a/.vscode/settings.json b/.vscode/settings.json index 01d49031..46764dea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,6 @@ }, "[jsonc]": { "editor.defaultFormatter": "denoland.vscode-deno" - } + }, + "editor.formatOnSave": true } \ No newline at end of file diff --git a/bds.ts b/bds.ts new file mode 100644 index 00000000..bf8fde9a --- /dev/null +++ b/bds.ts @@ -0,0 +1,172 @@ +import { load } from "https://deno.land/std@0.204.0/dotenv/mod.ts"; +import { Application } from "https://deno.land/x/oak/mod.ts"; +import { Image, decode, GIF } from "imagescript/mod.ts"; +import { convertImage } from "./src/components/ImagePrinter.ts"; +import assemble from "./src/components/_assemble.ts"; +import openai from "npm:openai@^3.2.1"; +import { Axis } from "./types/index.ts"; + +const { Configuration, OpenAIApi } = openai; + +const env = await load(); +const OPENAI_API_KEY = env.OPENAI_API_KEY || Deno.env.get("OPENAI_API_KEY") || + ""; + +const configuration = new Configuration({ + apiKey: OPENAI_API_KEY, +}); + +const client = new OpenAIApi(configuration); + +const colorList = [ + "yellow", + "amber", + "orange", + "deep_orange", + "red", + "pink", + "purple", + "deep_purple", + "indigo", + "blue", + "light_blue", + "cyan", + "teal", + "light_green", + "green", + "lime", + ]; + + let lastColor = 0; +let lastFrame = 0; +let lastMaterialIdx = 0; + + const material = ["glass","negative", "glowing", "metallic"]; + // const imageRes = await fetch( + // "https://raw.githubusercontent.com/jasonjgardner/minecraft-rtx-rainbow/main/src/assets/pixel_art/chest.png", + // ); + // const imageData = await imageRes.arrayBuffer(); +const imageData = await Deno.readFile("./src/assets/pixel_art/big.jpeg") + // const decoded = await decode(imageData, false) as GIF; + +const mask = (await Image.decode(await Deno.readFile("./src/assets/mask/tunnel_mask.png"))).invert(); +const depth = (await Image.decode(await Deno.readFile("./src/assets/mask/poly_depth.png"))); +const axises = ["x", "y", "z"]; + +async function printImage() { + const blockLibrary = assemble(["glass_pane", "brick_lit"]).filter((b) => + b.behaviorId.includes(material[lastMaterialIdx]) + ); + + const commands: string[] = []; + + // if (lastFrame >= decoded.length) { + // lastFrame = 0; + // } + + if (lastMaterialIdx >= material.length) { + lastMaterialIdx = 0; + } + + commands.push( + ...convertImage( + // decoded[lastFrame].resize(72, Image.RESIZE_AUTO), + (await decode(imageData, false)).resize(64, Image.RESIZE_AUTO) as Image, + blockLibrary, + [0, 0, 20], + axises[lastFrame % 3] as Axis, + undefined, + mask, + ), + ); + + lastFrame += 1; + lastMaterialIdx += 1; + + return commands; +} + +const app = new Application(); + +function getBlockName(color: string, material: string, level = 50, tint = 500) { + return `rainbow:${color}_${tint}_${material}_${level}`; +} + +function getBlocks(tint = 500) { + const colors = [ + "yellow", + "amber", + "orange", + "deep_orange", + "red", + "pink", + "purple", + "deep_purple", + "indigo", + "blue", + "light_blue", + "cyan", + "teal", + "light_green", + "green", + "lime", + ]; + + const materials = ["glowing"]; + const levels = [25]; + + const blockList = []; + + for (const color of colors) { + for (const material of materials) { + for (const level of levels) { + blockList.push(getBlockName(color, material, level, tint)); + } + } + } + + return blockList; +} + +let cameraVector = [10, 20, 10]; + +app.use(async (ctx) => { + if (ctx.request.method !== "GET") { + + console.log(ctx.request) + } + + ctx.response.headers.set("Content-Type", "text/plain"); + + if (lastColor >= colorList.length) { + lastColor = 0; + } + + if (lastMaterialIdx >= material.length) { + lastMaterialIdx = 0; + } + + const lines = [ + // ...await printImage(), + // '/setblock ~ ~9 ~ redstone_block', + // '/setblock ~ ~10 ~ tnt', + `/setblock ~ ~-1 ~ ${getBlockName(colorList[lastColor], material[lastMaterialIdx], 50, 500)}`, + `/execute at @e[type=!player] as @e[r=5] at @s run setblock ~ ~-1 ~ ${getBlockName(colorList[lastColor], material[lastMaterialIdx], 50, 800)}`, + `/execute at @e[type=!player] as @e[r=5] at @s run fill ~-4 ~-1 ~3 ~4 ~-1 ~5 ${getBlockName(colorList[lastColor], material[lastMaterialIdx], 50, 200)}`, + ] + + + // Update camera vector to move camera in circle around player + // cameraVector = [ + // Math.sin(Date.now() / 1000) * 5, + // Math.sin(Date.now() / 1000) * 5, + // Math.cos(Date.now() / 1000) * 5, + // ] + + lastColor += 1; + lastMaterialIdx += 1; + + ctx.response.body = JSON.stringify({ command: lines.join(";"), target: "eljaysun", tick: 5 }); +}); + +await app.listen({ port: 8000 }); diff --git a/deno.json b/deno.json index 2c651448..e505d2de 100644 --- a/deno.json +++ b/deno.json @@ -1,9 +1,11 @@ { "tasks": { - "test": "deno run --allow-read --allow-net --allow-env --allow-write --inspect ./src/mod.ts --DEPLOY true", - "build": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts", + "test": "deno run -A --unstable ./src/mod.ts --DEPLOY true", + "test:preview": "deno run --allow-read --allow-net --allow-env --allow-write --allow-run --inspect ./src/mod.ts --DEPLOY true --PREVIEW true", + "build": "deno run -A --unstable ./src/mod.ts", "art": "deno run --allow-read --allow-net --allow-env --allow-write ./src/mod.ts --ART_DIR ./px", - "serve": "deno run --allow-read --allow-net --allow-env --allow-write --unstable --allow-ffi ./serve.ts" + "serve": "deno run -A --unstable ./serve.ts", + "build:data": "gh release download --skip-existing -D ./cache -A zip -R Mojang/bedrock-samples -O bedrock-samples.zip && Expand-Archive -Path ./cache/bedrock-samples.zip -D ./cache" }, "fmt": { "files": { diff --git a/deno.lock b/deno.lock index 09e1634f..b2b02611 100644 --- a/deno.lock +++ b/deno.lock @@ -1,5 +1,79 @@ { - "version": "2", + "version": "3", + "packages": { + "specifiers": { + "npm:@minecraft/server-net": "npm:@minecraft/server-net@1.0.0-beta.11940b24", + "npm:openai": "npm:openai@3.2.1", + "npm:openai@^3.2.1": "npm:openai@3.2.1" + }, + "npm": { + "@minecraft/server-admin@1.0.0-beta.11940b24": { + "integrity": "sha512-UZ8YyB8mvF31f22+uM67gJums9d6NDgz9AQ2l4a++EHUP9uen0m3aaBaqM9RyumJpRBJbv/hslAKOyeDT01qew==", + "dependencies": {} + }, + "@minecraft/server-net@1.0.0-beta.11940b24": { + "integrity": "sha512-jiL03mGNpADCfvPDyydheEAhdi7J++nx5nB7Gyu3GImDlV5pv9uTwKCrnmILrLs4qJTtxhuRLKBKRlVyYDpKMw==", + "dependencies": { + "@minecraft/server-admin": "@minecraft/server-admin@1.0.0-beta.11940b24" + } + }, + "asynckit@0.4.0": { + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dependencies": {} + }, + "axios@0.26.1": { + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "follow-redirects@1.15.2" + } + }, + "combined-stream@1.0.8": { + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "delayed-stream@1.0.0" + } + }, + "delayed-stream@1.0.0": { + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dependencies": {} + }, + "follow-redirects@1.15.2": { + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dependencies": {} + }, + "form-data@4.0.0": { + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "asynckit@0.4.0", + "combined-stream": "combined-stream@1.0.8", + "mime-types": "mime-types@2.1.35" + } + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dependencies": {} + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "mime-db@1.52.0" + } + }, + "openai@3.2.1": { + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", + "dependencies": { + "axios": "axios@0.26.1", + "form-data": "form-data@4.0.0" + } + } + } + }, + "redirects": { + "https://crux.land/3RdawE": "https://crux.land/api/get/3RdawE.ts", + "https://deno.land/x/astral/mod.ts": "https://deno.land/x/astral@0.3.0/mod.ts", + "https://deno.land/x/imagescript/mod.ts": "https://deno.land/x/imagescript@1.2.9/mod.ts", + "https://deno.land/x/oak/mod.ts": "https://deno.land/x/oak@v10.4.0/mod.ts" + }, "remote": { "https://cdn.jsdelivr.net/npm/nbtify/dist/compression.js": "525ccd73049b064144723d6342ce939c922c673e599ffbd1010e8fb420c68206", "https://cdn.jsdelivr.net/npm/nbtify/dist/data.js": "9f2a21b233ceea61b064cb6083f8524225d3edee30014d2413eebcfd0b1ebcfd", @@ -38,6 +112,25 @@ "https://deno.land/std@0.122.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2", "https://deno.land/std@0.122.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", "https://deno.land/std@0.122.0/path/win32.ts": "11549e8c6df8307a8efcfa47ad7b2a75da743eac7d4c89c9723a944661c8bd2e", + "https://deno.land/std@0.126.0/_util/assert.ts": "6396c1bd0361c4939e7f32f9b03efffcd04b640a1b206ed67058553d6cb59cc4", + "https://deno.land/std@0.126.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", + "https://deno.land/std@0.126.0/bytes/bytes_list.ts": "cfe63225fc153a1ae8f18c1d68d979e131906487664719d92771642b6587b11c", + "https://deno.land/std@0.126.0/bytes/equals.ts": "6f38a72d4a75a1232fe984e5fb362b962c2e9bec6385f4ca13252e1ca53dba00", + "https://deno.land/std@0.126.0/bytes/mod.ts": "ed5c42feb746c3709d186787346004f97301dc076b33434653e451efd8efcf6a", + "https://deno.land/std@0.126.0/encoding/base64.ts": "784384e3cc39c59534d15f71e87f5b8da73cc5e1460417eb32ec9d62617d4f87", + "https://deno.land/std@0.126.0/http/http_status.ts": "3e0b5615881917ad82bddf5395fbab6a82d2d20c1d77e98089513a26e3257b2c", + "https://deno.land/std@0.126.0/io/buffer.ts": "09e3111ebf8f354afcc6cdf9697f21167e8289f57bf753650f68b231f4782187", + "https://deno.land/std@0.126.0/io/readers.ts": "679471f3b9929b54393c9cd75b6bd178b4bc6d9aab5c0f1f9538f862cf4746fe", + "https://deno.land/std@0.126.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.126.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.126.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", + "https://deno.land/std@0.126.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.126.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.126.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", + "https://deno.land/std@0.126.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", + "https://deno.land/std@0.126.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.126.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", + "https://deno.land/std@0.126.0/streams/conversion.ts": "fec481215c9c376047e7cc4251933c37f00708b2a582ee39b320c6e0a7e1e379", "https://deno.land/std@0.147.0/_deno_unstable.ts": "be3276fd42cffb49f51b705c4b0aa8656aaf2a34be22d769455c8e50ea38e51a", "https://deno.land/std@0.147.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", "https://deno.land/std@0.147.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49", @@ -114,6 +207,13 @@ "https://deno.land/std@0.155.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", "https://deno.land/std@0.155.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", "https://deno.land/std@0.155.0/testing/asserts.ts": "ac295f7fd22a7af107580e2475402a8c386cb1bf18bf837ae266ac0665786026", + "https://deno.land/std@0.162.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.162.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4", + "https://deno.land/std@0.162.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a", + "https://deno.land/std@0.162.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179", + "https://deno.land/std@0.162.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", + "https://deno.land/std@0.162.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289", + "https://deno.land/std@0.162.0/streams/conversion.ts": "555c6c249f3acf85655f2d0af52d1cb3168e40b1c1fa26beefea501b333abe28", "https://deno.land/std@0.164.0/_util/asserts.ts": "d0844e9b62510f89ce1f9878b046f6a57bf88f208a10304aab50efcb48365272", "https://deno.land/std@0.164.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", "https://deno.land/std@0.164.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", @@ -200,6 +300,156 @@ "https://deno.land/std@0.184.0/collections/filter_values.ts": "5b9feaf17b9a6e5ffccdd36cf6f38fa4ffa94cff2602d381c2ad0c2a97929652", "https://deno.land/std@0.184.0/collections/without_all.ts": "a89f5da0b5830defed4f59666e188df411d8fece35a5f6ca69be6ca71a95c185", "https://deno.land/std@0.184.0/dotenv/mod.ts": "e4c3cdbe11f05340e1217a4ebe2e6f2eef49e11c8d7d2595bb977923a396e18b", + "https://deno.land/std@0.198.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.198.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.198.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.198.0/async/abortable.ts": "fd682fa46f3b7b16b4606a5ab52a7ce309434b76f820d3221bdfb862719a15d7", + "https://deno.land/std@0.198.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", + "https://deno.land/std@0.198.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", + "https://deno.land/std@0.198.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", + "https://deno.land/std@0.198.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659", + "https://deno.land/std@0.198.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", + "https://deno.land/std@0.198.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", + "https://deno.land/std@0.198.0/async/pool.ts": "f1b8d3df4d7fd3c73f8cbc91cc2e8b8e950910f1eab94230b443944d7584c657", + "https://deno.land/std@0.198.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e", + "https://deno.land/std@0.198.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", + "https://deno.land/std@0.198.0/fmt/printf.ts": "d3cf1bcc6af4d1ab04747a99f22845abfd0c7ec3cfcc72cfc2b544128a821a12", + "https://deno.land/std@0.198.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978", + "https://deno.land/std@0.198.0/fs/copy.ts": "b4f7fe87190d7b310c88a2d9ff845210c0a2b7b0a094ec509747359023beb7d6", + "https://deno.land/std@0.198.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.198.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.198.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", + "https://deno.land/std@0.198.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", + "https://deno.land/std@0.198.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", + "https://deno.land/std@0.198.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", + "https://deno.land/std@0.198.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", + "https://deno.land/std@0.198.0/fs/expand_glob.ts": "3e427436f4b3768727bd7de84169f10db75fe50b32e6dde567b8ae558a8d857a", + "https://deno.land/std@0.198.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", + "https://deno.land/std@0.198.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", + "https://deno.land/std@0.198.0/fs/walk.ts": "21a3cc5ff39c38acc93575213f54d5f1d44c5c6614ed97603d171eb0bf56a565", + "https://deno.land/std@0.198.0/http/server.ts": "3cd5a8acc31d3259813ddcfa7dd2fe066952db6763fc7a58b9a026d4b5af9559", + "https://deno.land/std@0.198.0/json/common.ts": "ecd5e87d45b5f0df33238ed8b1746e1444da7f5c86ae53d0f0b04280f41a25bb", + "https://deno.land/std@0.198.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343", + "https://deno.land/std@0.198.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.198.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395", + "https://deno.land/std@0.198.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664", + "https://deno.land/std@0.198.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4", + "https://deno.land/std@0.198.0/path/_from_file_url.ts": "7e4e5626089785adddb061f1b9f4932d6b21c7df778e7449531a11e32048245c", + "https://deno.land/std@0.198.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.198.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c", + "https://deno.land/std@0.198.0/path/_join.ts": "fd78555bc34d5f188918fc7018dfe8fe2df5bbad94a3b30a433666c03934d77f", + "https://deno.land/std@0.198.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", + "https://deno.land/std@0.198.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa", + "https://deno.land/std@0.198.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", + "https://deno.land/std@0.198.0/path/_resolve.ts": "3bf0287d62488cad08c3c219a9708c4a4c658c65d7b4400fd99afdc3ba10a64d", + "https://deno.land/std@0.198.0/path/_to_file_url.ts": "739bfda583598790b2e77ce227f2bb618f6ebdb939788cea47555b43970ec58c", + "https://deno.land/std@0.198.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8", + "https://deno.land/std@0.198.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.198.0/path/basename.ts": "6f08fbb90dbfcf320765b3abb01f995b1723f75e2534acfd5380e202c802a3aa", + "https://deno.land/std@0.198.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.198.0/path/dirname.ts": "098996822a31b4c46e1eb52a19540d3c6f9f54b772fc8a197939eeabc29fca2f", + "https://deno.land/std@0.198.0/path/extname.ts": "9b83c62fd16505739541f7a3ab447d8972da39dbf668d47af2f93206c2480893", + "https://deno.land/std@0.198.0/path/format.ts": "cb22f95cc7853d590b87708cc9441785e760d711188facff3d225305a8213aca", + "https://deno.land/std@0.198.0/path/from_file_url.ts": "a6221cfc928928ec4d9786d767dfac98fa2ab746af0786446c9834a07b98817e", + "https://deno.land/std@0.198.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.198.0/path/is_absolute.ts": "6b3d36352eb7fa29edb53f9e7b09b1aeb022a3c5465764f6cc5b8c41f9736197", + "https://deno.land/std@0.198.0/path/join.ts": "4a2867ff2f3c81ffc9eb3d56dade16db6f8bd3854f269306d23dad4115089c84", + "https://deno.land/std@0.198.0/path/mod.ts": "7765507696cb321994cdacfc19ee3ba61e8e3ebf4bd98fa75a276cf5dc18ce2a", + "https://deno.land/std@0.198.0/path/normalize.ts": "7d992cd262b2deefa842d93a8ba2ed51f3949ba595b1d07f627ac2cddbc74808", + "https://deno.land/std@0.198.0/path/parse.ts": "031fe488b3497fb8312fc1dc3c3d6c2d80707edd9c661e18ee9fd20f95edf322", + "https://deno.land/std@0.198.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b", + "https://deno.land/std@0.198.0/path/relative.ts": "7db80c5035016174267da16321a742d76e875215c317859a383b12f413c6f5d6", + "https://deno.land/std@0.198.0/path/resolve.ts": "103b62207726a27f28177f397008545804ecb20aaf00623af1f622b18cd80b9f", + "https://deno.land/std@0.198.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.198.0/path/to_file_url.ts": "dd32f7a01bbf3b15b5df46796659984b372973d9b2d7d59bcf0eb990763a0cb5", + "https://deno.land/std@0.198.0/path/to_namespaced_path.ts": "4e643ab729bf49ccdc166ad48d2de262ff462938fcf2a44a4425588f4a0bd690", + "https://deno.land/std@0.198.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", + "https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.200.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.200.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.200.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343", + "https://deno.land/std@0.200.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.200.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395", + "https://deno.land/std@0.200.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664", + "https://deno.land/std@0.200.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4", + "https://deno.land/std@0.200.0/path/_from_file_url.ts": "7e4e5626089785adddb061f1b9f4932d6b21c7df778e7449531a11e32048245c", + "https://deno.land/std@0.200.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.200.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c", + "https://deno.land/std@0.200.0/path/_join.ts": "fd78555bc34d5f188918fc7018dfe8fe2df5bbad94a3b30a433666c03934d77f", + "https://deno.land/std@0.200.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", + "https://deno.land/std@0.200.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa", + "https://deno.land/std@0.200.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", + "https://deno.land/std@0.200.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6", + "https://deno.land/std@0.200.0/path/_to_file_url.ts": "739bfda583598790b2e77ce227f2bb618f6ebdb939788cea47555b43970ec58c", + "https://deno.land/std@0.200.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8", + "https://deno.land/std@0.200.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.200.0/path/basename.ts": "6f08fbb90dbfcf320765b3abb01f995b1723f75e2534acfd5380e202c802a3aa", + "https://deno.land/std@0.200.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.200.0/path/dirname.ts": "098996822a31b4c46e1eb52a19540d3c6f9f54b772fc8a197939eeabc29fca2f", + "https://deno.land/std@0.200.0/path/extname.ts": "9b83c62fd16505739541f7a3ab447d8972da39dbf668d47af2f93206c2480893", + "https://deno.land/std@0.200.0/path/format.ts": "cb22f95cc7853d590b87708cc9441785e760d711188facff3d225305a8213aca", + "https://deno.land/std@0.200.0/path/from_file_url.ts": "a6221cfc928928ec4d9786d767dfac98fa2ab746af0786446c9834a07b98817e", + "https://deno.land/std@0.200.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.200.0/path/is_absolute.ts": "6b3d36352eb7fa29edb53f9e7b09b1aeb022a3c5465764f6cc5b8c41f9736197", + "https://deno.land/std@0.200.0/path/join.ts": "4a2867ff2f3c81ffc9eb3d56dade16db6f8bd3854f269306d23dad4115089c84", + "https://deno.land/std@0.200.0/path/mod.ts": "7765507696cb321994cdacfc19ee3ba61e8e3ebf4bd98fa75a276cf5dc18ce2a", + "https://deno.land/std@0.200.0/path/normalize.ts": "7d992cd262b2deefa842d93a8ba2ed51f3949ba595b1d07f627ac2cddbc74808", + "https://deno.land/std@0.200.0/path/parse.ts": "031fe488b3497fb8312fc1dc3c3d6c2d80707edd9c661e18ee9fd20f95edf322", + "https://deno.land/std@0.200.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b", + "https://deno.land/std@0.200.0/path/relative.ts": "7db80c5035016174267da16321a742d76e875215c317859a383b12f413c6f5d6", + "https://deno.land/std@0.200.0/path/resolve.ts": "103b62207726a27f28177f397008545804ecb20aaf00623af1f622b18cd80b9f", + "https://deno.land/std@0.200.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.200.0/path/to_file_url.ts": "dd32f7a01bbf3b15b5df46796659984b372973d9b2d7d59bcf0eb990763a0cb5", + "https://deno.land/std@0.200.0/path/to_namespaced_path.ts": "4e643ab729bf49ccdc166ad48d2de262ff462938fcf2a44a4425588f4a0bd690", + "https://deno.land/std@0.200.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", + "https://deno.land/std@0.201.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.201.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.201.0/async/deadline.ts": "58f72a3cc0fcb731b2cc055ba046f4b5be3349ff6bf98f2e793c3b969354aab2", + "https://deno.land/std@0.201.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659", + "https://deno.land/std@0.201.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e", + "https://deno.land/std@0.201.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978", + "https://deno.land/std@0.201.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.201.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343", + "https://deno.land/std@0.201.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.201.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395", + "https://deno.land/std@0.201.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664", + "https://deno.land/std@0.201.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4", + "https://deno.land/std@0.201.0/path/_from_file_url.ts": "6eadfae2e6f63ad9ee46b26db4a1b16583055c0392acedfb50ed2fc694b6f581", + "https://deno.land/std@0.201.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.201.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c", + "https://deno.land/std@0.201.0/path/_join.ts": "815f5e85b042285175b1492dd5781240ce126c23bd97bad6b8211fe7129c538e", + "https://deno.land/std@0.201.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", + "https://deno.land/std@0.201.0/path/_os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.201.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa", + "https://deno.land/std@0.201.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", + "https://deno.land/std@0.201.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6", + "https://deno.land/std@0.201.0/path/_to_file_url.ts": "a141e4a525303e1a3a0c0571fd024552b5f3553a2af7d75d1ff3a503dcbb66d8", + "https://deno.land/std@0.201.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8", + "https://deno.land/std@0.201.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.201.0/path/basename.ts": "bdfa5a624c6a45564dc6758ef2077f2822978a6dbe77b0a3514f7d1f81362930", + "https://deno.land/std@0.201.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.201.0/path/dirname.ts": "b6533f4ee4174a526dec50c279534df5345836dfdc15318400b08c62a62a39dd", + "https://deno.land/std@0.201.0/path/extname.ts": "62c4b376300795342fe1e4746c0de518b4dc9c4b0b4617bfee62a2973a9555cf", + "https://deno.land/std@0.201.0/path/format.ts": "110270b238514dd68455a4c54956215a1aff7e37e22e4427b7771cefe1920aa5", + "https://deno.land/std@0.201.0/path/from_file_url.ts": "9f5cb58d58be14c775ec2e57fc70029ac8b17ed3bd7fe93e475b07280adde0ac", + "https://deno.land/std@0.201.0/path/glob.ts": "593e2c3573883225c25c5a21aaa8e9382a696b8e175ea20a3b6a1471ad17aaed", + "https://deno.land/std@0.201.0/path/is_absolute.ts": "0b92eb35a0a8780e9f16f16bb23655b67dace6a8e0d92d42039e518ee38103c1", + "https://deno.land/std@0.201.0/path/join.ts": "31c5419f23d91655b08ec7aec403f4e4cd1a63d39e28f6e42642ea207c2734f8", + "https://deno.land/std@0.201.0/path/mod.ts": "6e1efb0b13121463aedb53ea51dabf5639a3172ab58c89900bbb72b486872532", + "https://deno.land/std@0.201.0/path/normalize.ts": "6ea523e0040979dd7ae2f1be5bf2083941881a252554c0f32566a18b03021955", + "https://deno.land/std@0.201.0/path/parse.ts": "be8de342bb9e1924d78dc4d93c45215c152db7bf738ec32475560424b119b394", + "https://deno.land/std@0.201.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b", + "https://deno.land/std@0.201.0/path/relative.ts": "8bedac226afd360afc45d451a6c29fabceaf32978526bcb38e0c852661f66c61", + "https://deno.land/std@0.201.0/path/resolve.ts": "133161e4949fc97f9ca67988d51376b0f5eef8968a6372325ab84d39d30b80dc", + "https://deno.land/std@0.201.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.201.0/path/to_file_url.ts": "00e6322373dd51ad109956b775e4e72e5f9fa68ce2c6b04e4af2a6eed3825d31", + "https://deno.land/std@0.201.0/path/to_namespaced_path.ts": "1b1db3055c343ab389901adfbda34e82b7386bcd1c744d54f9c1496ee0fd0c3d", + "https://deno.land/std@0.201.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", + "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.203.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e", + "https://deno.land/std@0.203.0/fs/exists.ts": "cb59a853d84871d87acab0e7936a4dac11282957f8e195102c5a7acb42546bb8", + "https://deno.land/std@0.204.0/dotenv/mod.ts": "1da8c6d0e7f7d8a5c2b19400b763bc11739df24acec235dda7ea2cfd3d300057", "https://deno.land/std@0.97.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", "https://deno.land/std@0.97.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7", "https://deno.land/std@0.97.0/encoding/base64.ts": "eecae390f1f1d1cae6f6c6d732ede5276bf4b9cd29b1d281678c054dc5cc009e", @@ -220,6 +470,18 @@ "https://deno.land/std@0.97.0/path/posix.ts": "f56c3c99feb47f30a40ce9d252ef6f00296fa7c0fcb6dd81211bdb3b8b99ca3b", "https://deno.land/std@0.97.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", "https://deno.land/std@0.97.0/path/win32.ts": "77f7b3604e0de40f3a7c698e8a79e7f601dc187035a1c21cb1e596666ce112f8", + "https://deno.land/x/astral@0.3.0/bindings/celestial.ts": "cd00b4a4af9c5860848251b5a4d69e5af48e3dfdce47bc07644874b74cdb03b8", + "https://deno.land/x/astral@0.3.0/mod.ts": "c07ced6f4434b839ff4ab90e7eec00da31e31c593ae8938f37a9e87e217f16d4", + "https://deno.land/x/astral@0.3.0/src/browser.ts": "b679fe90fcdfc98f57a8735b971eb7d8db5edf1816394b7d1c5490123690548a", + "https://deno.land/x/astral@0.3.0/src/cache.ts": "d3a4f839f77ff7e3003c42debd9258dcc0a93c78de9f0399d329f5bd5fb99bbe", + "https://deno.land/x/astral@0.3.0/src/dialog.ts": "2b198b672d88c14ca33ee70d8f959a91ccc70375b71c4bc16b1a24bbdc6396d7", + "https://deno.land/x/astral@0.3.0/src/element_handle.ts": "59bdea334dab31788f568ec993971cbb130ce28c2d799f758ac1dd5848ef35d5", + "https://deno.land/x/astral@0.3.0/src/file_chooser.ts": "4ea82e3e14393c2aea469e95cf6920ac97f7d5cbd0c12cf30440241f632ad642", + "https://deno.land/x/astral@0.3.0/src/keyboard.ts": "84b65bd9ae7594c61a7f7952857d70caa0f3d60250da989fce31c54390cb8f53", + "https://deno.land/x/astral@0.3.0/src/mouse.ts": "9d3eb409afea003a54ceb42f29b61ec56854c7528c5761a494ea8b095ae8af8d", + "https://deno.land/x/astral@0.3.0/src/page.ts": "4c28f2fe905747d44c7e146dbead9b48563b3a1e884e817f22e826c2907324eb", + "https://deno.land/x/astral@0.3.0/src/touchscreen.ts": "ceca95f3a790ea71505d79bae51c4300d4b6424af2b41f4012270f955a892500", + "https://deno.land/x/astral@0.3.0/src/util.ts": "a7e307ea7e569b8536d64f1141597a79b11fded95367a0cdaa579f70e537d3ca", "https://deno.land/x/cache@0.2.13/cache.ts": "4005aad54fb9aac9ff02526ffa798032e57f2d7966905fdeb7949263b1c95f2f", "https://deno.land/x/cache@0.2.13/deps.ts": "6f14e76a1a09f329e3f3830c6e72bd10b53a89a75769d5ea886e5d8603e503e6", "https://deno.land/x/cache@0.2.13/directories.ts": "ef48531cab3f827252e248596d15cede0de179a2fb15392ae24cf8034519994f", @@ -236,6 +498,14 @@ "https://deno.land/x/canvas@v1.4.1/src/lib.js": "bb21711589bfbc8997b455cdf53e3150e23289f3b44809188041b1d2fc7924fa", "https://deno.land/x/canvas@v1.4.1/src/types.ts": "67d5800f8f4b0a407e0251676a03ae91b5f50a3ed53e6b72dc5984113cb93128", "https://deno.land/x/canvas@v1.4.1/src/wasm.js": "449d72cc14fc4142a5853f944df49a744d852981d09c5515528ede8aebb0afda", + "https://deno.land/x/case@2.2.0/lowerCase.ts": "d75eb55cadfa589f9f2a973924a8a209054477d9574da669410f4d817ab25b41", + "https://deno.land/x/case@2.2.0/normalCase.ts": "085c8b6f9d69283c8b86f2e504d43278c2be8b7e56a3ed8d4a5f395e398bdc29", + "https://deno.land/x/case@2.2.0/titleCase.ts": "c287131ea2c955e67cdd5cf604de96d31a8e2813305759922b9ed27e3be354e7", + "https://deno.land/x/case@2.2.0/types.ts": "8e2bd6edaa27c0d1972c0d5b76698564740f37b4d3787d58d1fb5f48de611e61", + "https://deno.land/x/case@2.2.0/upperCase.ts": "6cca267bb04d098bf4abf21e42e60c3e68ede89b12e525643c6b6eff3e10de34", + "https://deno.land/x/case@2.2.0/vendor/camelCaseRegexp.ts": "7d9ff02aad4ab6429eeab7c7353f7bcdd6cc5909a8bd3dda97918c8bbb7621ae", + "https://deno.land/x/case@2.2.0/vendor/camelCaseUpperRegexp.ts": "292de54a698370f90adcdf95727993d09888b7f33d17f72f8e54ba75f7791787", + "https://deno.land/x/case@2.2.0/vendor/nonWordRegexp.ts": "c1a052629a694144b48c66b0175a22a83f4d61cb40f4e45293fc5d6b123f927e", "https://deno.land/x/case@v2.1.0/lowerCase.ts": "86d5533f9587ed60003181591e40e648838c23f371edfa79d00288153d113b16", "https://deno.land/x/case@v2.1.0/normalCase.ts": "6a8b924da9ab0790d99233ae54bfcfc996d229cb91b2533639fe20972cc33dac", "https://deno.land/x/case@v2.1.0/titleCase.ts": "36d3fc73df58712240a74b04d84191ac22dd2866bef3838b02157f8e46cb0ecb", @@ -429,9 +699,57 @@ "https://deno.land/x/imagescript@1.2.15/v2/ops/resize.mjs": "814e78ebce8eaf8f1f918688db7b52a141405e06a36ed4b25d04413d69e7d17b", "https://deno.land/x/imagescript@1.2.15/v2/ops/rotate.mjs": "a1b65616717bd2eed8db406affea3263b4674dada46b56441ef38167a187455d", "https://deno.land/x/imagescript@1.2.15/v2/util/mem.mjs": "4968d400dae069b4bf0ef4767c1802fd2cc7d15d90eda4cfadf5b4cd19b96c6d", + "https://deno.land/x/imagescript@1.2.9/ImageScript.js": "4f6f9c1542dd55b91dfdd01d1b2e8e5ff1eb3b3a7a4a30bdd5ff673d9aecab45", + "https://deno.land/x/imagescript@1.2.9/mod.ts": "18a6cb83c55e690c873505f6fe867364c678afb64934fe7aef593a6b92f79995", + "https://deno.land/x/imagescript@1.2.9/utils/buffer.js": "952cb1beb8827e50a493a5d1f29a4845e8c648789406d389dd51f51205ba02d8", + "https://deno.land/x/imagescript@1.2.9/utils/crc32.js": "573d6222b3605890714ebc374e687ec2aa3e9a949223ea199483e47ca4864f7d", + "https://deno.land/x/imagescript@1.2.9/utils/png.js": "fbed9117e0a70602645d70df9c103ff6e79c03e987bd5c1685dcb4200729b6de", + "https://deno.land/x/imagescript@1.2.9/utils/wasm/font.js": "ae4e8b7b6e6e9a30250c5f89e8fbb92d237f69d80ff84cd80c95d9a04163e714", + "https://deno.land/x/imagescript@1.2.9/utils/wasm/gif.js": "100fbd424a828a76ce90e4f5c282d42e0ab83518908a75839d5ded52bb795340", + "https://deno.land/x/imagescript@1.2.9/utils/wasm/jpeg.js": "da660e827fa45a85ee63aafa38dceb031dbd8988162bf730ace8d6135d842413", + "https://deno.land/x/imagescript@1.2.9/utils/wasm/svg.js": "f7d8827fa8dc4aa9b1027374a87ae658da4bdd46048c66c01d909291d1cf132a", + "https://deno.land/x/imagescript@1.2.9/utils/wasm/zlib.js": "63060a9aabee087929eb01d9e8b75b3ddc94a846a929a81d4456ed9b54d72671", + "https://deno.land/x/media_types@v2.12.2/db.ts": "92e5f1735fd5896382e2cc6e94518cb57608777e2ac61c5f7bc1874b50df6f4d", + "https://deno.land/x/media_types@v2.12.2/mod.ts": "e1efe50915092e680243f0f2709e3ac9d2c3c8bd94c40619d810e9ce075ea183", + "https://deno.land/x/oak@v10.4.0/application.ts": "b5a2e9b83fb5d5d5aa166176cf9b40e38b4a531f3f7d8eb080c2eb21b314fe62", + "https://deno.land/x/oak@v10.4.0/body.ts": "80ef7b8e7bb6bd7907ecdfb9148bffdbde10f9d3be9336d84075038473f804a1", + "https://deno.land/x/oak@v10.4.0/buf_reader.ts": "7cf96aa0ac670b75098113cf88a291a68332cc45efa8a9698f064ac5b8098a0f", + "https://deno.land/x/oak@v10.4.0/content_disposition.ts": "8b8c3cb2fba7138cd5b7f82fc3b5ea39b33db924a824b28261659db7e164621e", + "https://deno.land/x/oak@v10.4.0/context.ts": "078306333a79f1010a49d5e9a05fca3dbc0c18fbda7610fa05400fa2e6eb9f95", + "https://deno.land/x/oak@v10.4.0/cookies.ts": "d9f94b99f26c6169c6982ce12323c41a548d001bfc28f464264c22dc3dbf2181", + "https://deno.land/x/oak@v10.4.0/deps.ts": "6c7f69a9d3a5d39113dda9b2792dbf18a43e653e4398a4cbd33801d917475ae2", + "https://deno.land/x/oak@v10.4.0/etag.ts": "16d4189ac657244e4b0ced6eeca8dfc48cc229ed22e9baa744b43c9613bae57e", + "https://deno.land/x/oak@v10.4.0/headers.ts": "9a28c40344e85be0877e192210f89d7de12fa3fc4e9c959f7480f6fc4006fcfe", + "https://deno.land/x/oak@v10.4.0/helpers.ts": "42212afa07a560b2958359cc19577417e89d9574d6579551a0af36ff7f00cc6e", + "https://deno.land/x/oak@v10.4.0/httpError.ts": "8368d20c2bbb1e97eab0f9de5752ed04217675b4b5f33862a4e0bde71a492096", + "https://deno.land/x/oak@v10.4.0/http_server_native.ts": "383295e11355161352d7ad384b976e257e3f6b2c4deb0ae972f7a28f697341fc", + "https://deno.land/x/oak@v10.4.0/http_server_native_request.ts": "07910ea2ed51af6c4e69addf9015cdd8d2b5c9ee03fd4993e386834a129a9eb6", + "https://deno.land/x/oak@v10.4.0/isMediaType.ts": "e7457f8c14245a7bb9a3a48000d50e112bb6fcafeab8fd7bcff16363a0cd8687", + "https://deno.land/x/oak@v10.4.0/keyStack.ts": "7594ef9ff4f9be12a4ad23471a5f8bcbdeb5c1ec8ff51229b47114176b5f280d", + "https://deno.land/x/oak@v10.4.0/mediaTyper.ts": "042b853fc8e9c3f6c628dd389e03ef481552bf07242efc3f8a1af042102a6105", + "https://deno.land/x/oak@v10.4.0/middleware.ts": "de14f045a2ddfe845d89b5d3140ff52cbcc6f3b3965391106ce04480f9786737", + "https://deno.land/x/oak@v10.4.0/middleware/proxy.ts": "19903b903323742220842463b96008a57190f768663b4cca3b006907d1df1667", + "https://deno.land/x/oak@v10.4.0/mod.ts": "7a1b5169ef702e96dd994168879dbcbd8af4f639578b6300cbe1c6995d7f3f32", + "https://deno.land/x/oak@v10.4.0/multipart.ts": "060133289d9f7464fe372aa015ec9b31f15b6f2e2b2ae05e83853a85dce1273a", + "https://deno.land/x/oak@v10.4.0/negotiation/charset.ts": "b4c2e0c49dd5122f130f95bf29508448d983c424801b5bc304b00288b5ae3195", + "https://deno.land/x/oak@v10.4.0/negotiation/common.ts": "f54d599d37408005f8c565d0f6505de51fed31feaa3654a7758e2359c006b02c", + "https://deno.land/x/oak@v10.4.0/negotiation/encoding.ts": "60eb0fc3df57cc39eaa739e0e9e2cda6c20b3aa429d1a0108f503065f5709bbe", + "https://deno.land/x/oak@v10.4.0/negotiation/language.ts": "62ef13ea3146538dd52a4666611bd423ebb9a6438e7312398e17a4d16dbafb51", + "https://deno.land/x/oak@v10.4.0/negotiation/mediaType.ts": "7e25cc34600beea3bf0b0879ff1783c752260fcb517dffea2e122830c36e8451", + "https://deno.land/x/oak@v10.4.0/range.ts": "a64274e5b9924ea59c47f544e3a301bd8204e6bdf2561ea83e27993c5f03a874", + "https://deno.land/x/oak@v10.4.0/request.ts": "50c16bd8f8adef03fbb280de8f23b8ea1927fa813bbc87041c35078ad74e70f1", + "https://deno.land/x/oak@v10.4.0/response.ts": "04d5faebf53d79f69a093332fb2180904e8c47d3666f60754b72ef8aca4cfef0", + "https://deno.land/x/oak@v10.4.0/router.ts": "21e9cd4c1d83c9a0848e5943074a5ea748932d8a5fee9ce528f8bd790eaf6483", + "https://deno.land/x/oak@v10.4.0/send.ts": "aa445d3eb215d7c9a493c75529df4f965b93f15c73460608abf3ce13f237e652", + "https://deno.land/x/oak@v10.4.0/server_sent_event.ts": "948b0fe4cb3fe38c7db15e476eb3b7671ef20e566d130e9f701d7c0146aa47dd", + "https://deno.land/x/oak@v10.4.0/structured_clone.ts": "ecf42598652b8082f37252cb873d6e257ad728e6fe73c6bd61f343d94501fbde", + "https://deno.land/x/oak@v10.4.0/testing.ts": "5a2c53dd48e0b45994de668082d26dba4c2a0694f2ceeb1e7c68d5aca170c73f", + "https://deno.land/x/oak@v10.4.0/tssCompare.ts": "27f45f507ef21ba7b727b6c66b491543e6e1815c17194bf0b1d7a1ea5ab8027b", + "https://deno.land/x/oak@v10.4.0/util.ts": "77f1d0aa270191319139a8368f39a73ce46aa3ed0d8fe286bdcbcf668828b313", "https://deno.land/x/openai@1.3.1/mod.ts": "33d22d2e4612a1a2f0ca2f395c806587f4201532d57ee609b34c91b491ee8496", "https://deno.land/x/openai@1.3.1/src/openai.ts": "8459bd2a51d7e80ecc6b1d597c33748681b58c4d3654eb7628070a5718606dca", "https://deno.land/x/openai@1.3.1/src/types.ts": "f599b1138087816908bd7fb10d698f74b0af244c0f542368a18a5b0ae0ae3fdb", + "https://deno.land/x/path_to_regexp@v6.2.0/index.ts": "e94c04a44bbecac99ff2db2d831afe98b423e627b775cb57fc7935f848c64c51", "https://deno.land/x/plug@0.5.2/deps.ts": "0f53866f60dc4f89bbc3be9d096f7501f2068a51923db220f43f0ad284b6b892", "https://deno.land/x/plug@0.5.2/plug.ts": "e495c772bc3b19eb30d820bb83f1b75f6047e3009e19d9a5d81dbe68ca642fd9", "https://deno.land/x/plug@1.0.1/deps.ts": "35ea2acd5e3e11846817a429b7ef4bec47b80f2d988f5d63797147134cbd35c2", @@ -439,6 +757,10 @@ "https://deno.land/x/plug@1.0.1/mod.ts": "5dec80ee7a3a325be45c03439558531bce7707ac118f4376cebbd6740ff24bfb", "https://deno.land/x/plug@1.0.1/types.ts": "d8eb738fc6ed883e6abf77093442c2f0b71af9090f15c7613621d4039e410ee1", "https://deno.land/x/plug@1.0.1/util.ts": "5ba8127b9adc36e070b9e22971fb8106869eea1741f452a87b4861e574f13481", + "https://deno.land/x/progress@v1.3.9/deps.ts": "83050e627263931d853ba28b7c15c80bf4be912bea7e0d3d13da2bc0aaf7889d", + "https://deno.land/x/progress@v1.3.9/mod.ts": "ca14ba3c56fc5991c4beee622faeb882e59db8f28d4f5e529ac17b7b07d55741", + "https://deno.land/x/progress@v1.3.9/multi.ts": "1de7edf67047ba0050edf63229970f2cbf9cd069342fcd29444a3800d4cfdcbc", + "https://deno.land/x/progress@v1.3.9/time.ts": "f5b302425cef076958c9352030f48a79bd7b54316b4057f99192884b5e140ea0", "https://deno.land/x/semver@v1.4.0/mod.ts": "c3e08f7f0a3e625015ecc9a46cf79a9dc3f081f3c5b3037d3c49c0773e58d265", "https://deno.land/x/sqlite3@0.9.1/deno.json": "50895b0bb0c13ae38b93413d7f9f62652f6e7076cd99b9876f6b3b7f6c488dca", "https://deno.land/x/sqlite3@0.9.1/deps.ts": "f6035f0884a730c0d55b0cdce68846f13bbfc14e8afbf0b3cd4f12a52b4107b7", @@ -448,63 +770,35 @@ "https://deno.land/x/sqlite3@0.9.1/src/database.ts": "c326446463955f276dcbe18547ede4b19ea3085bef0980548c0a58d830b3b5d9", "https://deno.land/x/sqlite3@0.9.1/src/ffi.ts": "b83f6d16179be7a97a298d6e8172941dbf532058e7c2b3df3a708beefe285c90", "https://deno.land/x/sqlite3@0.9.1/src/statement.ts": "4773bc8699a9084b93e65126cd5f9219c248de1fce447270bdae2c3630637150", - "https://deno.land/x/sqlite3@0.9.1/src/util.ts": "3892904eb057271d4072215c3e7ffe57a9e59e4df78ac575046eb278ca6239cd" - }, - "npm": { - "specifiers": { - "openai": "openai@3.2.1", - "openai@^3.2.1": "openai@3.2.1" - }, - "packages": { - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dependencies": {} - }, - "axios@0.26.1": { - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "dependencies": { - "follow-redirects": "follow-redirects@1.15.2" - } - }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "delayed-stream@1.0.0" - } - }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dependencies": {} - }, - "follow-redirects@1.15.2": { - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dependencies": {} - }, - "form-data@4.0.0": { - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "asynckit@0.4.0", - "combined-stream": "combined-stream@1.0.8", - "mime-types": "mime-types@2.1.35" - } - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dependencies": {} - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "mime-db@1.52.0" - } - }, - "openai@3.2.1": { - "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", - "dependencies": { - "axios": "axios@0.26.1", - "form-data": "form-data@4.0.0" - } - } - } + "https://deno.land/x/sqlite3@0.9.1/src/util.ts": "3892904eb057271d4072215c3e7ffe57a9e59e4df78ac575046eb278ca6239cd", + "https://deno.land/x/zipjs@v2.7.29/index.js": "7c71926e0c9618e48a22d9dce701131704fd3148a1d2eefd5dba1d786c846a5f", + "https://deno.land/x/zipjs@v2.7.29/lib/core/codec-pool.js": "e5ab8ee3ec800ed751ef1c63a1bd8e50f162aa256a5f625d173d7a32e76e828c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/codec-worker.js": "744b7e149df6f2d105afbcb9cce573df2fbf7bf1c2e14c3689220c2dedeabe65", + "https://deno.land/x/zipjs@v2.7.29/lib/core/configuration.js": "baa316a63df2f8239f9d52cd4863eaedaddd34ad887b7513588da75d19e84932", + "https://deno.land/x/zipjs@v2.7.29/lib/core/constants.js": "14fe1468b87cd0fe20c6f1fec916485f875d8592beba94c9241af4cbd12dd88f", + "https://deno.land/x/zipjs@v2.7.29/lib/core/io.js": "4c4e86ba187540be533003271f222183455897cd144cb542539e9480882c2dda", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/aes-crypto-stream.js": "63988c9f3ce1e043c80e6eb140ebb07bf2ab543ee9a85349651ab74b96aab2cf", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codec-stream.js": "685f1120b94b6295dcd61b195d6202cd24a5344e4588dc52f42e8ac0f9dfe294", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/crc32.js": "dfdde666f72b4a5ffc8cf5b1451e0db578ce4bd90de20df2cff5bfd47758cb23", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/deflate.js": "08c1b24d1845528f6db296570d690ecbe23c6c01c6cb26b561e601e770281c3a", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/inflate.js": "55d00eed332cf2c4f61e2ee23133e3257768d0608572ee3f9641a2921c3a6f67", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/codecs/sjcl.js": "462289c5312f01bba8a757a7a0f3d8f349f471183cb4c49fb73d58bba18a5428", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/common-crypto.js": "4d462619848d94427fcd486fd94e5c0741af60e476df6720da8224b086eba47e", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/crc32-stream.js": "10e26bd18df0e1e89d61a62827a1a1c19f4e541636dd0eccbd85af3afabce289", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/stream-adapter.js": "9e7f3fe1601cc447943cd37b5adb6d74c6e9c404d002e707e8eace7bc048929c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/zip-crypto-stream.js": "19305af1e8296e7fa6763f3391d0b8149a1e09c659e1d1ff32a484448b18243c", + "https://deno.land/x/zipjs@v2.7.29/lib/core/streams/zip-entry-stream.js": "01d4dc0843e8c43d32454cbb15e4d1f9b7122ab288d7650129d010df54bc0b8e", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/cp437-decode.js": "d665ded184037ffe5d255be8f379f90416053e3d0d84fac95b28f4aeaab3d336", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/decode-text.js": "c04a098fa7c16470c48b6abd4eb4ac48af53547de65e7c8f39b78ae62330ad57", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/default-mime-type.js": "177ae00e1956d3d00cdefc40eb158cb591d3d24ede452c056d30f98d73d9cd73", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/encode-text.js": "c51a8947c15b7fe31b0036b69fd68817f54b30ce29502b5c9609d8b15e3b20d9", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/mime-type.js": "6c6dfa4daf98ef59cd65118073b74f327ceab2ef28140e38934b0d15eb2b5c29", + "https://deno.land/x/zipjs@v2.7.29/lib/core/util/stream-codec-shim.js": "1323016ec3c743942dc887215832badc7f2c1e8dbb37b71c94bf54276d2b281a", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-entry.js": "d30a535cd1e75ef98094cd04120f178c103cdc4055d23ff747ffc6a154da8d2d", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-fs-core.js": "737a92a0e27083eefa2b51811dd0b9b6ffc216b3949509fbc8833bfd2a78c071", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-reader.js": "c918875362d7e46fc690cea0b3f81e50a0ec0b6e20f6ae35e5982de73ae45449", + "https://deno.land/x/zipjs@v2.7.29/lib/core/zip-writer.js": "b78c099828ec3134983c259adc4d6118fbfda7f033a7e95de8176a470e9a5a54", + "https://deno.land/x/zipjs@v2.7.29/lib/z-worker-inline.js": "a38bbe15eb011daee291b8243c3101b197aa91dcd786f1c04251bd1dda5c33e7", + "https://deno.land/x/zipjs@v2.7.29/lib/zip-fs.js": "a733360302f5fbec9cc01543cb9fcfe7bae3f35a50d0006626ce42fe8183b63f" } } diff --git a/import_map.json b/import_map.json index bf0fd56e..ec4063a3 100644 --- a/import_map.json +++ b/import_map.json @@ -10,12 +10,22 @@ "canvas/": "https://deno.land/x/canvas@v1.4.1/", "culori": "https://deno.land/x/culori@v2.1.0-alpha.0/index.js", "case/": "https://deno.land/x/case@v2.1.0/", - "path/": "https://deno.land/std@0.164.0/path/", - "fs/": "https://deno.land/std@0.164.0/fs/", - "fmt/": "https://deno.land/std@0.164.0/fmt/", - "log/": "https://deno.land/std@0.164.0/log/", - "http/": "https://deno.land/std@0.164.0/http/", + "path/": "https://deno.land/std@0.198.0/path/", + "fs/": "https://deno.land/std@0.198.0/fs/", + "fmt/": "https://deno.land/std@0.198.0/fmt/", + "log/": "https://deno.land/std@0.198.0/log/", + "http/": "https://deno.land/std@0.198.0/http/", + "$std/": "https://deno.land/std@0.200.0/", + "oak/": "https://deno.land/x/oak@v10.4.0/", + "io/": "https://deno.land/std@0.198.0/io/", + "testing/": "https://deno.land/std@0.198.0/testing/", + "node/": "https://deno.land/x/std@0.198.0/node/", + "json/": "https://deno.land/std@0.198.0/json/", + "streams/": "https://deno.land/x/std@0.198.0/streams/", "deno_markdown/": "https://deno.land/x/deno_markdown@v0.2/", - "@minecraft/server": "npm:@minecraft/server@^1.0.0-beta.release.1.19.40" + "nbt-ts": "npm:nbt-ts@1.1.0", + "nbtify": "https://cdn.jsdelivr.net/npm/nbtify/dist/index.min.js", + "@minecraft/server": "npm:@minecraft/server", + "@minecraft/server-net": "npm:@minecraft/server-net" } } diff --git a/serve.ts b/serve.ts index f5195b49..e63c1fd1 100644 --- a/serve.ts +++ b/serve.ts @@ -1,4 +1,4 @@ -import type { Axis, WssParams, WssState } from "./typings/types.ts"; +import type { Axis, WssParams, WssState } from "./types/index.ts"; import { serve } from "http/server.ts"; import { join } from "path/win32.ts"; import { ensureDir } from "fs/mod.ts"; @@ -148,7 +148,7 @@ async function watch(fnNameInput: string) { // TODO: Add params for `watch` command, and remove them before passing to `queueFunctionFile` - const filepath = `./src/functions/${fnName}.mcfunction`; + const filepath = `./server/functions/${fnName}.mcfunction`; console.log("Watching function file %s", filepath); @@ -261,7 +261,7 @@ async function loadFunctionScript(fnNameInput: string) { const [scriptFile, params] = fnNameInput.split("?", 2); try { // TODO: Add cache busting to import statement - const { default: mod } = await import(`./src/functions/${scriptFile}.ts`); + const { default: mod } = await import(`./server/functions/${scriptFile}.ts`); const wssParams: WssParams = { queueCommandRequest, parameters: new URLSearchParams(params), @@ -282,7 +282,7 @@ async function loadFunctionScript(fnNameInput: string) { async function queueFunctionFile(fnNameInput: string) { const [fnName, fnParams] = fnNameInput.split("?", 2); - const filepath = `./src/functions/${fnName}.mcfunction`; + const filepath = `./server/functions/${fnName}.mcfunction`; console.log("Loading function file %s", filepath); diff --git a/server/_router.ts b/server/_router.ts index 1857f8a3..a6cb7049 100644 --- a/server/_router.ts +++ b/server/_router.ts @@ -1,4 +1,4 @@ -import { ensureDir } from "https://deno.land/std@0.147.0/fs/mod.ts"; +import { ensureDir } from "fs/mod.ts"; import { queueCommandRequest } from "./_io.ts"; import { logFunction } from "./routes/log.ts"; import { watch } from "./routes/watch.ts"; diff --git a/server/_state.ts b/server/_state.ts index 1a1d511b..482093c5 100644 --- a/server/_state.ts +++ b/server/_state.ts @@ -1,4 +1,4 @@ -import type { Axis, WssParams, WssState } from "../typings/types.ts"; +import type { Axis, WssParams, WssState } from "../types/index.ts"; import type { Request} from './_io.ts' import { join } from "path/win32.ts"; import { queueCommandRequest } from "./_io.ts"; diff --git a/server/app.ts b/server/app.ts new file mode 100644 index 00000000..e69de29b diff --git a/server/functions/astral/main.ts b/server/functions/astral/main.ts new file mode 100644 index 00000000..a89d1448 --- /dev/null +++ b/server/functions/astral/main.ts @@ -0,0 +1,32 @@ +import { launch } from "https://deno.land/x/astral@0.3.0/mod.ts"; +import { Image } from "https://deno.land/x/imagescript@1.2.15/mod.ts"; +export default async function run(url: string) { + // Launch the browser + const browser = await launch({ + headless: false, + }); + const page = await browser.newPage(url); + console.log("opened %s", url); + // Take a screenshot of the page and save that to disk + const screenshot = await page.screenshot(); + console.log("took screenshot!"); + + const decoded = await Image.decode(screenshot); + + Deno.writeFileSync("./screenshot.png", screenshot); + console.log("wrote screenshot!"); + + // Close the browser + await browser.close(); + console.log("closed browser"); + + return decoded; +} + +if (import.meta.main) { + const url = Deno.args[0] ?? "https://deno.land"; + const decoded = await run(url); + const resized = decoded.resize(256, Image.RESIZE_AUTO); + await Deno.writeFile("./resized.png", await resized.encode()); + console.log("wrote resized image!"); +} \ No newline at end of file diff --git a/server/functions/browser.ts b/server/functions/browser.ts new file mode 100644 index 00000000..a6d5edbc --- /dev/null +++ b/server/functions/browser.ts @@ -0,0 +1,50 @@ +import type { Axis, WssParams } from "../../types/index.ts"; +import { Image } from "imagescript/mod.ts"; +import { convertImage } from "../../src/components/ImagePrinter.ts"; +import assemble from "../../src/components/_assemble.ts"; + + + + +export default async function stableDiffusion( + { parameters, queueCommandRequest }: WssParams, +) { + const url = parameters.get("url") ?? "https://minecraft.net"; + const material = parameters.get("material") ?? "plastic"; + const axis = parameters.get("axis") ?? "y"; + const position = parameters.get("position") ?? "0 0 0"; + const useAbsolutePosition = parameters.get("absolute") === "true"; + const [x, y, z] = position.split(" ").map((v) => parseInt(v, 10)); + console.group("Astral request"); + + + + console.log("Position %s %s %s:", x, y, z); + // Open a new page + + + const resized = decoded.resize(256, Image.RESIZE_AUTO); + const blockLibrary = assemble().filter((b) => + b.behaviorId.includes(material) + ); + + const commands: string[] = []; + + console.log("converting image"); + + commands.push( + ...convertImage( + resized, + blockLibrary, + [x, y, z], + axis, + useAbsolutePosition === true, + ), + ); + + commands.map((c) => queueCommandRequest(c)); + + console.log("commands sent"); + + console.groupEnd(); +} diff --git a/src/functions/chatgpt.ts b/server/functions/chatgpt.ts similarity index 98% rename from src/functions/chatgpt.ts rename to server/functions/chatgpt.ts index 80f40c92..18beadad 100644 --- a/src/functions/chatgpt.ts +++ b/server/functions/chatgpt.ts @@ -1,7 +1,7 @@ import { load } from "https://deno.land/std@0.184.0/dotenv/mod.ts"; import openai from "npm:openai@^3.2.1"; import { sprintf } from "fmt/printf.ts"; -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; const { Configuration, OpenAIApi } = openai; diff --git a/src/functions/checkerboard.ts b/server/functions/checkerboard.ts similarity index 95% rename from src/functions/checkerboard.ts rename to server/functions/checkerboard.ts index 2eec2663..b81790f7 100644 --- a/src/functions/checkerboard.ts +++ b/server/functions/checkerboard.ts @@ -1,4 +1,4 @@ -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; export default function checkerboard( { parameters, queueCommandRequest, formatPosition }: WssParams, ) { diff --git a/src/functions/circle.ts b/server/functions/circle.ts similarity index 94% rename from src/functions/circle.ts rename to server/functions/circle.ts index f340f297..bd1a5a0a 100644 --- a/src/functions/circle.ts +++ b/server/functions/circle.ts @@ -1,4 +1,4 @@ -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; export default function circle( { parameters, queueCommandRequest, formatPosition }: WssParams, ) { diff --git a/src/functions/dalle.ts b/server/functions/dalle.ts similarity index 92% rename from src/functions/dalle.ts rename to server/functions/dalle.ts index 9f03cb25..b587ee2c 100644 --- a/src/functions/dalle.ts +++ b/server/functions/dalle.ts @@ -1,9 +1,9 @@ import { load } from "https://deno.land/std@0.184.0/dotenv/mod.ts"; import openai from "npm:openai@^3.2.1"; import { Image } from "imagescript/mod.ts"; -import type { Axis, WssParams } from "../../typings/types.ts"; -import { convertImage } from "../components/ImagePrinter.ts"; -import assemble from "../components/_assemble.ts"; +import type { Axis, WssParams } from "../../types/index.ts"; +import { convertImage } from "../../src/components/ImagePrinter.ts"; +import assemble from "../../src/components/_assemble.ts"; import { join } from "path/mod.ts"; const { Configuration, OpenAIApi } = openai; diff --git a/src/functions/graph.ts b/server/functions/graph.ts similarity index 94% rename from src/functions/graph.ts rename to server/functions/graph.ts index d9bb67b7..6c461332 100644 --- a/src/functions/graph.ts +++ b/server/functions/graph.ts @@ -1,4 +1,4 @@ -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; export default function graphingCalculator( { parameters, queueCommandRequest, formatPosition }: WssParams, ) { diff --git a/src/functions/image.ts b/server/functions/image.ts similarity index 89% rename from src/functions/image.ts rename to server/functions/image.ts index fd915744..c8767213 100644 --- a/src/functions/image.ts +++ b/server/functions/image.ts @@ -1,8 +1,8 @@ import * as clippy from "https://deno.land/x/clippy@v0.2.0/mod.ts"; import { readAll } from "https://deno.land/std@0.152.0/streams/conversion.ts"; -import { convertImage, decode } from "../components/ImagePrinter.ts"; -import assemble from "../components/_assemble.ts"; -import type { Axis, WssParams } from "../../typings/types.ts"; +import { convertImage, decode } from "../../src/components/ImagePrinter.ts"; +import assemble from "../../src/components/_assemble.ts"; +import type { Axis, WssParams } from "../../types/index.ts"; import { Image } from "imagescript/mod.ts"; function getBlockLibrary(material: string, exclude?: string[]) { diff --git a/src/functions/pyramid.ts b/server/functions/pyramid.ts similarity index 95% rename from src/functions/pyramid.ts rename to server/functions/pyramid.ts index 7924e23a..1dde9513 100644 --- a/src/functions/pyramid.ts +++ b/server/functions/pyramid.ts @@ -1,4 +1,4 @@ -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; export default function pyramid( { parameters, queueCommandRequest, formatPosition }: WssParams, ) { diff --git a/src/functions/sphere.ts b/server/functions/sphere.ts similarity index 94% rename from src/functions/sphere.ts rename to server/functions/sphere.ts index 9b08871b..1f84e392 100644 --- a/src/functions/sphere.ts +++ b/server/functions/sphere.ts @@ -1,4 +1,4 @@ -import type { WssParams } from "../../typings/types.ts"; +import type { WssParams } from "../../types/index.ts"; export default function sphere( { parameters, queueCommandRequest, formatPosition }: WssParams, ) { diff --git a/server/functions/stableDiffusion.ts b/server/functions/stableDiffusion.ts new file mode 100644 index 00000000..33f7d416 --- /dev/null +++ b/server/functions/stableDiffusion.ts @@ -0,0 +1,60 @@ +import type { Axis, WssParams } from "../../types/index.ts"; +import { Image } from "imagescript/mod.ts"; +import { convertImage } from "../../src/components/ImagePrinter.ts"; +import assemble from "../../src/components/_assemble.ts"; +import { join } from "path/mod.ts"; +import { SDHelper } from "https://deno.land/x/stable_diffusion_client@0.0.3/mod.ts"; + +const helper = new SDHelper("http://127.0.0.1:7860"); + +export default async function stableDiffusion( + { parameters, queueCommandRequest }: WssParams, +) { + const material = parameters.get("material") ?? "plastic"; + const prompt = parameters.get("prompt") ?? "A unicorn in space"; + const axis = parameters.get("axis") ?? "y"; + const position = parameters.get("position") ?? "0 0 0"; + const useAbsolutePosition = parameters.get("absolute") === "true"; + const [x, y, z] = position.split(" ").map((v) => parseInt(v, 10)); + console.group("Stable Diffusion request"); + console.log("Prompt:", prompt); + + const batch = await helper.txt2img({ + prompt, + steps: Number(parameters.get("steps") ?? 30), + batch_size: 1, + width: 256, + height: 256, + cfg_scale: Number(parameters.get("cfg_scale") ?? 7), + sampler_name: parameters.get("sampler") ?? "Euler a", + }); + + if (batch.images === undefined) { + console.log("No images returned"); + return; + } + + const image = batch.images[0]; + const decoded = await Image.decode(image); + + const resized = decoded.resize(128, 128); + const blockLibrary = assemble().filter((b) => + b.behaviorId.includes(material) + ); + + const commands: string[] = []; + + commands.push( + ...convertImage( + resized, + blockLibrary, + [x, y, z], + axis, + useAbsolutePosition === true, + ), + ); + + commands.map((c) => queueCommandRequest(c)); + + console.groupEnd(); +} diff --git a/server/mod.ts b/server/mod.ts index 93f83c19..4e96c8c8 100644 --- a/server/mod.ts +++ b/server/mod.ts @@ -1,4 +1,4 @@ -import type { SubscribeEvents } from "./types.d.ts"; +import type { SubscribeEvents } from "../types/server.ts"; /** * Subscribe to events from the server * @param socket WebSocket connection to the server diff --git a/server/routes/functions.ts b/server/routes/functions.ts index dc8d678b..7fbc4ded 100644 --- a/server/routes/functions.ts +++ b/server/routes/functions.ts @@ -1,10 +1,10 @@ -import type { WssParams } from "../../typings/types.ts"; -import { join, toFileUrl } from "https://deno.land/std@0.149.0/path/mod.ts"; +import type { WssParams } from "../../types/index.ts"; +import { join, toFileUrl } from "path/mod.ts"; import { queueCommandRequest } from "../_io.ts"; import { state } from "../_state.ts"; import { formatPosition } from "../utils/formatPosition.ts"; -const fnsPath = join(Deno.cwd(), "src", "functions"); +const fnsPath = join(Deno.cwd(), "server", "functions"); /** * Load special functions found in `./src/functions/` diff --git a/server/routes/log.ts b/server/routes/log.ts index eeecb52c..bbf276f6 100644 --- a/server/routes/log.ts +++ b/server/routes/log.ts @@ -1,4 +1,4 @@ -import { join } from "https://deno.land/std@0.149.0/path/mod.ts"; +import { join } from "path/mod.ts"; import { queueCommandRequest } from "../_io.ts"; import { state } from "../_state.ts"; diff --git a/src/_utils.ts b/src/_utils.ts index 7a0f9e3f..954ca251 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -1,5 +1,9 @@ import { Image } from "imagescript/mod.ts"; +export function rgb2hex(r: number, g: number, b: number) { + return "#" + [r, g, b].map((x) => x.toString(16).padStart(2, "0")).join(""); +} + export async function encodeRGBColor(layerValue: number[], size = 16) { const [r, g, b, alpha] = layerValue; const imgOutput = new Image(size, size); @@ -41,3 +45,19 @@ export function getConfig( export function channelPercentage(percentage: number) { return Math.ceil((Math.max(0, percentage) * 255) / 100); } + +export function getMinecraftLocation(): string { + const { code, stdout, stderr } = (new Deno.Command("powershell.exe", { + args: [ + 'Get-AppxPackage -Name "Microsoft.MinecraftUWP*" | Select-Object -ExpandProperty InstallLocation', + ], + })).outputSync(); + + if (code !== 0) { + console.error(stderr); + Deno.exit(code); + } + + const decoder = new TextDecoder(); + return decoder.decode(stdout).trim(); +} diff --git a/src/assets/mask/checker_mask.png b/src/assets/mask/checker_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..4a3022431adb7e617d70e23e554c8074fed40261 GIT binary patch literal 1100 zcmbVL&2JJx6koNHv=+Z&Ok-j)S+BCQ-L^4omZbR(_QPaZt#RpUv$c=6DB@#59LL8Av}mZc!27dP{@zxSK>-f!~Wez9;bymVuUPYYmo897+ zxMyp62N6(5Eo{bk9^gid-`U8Ec{_>PI5Tk2_MlMK2OT}y;Nz;|$@H>;hz2~PYr3)* z<9(kpJu?{cz^9N-j8Cv&ppq|wq~##6A*@GqQIx={Ao|~Jz&7ZQh$zbb0KvzriqmMy zrBphIME_!Zn-E)uu;1?s{WZaIT5vTQjY3g^k`$qgi2KMSninzMTY-ZVa&-sW1Y0Ix z4z;?~BTQ+KzhNJHHQnHS=u1IQvms3sQa4nwoOF;zET?Q)U3I%xcZp`|2vFy^Wgi*^lA= zOWL2wY${RCg!P}77M3?Rg)e81a*MSE9C|mr@|l17>f8Co@0-_~*WTCGmv;~22N%4_Uuww+gA(cWZlp0TEzu3IG5A literal 0 HcmV?d00001 diff --git a/src/assets/mask/poly_depth.png b/src/assets/mask/poly_depth.png new file mode 100644 index 0000000000000000000000000000000000000000..9750749787d2a2b6434d9b20081ebe1bc4664a34 GIT binary patch literal 2008 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(I&H|6fVg?3oArNM~bhqvg0|WE= z%#etZ2wxwoOpmIf)Zi+=kmRcDWXlvKdpny7E1>4w)RIJnirk#MVyg;UC9s)RKsHENUr7P1 zq$Jx`$q{6kLWFOCf^&XRs)DJWiEgrifsulto&gx>D1az^Fw!+JumU3mC{VJqDN0GR z3UYCS+63g;DrJ%7)t(u-l;Z&L9_1AabyB}BG+WVD%=9;Dr;s?4P-QaoU!I9K- zl!38HaT3qPM~}RH0@6wz$;Sz9atIJ#e2j(D&}#Ytj+VrL-7EEFW?3GvI(}>F-)_L?4QTocy{Z7D|&vRWjoI^_q>s*&N{U2%k=u< zKA|1@GSPG6p2sE1H|z?ZUTm&A_tU$gj!8T^hrfJ2qjvjt+4jq!7oS{WNP1H7J4MG- zY45t2F#D6bd&P}e4?OCVvgph`R=cC$!h!GN^oTQMemi16MqLeT*uD1gaiP;oV>5&A zt_giDFPW&E6W0_zseV>B!!qLy$@kV?k&ocn-FJM=Y>!Fz4rqRR+g%oV*Qr7DWARG; zRJXnBGH;%4JAEf%ml40aRdAZ!^p|o=!fsuwTstp*V4wcm5L#IrxxC%P{VUz~kJ{qw_C9)?T#Cpr;9kb87J`M97`YB5(Y z1DoS9qq4onZFJfeT}_%?;_*-K?ToH@yav0bCRFX$@f6%Rm%rj%#P-LpUnDBVs~^)f ztvfZZljTU+Np<^q_tr}sdnv@QYU+iinT%Hh+@6XbNi3hc``Ra+YK{i0slT(9*w?w1 zY09tKZoK2#`m1MK&TBC)UG*ycYxri}n_;>WB*V6Ah)q9#lELR!l31yC+IF!Mai{B+ z?O(b-e^0nrz?_;17j-lrn`~^C*buitPu+TjMD4_tqKDg>Zb_#VeQ|viyXkP%zt*;auW3Z`rLE9KsKhYK$&dKR3T}M8-jBg`9EK&lIg=G7GFV^l$oX6ED_DX)rfZ z6nk#B_38)T0C`)#Im`EL>|DU?BOugY_qe-Y27jme4(&C)oDX~5zS~b=YN(z3dUNiR zzgG@+?pLdxz-1K~CVgn)gh&6LaH%Z$TIHWNLF$qHwx2tv=5$U7`*`J6_3Y>em?m4Zpqf#??&c%Hwfg z^__QW%g($U(Dl%G@t&o+-@^2YZQEsDe%^Sn)8kW}ZZ2El{$)`gnr9w(H2L XX*Fl1P9?Vg0M)&ou6{1-oD!M<@_R;< literal 0 HcmV?d00001 diff --git a/src/assets/mask/poly_depth1.png b/src/assets/mask/poly_depth1.png new file mode 100644 index 0000000000000000000000000000000000000000..e91831536494b1a3c7c8b3aa23080537ac1c39c9 GIT binary patch literal 3062 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 hh&r{4t?ou00KF6*2UngD@x4Kx4% literal 0 HcmV?d00001 diff --git a/src/assets/mask/tunnel_mask.png b/src/assets/mask/tunnel_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..2e9eebda52cfe07535e2e32c1dc4ea84c09e6853 GIT binary patch literal 1485 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 delta 529 zcmV+s0`C30Ae;mtiBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHjyD0e*w-( zL_t(2kxi0MYui8&#edpett_j-PB8@0fCI)Nw|ohMzeB&N!PlNkPo*{m9}L08;6Jsz z)@pZ^gRLaPFbu^ zdO-DwG6XawVXvyq*zSsYnl&Rpe_hePlKDzM6F(A5G)MrN9=_+|$KUnZnE8t#YeoWC zsM#tRlMd-h?%)e#&~0m1k`7ntc#+HiIZ(r&X$bn6MBG3rbRYo@2?%H)y4yFquVEuQ z`rAMKgubWKqPw3sF+^8_2+noFAGH+*)klMd7&LB$^f0Gy_#E?J& z3LF3lR8WJ;4zU&ph(Le<+2;FDf&vNAZ6bQLC6qJ}uD@Odg#ZDfwh0i*p0i?LQ~aJ9 zYH(0N1s6Djfiqmdfd{uA*i__Aq5KBtv2cMC9N`EjDB%btT%hvGZgN|Q+)u`Khg;v) zU%1oV8E=$=UrY;XB1FWQzPfA2>c^n;z2?K9s<-Jvwk`)3p2fWDQiYAXVPZ2 zbQ|X$xb%@30zygHt9mm&?}~Doh0%vwZ}!&nSGm|~bBHyHp0$HI&8s4}ANGC&8XnM3 TjX9X600000NkvXXu0mjf0*>Zy diff --git a/src/assets/materials/dot_mer.png b/src/assets/materials/dot_mer.png index a2b55e04bc1b51ab427a5504985ec5aec5922924..8b5bc917295db9184b02458c855ddd6960c1f1e3 100644 GIT binary patch literal 3239 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 diff --git a/src/assets/materials/dot_multiply.png b/src/assets/materials/dot_multiply.png index 5a7b607cdc8ae312f7e808db621e1537e7ceda22..99b7e901c898e068a7e4367a19f5142370935aa7 100644 GIT binary patch literal 1382 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*PVXMsm#F#`j)5C}6~x?A^$fq{8_ zW=KRygs+cPa(=E}VoH8es$NBI0Z12vjeSKyVsdtBi9%9pdS;%jl9EDZimg((sjq== zfpcm`rbks#YH*cbNODznvSo^ry`4>k6;N|-YDuC(MQ%=Bu~mhw64*>DAR8pCucQE0 zQj%?}oO6e!u*6s4qD z1-ZCEZ36Oal`=|73as??%gf94%8m8%i_-NCEiEne4UF`SjC6r2bc-wVN)jt{^NN+B z2DqdaCl_TFlw{`TDS!-2Ov*1U0r?c_{@en%sd>fF@BxODennz|KG321Kx2SDwsI~? zO)SYT3dzsUu?sFuDlSRPOHNe)8lIV+ms(__kFLf>A7mMl631D+gYDFf(lPJys z$-vwLVq3WaiLlh7VxYh542+BnZ1gdNZNMS{nH8xy5iXgzsd-?<#zs)3=n_cEBXKE5 zk|58AC7EeBd(MoS#z) zN(FZ2FvE}qA<81LDMJ>7C_|_U&C4vYgPDgWj3t1;i2|6U?SKgemXYibxiB>knET3s z+3Ze-x+e{E`+wz=(RF#c2LhyEs5GDMpzac$pxL0;KYXv5cQ zf)5WiNE{YcJh1D)e8m$6%_4`(-Cl}Z=BZOyF_D4$`Sy#)C+EK8{ww0?ZuGG;F~z*n3R_~!-D=w znRyJq962>F1b7+9-{cH1nzGEKNOY}B*Vp^3QWxSsJ?b`>&waYUgCS+b`ieu!=ZrHC zxGA(d%{t6cVZh=QeRvVyljUA~PdeKYPsaWE#`?BS({z<=#&^4YB8zr3%6?q#%XTu& zaiWRloBmxtbIc_Ye(zvhS6Lvv=jXk56FrxHSUvIZnHV13wnsBG{)*2!`F&%1OSe2b lM~_(4nO}SVRh87F)j#8A;L^VJQ4drSdb;|#taD0e0szL&`*8pO literal 2196 zcmb_eeQXnD7{37noM908G9xDEbwMP%-p9IYd#;eOj&(@K9EA;ogzMeg_FQ{+-d#Ud z7C_O!+(=~LP!N;=L4uK(DTFM#7??=LAPk8!6DHzEFwTj>AJYVUuU)&9fD&Sp`+A<= z`+WSK=Y8HbRaaF^$t%c15M)ZF+f@Uf(~N8KICy_!TabZ|M-=xe4M85AZd^IYu6G_s zkckIGZ=GJ}S<3Qq*vtvC518X&1)>q8bYWcK_I5<DNp zNn8*tJZ@)r8n^iD))HGWPm>gtUcXEg;mqJd4{gVoBt}=VZc&5%(lD~smxGr^-a;ap zY($ph4dxw~#zldEjR?hi2yP-V4CdLW1I?{0mxN)o>ix} zAWB=z6gsa~lmt1ZJ)Mq?4{}lfz&T1_#6u_ZV8#5`WJY(#|CbD5OwUx2h`Z~T)Qy?6 zF!!Vgj&I{Nb8x_q%+UiR$dd{WmciTK?n98Awn~@NJ8xU0)p^AE+_cl@E800{(T=_? zE^O8F!+DLl>%MuvV@8wvfG`*}J$C&w*HF~7e(2m_;^vhNRL|SP=Qo@?YhUuIY5SSN z?Po$v^Y$eNd#``|*VKbGhieYEoWAz?T5@-fvUvXm#a1+{dARZRROZB`7s0Ndh8q{` zsj~H5yWv(AygO;!%d_WfU32vKz>sIsjMYbLf89C0NoIDt3%{S`J#uL<`s{D&iMqDj znLqdEJdwY7eD0~s%`@{}yROeKEGq0j*40~)Xk8>7@6X-je6#z)o z+QNA|yKg1-9NPAG^`?_MYBsj7Ia>coLFd(5eMLJ9uJn^F1Kt4K>L!lJUqbLu216{5SJ8t z_UZxPYDASIC;tf?-*n&bYbyb_I=)36t{>>b;cva`eY?=#aKq4S_>P zCgxNp^^iq5s+|DG@m69zfjBMIkid2~cHs%mPu4Sdm�)FHwX0zt#2_$@Rybt>C_j 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 D3oKJj diff --git a/src/assets/materials/negative_mer.png b/src/assets/materials/negative_mer.png new file mode 100644 index 0000000000000000000000000000000000000000..c873f449174f32802b3536bc0191f8d373690642 GIT binary patch literal 1870 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1|Od-NIK*2e`C{@8!&qO!bz`#hsP|pAibQD08J{aj57+8Uk0u(6O*%YOu zSp~VcL2Ux^Y?U%fN(!v>^~=l4^~#O)@{7{-4J|D#^$m>ljf`}GDs+o0^GXscbn}Xp zp$5357AF^F7L;V>=P7^;OiaozEdluy>i*mUxT$%?(C`6=3yyH4m8k z%7NKzw*69FVAgf@ba4#v;5D6^+0hp$a{PSlyUlwxXZ8pvrTlE_oTOBfuw)JAM9u7s zHN7s=_*1%T7QX4#3H)|qnyQj_mrAdyRF{UaTJo9ScDKLUy#N3I9{>CG|1H1Ydw!?* z+}`y1QTiO?tHvQ{MIIbfh-h7N% zJFauiKdZlA_DAzL^tUTVFj`q&sQ0xM+4Ab(gvO}f3(gi9T)J}C@g;BDNzc!ZWfweO zVp5mM$$X-x_3-19hxT6Z6A$oby?w0FXx6{FD`#AGFh&_4Jt&l6I`P~21$*t6eyq3T zP{^=lbH5m9aEH-quH%2BzgI3+zWBkF;mfdi?gBZ+Q|v_sKbd$!y@aQzv7h|ja5jq7 z+wkD+!<}auRyFm>H@wo!n3ZAAnWooNB>u&$X{VP#6{pq7lb3n6l%z7a)<4*=!@!*5 zmPn9=kku^SUC{?0=&WFy^k+g8yX@pyuirl0@cVF()cw3v2Hm+2GQRVNm^->?EjsSN z{dec%FV`Ewm?bua911x;IU)S#EcN**3(D%|ee7;CV3=-t`OX2KgHDI1u*Lsq%wJ$U zUwS_KQ}q?)&4tV_b}hIvF~==(PS#`gUY-RN-0FK&cQ+=+zbZ~xXdc41tKjb=_bZDX z+WGyWKQ)$Ue48dR>x*NSY28UJ76FsZoTuk4?^Ai|e&CF8zzyY_Rj=4C++umvex>%n z9hq103xYWoa9OSL%WPT}%%Si+ul?|ARxkER{GAV(mD>}xuWw8&YQL0N!7Y=vWGHADfRZn8z*VckkFGt+Z2-=l^~x z6aM4NH)F>n`^By?KW02Ro%->iK-u1hoNF^@O!tl3RARyH_*u4RTH`wAV`ruu9*9#^uF_V>Cwy#oc i%PPj)ch75ufAVh=llVS8J!KoH#`ARbb6Mw<&;$U^oArYL literal 0 HcmV?d00001 diff --git a/src/assets/materials/negative_normal.png b/src/assets/materials/negative_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..42b1f4127689d18a2169799715c973425e8c5edb GIT binary patch literal 1256 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1|Od-NIK*2e`C{@8!&qO!bz`#hsP|pAibQD08J{aj57+8Uk0u(6O*%YOu zSp~VcL2Ux^Y?U%fN(!v>^~=l4^~#O)@{7{-4J|D#^$m>ljf`}GDs+o0^GXscbn}Xp zp$5357AF^F7L;V>=P7^;OiaozEdluy>i*mUxT$%?(C`6=3yyH4m8k z%7NKzw*69FVE(-C>EamT!TWaVMy{p+iPrO~ygrHr2AsEKnq4@)-8w*1sa?E~RePU7Oi+kZ8- zs~58N2p!Dk&kzrJzeHPs@9X{3n;K#q^c2J=aBEa$#GLf|dgYU+5J%6Xe_4SK<-SMp zeVKhH@c3MAx8iN8y0=zZDqAnSq^H6ecyex?^MmTL^K5b-Sk3pkGa4G+cLtS3p00i_ I>zopr0Byjz-v9sr literal 0 HcmV?d00001 diff --git a/src/assets/materials/negative_overlay.png b/src/assets/materials/negative_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..3d68ad0730e2ed2da081b2e9e52557c427a05f1e GIT binary patch literal 1060 zcmbVLzi-n(6uwe~pil`h6}p_vz=C~_`vc|VIBMgTL~5ca4K&$soNIDQe75Y1Vlp6L zV_-*u0R|Z1FJMJtLu?F)e}sWM#~~(77p(i`?|t{)`%dq@sMqeVuiaV$0IXNac0=sf z<-9BwfM31+EVe5_xg7y;^@f})-+w;&4Z!Lr+HCPw^$5GZr#W4}L$ufnFs@7r24+-4SwpCZx4VcsPmKgBY`?kR5AhiN47 zS5W$#2N(Ei;Akj4PhgBPX=W`z{qw7eD0QeciftXXSAFmjS?!jSHjwm(OI|gUUh8{<#0>`5)VIUvB^a literal 0 HcmV?d00001 diff --git a/src/assets/materials/positive_dodge.png b/src/assets/materials/positive_dodge.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0f06ba22ab6cf0817d4199ba76e15adb5e8b53 GIT binary patch literal 1160 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAI14-?iy0WWg+Q3`(%rg03=GWc zGeaUuB7A+UlJj%*5>xV%QuQiw3qZOUZ0suv5|gu2OB9k)(=+pIm6Q}RQ*4#OO??e~ z3!GCkGCit_QiH4fLXxYplPyz}?CoqStbm$xQ%e#RDspr3imfVamB40N0ofp7eI*63 zl9Fs&B}b5D3K6~m3eNdOsS2igCc4Q621W{odIn&iqX44x!ARG@zzU2Mpg_sarYI%N zD#*nRY7>xWtCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWvWTXpJp<7&;SCUwvn^&w1 zHNYjcIJqdZpd>RtPXT0LVp4u-3CO2V_vaSCP0cHYh7T~L^eYkz^nni52O0zPv6XXC zYGO%#QAmD%j$LqRQgKOQUUI4e(D2OkywoBaeRMT8`XI}YEQfdpNdTK`QY$hMoIRZu~QvWTG6;{2Ra zP%5x9H-Z|5EC^8+iA@=@AVe8LRcKyji5<*5G+`_O1WpvdBy9&wFtChdhscGgdBEIP z4$Nk=?U(8T^Jl%Mi(`m{WNZIHzQYC_&b%sLww%8e{(f`SVpg`5TUPEqGhM@fTTX+# zV4M$+)|`%>=M3JNjOFZX9wDu#1@pvMBT8m-%V?ZtaCzo@I`Oxrw$mA_-Q6=e-tK*A zFL9!0gI}ae+V&mtzeB5eSKC^M7krkR_O)(Vxk}dO?>la{e63_YD`WrW{EGWBpt8c# L)z4*}Q$iB}r_^gT9G>CpgK7@X~-?5#j{CIo=)eV=lA;Fe(%rw_xXI@-`D5!di~Nj`>i*a zJ$E($00utZUPSe7q?ty#>U9%sAzZ!9;(7=30YKkGGqqYel2ibobA{<2Bna{)U}zkc zJ%!GpLiQpSSIq_htec2Sp~XM~kP0!FY#ba8GU+&Ypp!4km+J{dF})Lb5GldWpOz3q zbEU)G@K_N>{V@v?P(TqYmd(eAaPUc=81-2LBjMmAMG%97yK91hLB5+oPYw?Po$MVE zG!zOAI@qHoA3IR9Atwg{g~Ch@aO#2Mu{?SthUm3!Dw6sQ2agg6xELf-C=}WYSK4!U z45Wjrt1A+PMxxOOH3PwqXA3AI1e?Eb%7GWer}3Cv0h7Z9H4Z6MPMkoabSi%q_rG4* z{Hgm<-xN|r;Ud+EBGqoxiZPx%h$7(d{5hOhJSmRK7f{$V2vj>~GT0Cgi~N>@MQVag z3;tQoG~j#LKqBT>k^hs9#_}Z}%^9;+y^tXuU#$y|LZeYwRyB(j+)f?|Wbm@J4r zNp@H@Mg7(=jUN1W^l66wPoaPr`8NtRmM;o5XN(Vv!hiyqbU_qelk>M2_A5Yu*o?oh zrohj%AR@%)#Kvi=0Pl>RayA|OObh-VZ94dw_LUXDW(x3M{Cxv|=Yl4SLS52$b-{dT zB>dMdgxKot6RO)R*X6x6Yy@8-xqZ{Fhx{tWR@(AfMfe_iD!%8NU-?*|<-|Jk$ldLXyL&8Ja}3I~^2>Ydbe~r$GPL#@c7#x= z`TlzF%C^P4{25*0iF0RKJ~>(~4;#wJ3%^x7fJ9waJzq2?tE<`6b!}_i!tN|vKD9rv zCeYU-cgWt2NG=ISq#jr->7^HwU@4jW()Oj2-sJXKNvMcQJVv5M^w9)$$JZ>L}kEoA$df9p>f1&tKqcq!M=#M zV;1GP^7#jg&JWH1RQj(!OK-dWY}QY(W7M60_uKC~w6deFT4U@UuXL0InCSi{9)w@L zzyTTAM@g(9zs7i>ZkxWotnLYfv)!&DuLaKPZ@3w+$ibb{9qRk|Q+m}SL#f1k0r-ee zNGd*_PVg;|WfOZJ6OLvGTKck%r!D89VS_pH6WG>cb21ieZ6$&vg|9SlAD~P+^u~;l zzC+6P9PQ5^u5A;KZ8iOHh*(`dd?zbG33)r2-Y!c>T`1|;K&>`=7Zg%2ZkfG5>BtE% z`(aMK-|O;*GIo!^QHM+hL^hXvRNbozk`nZK6$-jr_yO(Lv#9lVqMnz+UkB8s;$DSS zZ`pgKGcY8(zX0#{3TZyxG^00O`f=Gd;sh?<^4Q?%^pcnNPgYs|a_X>kXrXwGNrl8R zA6v(_y4Eu)n)nzFSS>j-X9j(bmEq6-esi);yZlC)>`BIzjX{Sxi?06}`^1od@c>Ax zjOz`O5^sWq1j}Wa4o2?=XGrAb2LfJMrzvkRZi`b9^%Gl-mK33WXda4tN`sNZZ@#!M zSy1_0>Ee9s=9wnp37hfNg-N?zXEJQf9rc03vs-f3TyO~`UL8I@xBQZq9m+pPDj~#(FA0s6No||Ch-)&X#1Nxj zJO84W~B{i`A2#zJ=;)e*#F~QlLXZcC0#{lZglL5JOX2}EK*-4;;vOR zTd#05-L(xxLL6?8`MF=($7h^NZZT?0!4;!ljLszo?Fza;Q&tLHe=_E$S2=d?LzvWV zPYFMfCEEY2&kmB#t4wp*Rv946CmQ91-fJEG5cyVx=~-_d@vzf&+@j`ujC10mBV6-` z;@tl3>>IWhHW~xZcDq~3q2lg6k_|F#V8-Ukn2JPiRSn_d2FucGbA*?+H1o|Lp3=W) zH&(qOxp~)iG`EXny#O59aj9le;j-Xj)0EKl4@`+J%8KN}vDMAmFY2N)lm#dE!Q`3Q zT2J*=rY)5Hp~QPG(J@@KcD>u#Qt_Y-<#)f8eqHBD6OBb_C1G^J8xzM>bqe!%16KTH z0->H4&~)Z*5tLwfu_HO&W55$xkd!#<7s1T;O}g`4^|10IHnH@1C-oHl_3{UH5maNH zmpu~Zvs~IgGW8UBH=Etbc?VyL1~#=F0AmbwCV=gaVFw#wA+4GpAD^{;UY9*0lK%q+ COd-NIK*2e`C{@8!&qO!bz`#hsP|pAibQD08J{aj57+8Uk0u(6O*%YOu zSp~VcL2Ux^Y?U%fN(!v>^~=l4^~#O)@{7{-4J|D#^$m>ljf`}GDs+o0^GXscbn}Xp zp$5357AF^F7L;V>=P7^;OiaozEdluy>i*mUxT$%?(C`6=3yyH4m8k z%7NKzw*69F1_s7zPZ!4!58k~~?fsb?MUJo+u|{;Xs)(@pWEgDRxMRYOjHO}64nmj3@p!dPUp!l?yqnERWw>e)dmUTDp0_ z2Ci%0_f47S@iaQ!VXx4}-H95r-F8ay-E2_z+Ih0{fX7qYgNO32o!z#Kli}cujjMd* z`>W68J#YGw^vOd{z~RWc@QULHOPnP>@7rKpynydPGV}6loR$Te@84)MNq8L1{A%_i zXn%!T=k>sb)RcnvueV)UJ(=mP9KSL{qea&~X8nQ^h7(#V{}d`Pdi+|LtHE?ldR`+B zOSIGFW3$+L{tGHJW=HJXFpcAY;Dmb@7`qybFPhz9%DO9{(0sIx+bT-I0?1)#h$?uz z{XuXr_px`47AgOEG7K4#8N3BPeOJ09>^L#&grLdcUZeefd*3d1sGe;8e_v|rm6-x- zU3b6JO*pT%S~!zQfBY50Bng0&CuyH0O?&(v{r QHmIERboFyt=akR{0Qp}A+W-In literal 0 HcmV?d00001 diff --git a/src/assets/pixel_art/burger_1.png b/src/assets/pixel_art/burger_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7767b27433dc7e8082d7ec99f9a8602e3340b37f GIT binary patch literal 9150 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 literal 0 HcmV?d00001 diff --git a/src/assets/pixel_art/chest.png b/src/assets/pixel_art/chest.png deleted file mode 100644 index 60ab9810c3bf6b9257205f998abf1dadba51e56e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)7j`KKw3c%#e>C` zo&?b#2xB-K6Q^$Ii}dW@o>dq;_hnRNr(Xzy}&zE0KMiwQ_+{ zFn;^X^nL5l&yB?^c)K(v5IMqDR1M@A)5aNzph4J$(GR1)FDX%y9?+m@n_&gW7N11%={DXi`^UB$ sR)BX1C0*vp*et*1EDY1Jm6I;bp==oizQ>5r6vPk_ddy5syx}svFIcmOsB5lOwCu z8eUz-wkJ*>1P8j169j}I&<)yQ0=tgb=Y@QpZYv__L)fw-&ZGjtlDhzAX-t4Es}fdK z6_jjQD`{9SH691Ls_TksDViy17F2auG{Eo?xwzPCz{TAtk+#p{^7rlOu*QNpS2(%%>qp8Bv|CFh}I74r~abj5bV$e&QZe zx+qdKSxs%~13jGKqLsMa7w%z&v`IihmT;_=VWSphG-=U)q0WwP62N`yx|xhy>JkK* zh=k3Z;ARXRa!Yhl>qSIaBne%KF_}BT)tpSNiQsHZ5TkL8()K7)3nP_*UX)d^|EM2& zw39q`!v#`Cj5uN%HA&O>ThpeXVezO*stHwf0ClP7H+uhsny_d>!@LQ~bH+mq-4^U& z*r0KMxWRsanna1hrU)`O!dcp;F&E6WGx}P%F06zJLm?)WvLkYvW#9LpT{0S)WtSwa zSh6IO6m`kcbxW$-7D7{oXP_b;-7izTl16rP-}|5UpN@TATBv=;JZZtDDFNsGga@lP ztcWAz`QfbXgMJo3gwyPHM4X0|c;ax|zhe&!Xge*!N&h3<16V>Etb<}Q)8yW|Z8|Aj zc_ppGfgQ@}UU;pgI7q(yi&Aqre@nvECmw$fukGH=S zHXhmc$yjus@v(92&r56jN+^FNx7FA&qib)5yRI&6HVyNYx}cfgPYD;!Jg_}cIDIO2L0qLT TO>Tabw&KcMwfyPfP4n087Ux8sU&T-QA&V! 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 + +// Loop through all blocks in blocks.json +// If the block name is in the database, add the dominant color to the block data +for (const blockName of blockNames) { + const blockTextures: string | Record = + data[blockName].textures ?? {}; + + const textures = typeof blockTextures === "string" + ? [blockTextures] + : Object.values(blockTextures); + + for (const texture of textures) { + textureMap.set(texture, blockName); + } +} + +const db: Record = {}; + +export async function readBlocksDirectory(dir: string): Promise { + for await (const entry of Deno.readDir(dir)) { + // Skip saplings, doors, etc. + if ( + excludeBlockNames.some((name) => entry.name.includes(name)) || + entry.name.endsWith(".tga") + ) { + continue; + } + + const currentPath = join(dir, entry.name); + if (entry.isDirectory) { + return await readBlocksDirectory(currentPath); + } + + try { + const texture = await decode( + await Deno.readFile(currentPath), + true, + ) as Image; + const ac = texture.averageColor(); + const dc = texture.dominantColor(); + + const key = [basename(currentPath).split(".")[0]]; + const dominantColor = Image.colorToRGBA(dc) as RGBA; + const averageColor = Image.colorToRGBA(ac) as RGBA; + const value = { + dominantColor, + dominantColorHex: rgb2hex( + dominantColor[0], + dominantColor[1], + dominantColor[2], + ), + averageColor, + averageColorHex: rgb2hex( + averageColor[0], + averageColor[1], + averageColor[2], + ), + } as const; + + const blockName = textureMap.get(key[0]) ?? key[0]; + + db[blockName] = value; + + // const res = await kv.atomic().check({ + // key, + // versionstamp: null, + // }).set(key, value).commit(); + + console.log( + `Added ${key[0]} to database with the values %cRGBA(${ + value.dominantColor.join(",") + }) %cand %cRGBA(${value.averageColor.join(",")})`, + `${ + rgb2hex( + value.dominantColor[0], + value.dominantColor[1], + value.dominantColor[2], + ) + }`, + colors.reset, + `${ + rgb2hex( + value.averageColor[0], + value.averageColor[1], + value.averageColor[2], + ) + }`, + ); + } catch (err) { + console.error(err); + } + } + + return Deno.writeTextFile( + join(Deno.cwd(), "src", "store", "db.json"), + JSON.stringify(db, null, 2), + ); +} + +await readBlocksDirectory(blocksDir); +// kv.close(); diff --git a/src/components/BlockEntry.ts b/src/components/BlockEntry.ts index ebd4a92e..2535b81e 100644 --- a/src/components/BlockEntry.ts +++ b/src/components/BlockEntry.ts @@ -1,11 +1,11 @@ -import titleCase from "https://deno.land/x/case@v2.1.0/titleCase.ts"; +import titleCase from "https://deno.land/x/case@2.2.0/titleCase.ts"; import type { IBlock, IMaterial, LanguageId, MinecraftEvent, RGB, -} from "../../typings/types.ts"; +} from "../../types/index.ts"; import { hex2rgb } from "https://crux.land/3RdawE"; import { BEHAVIOR_BLOCK_FORMAT_VERSION, NAMESPACE } from "../store/_config.ts"; @@ -99,7 +99,7 @@ export default class BlockEntry { // pbr_emissive_brightness: this._material.label === "emissive" ? 0 : 0, // brightness_gamma: this._material.label === "emissive" ? 0 : 0, textures: this.resourceId, - sound: this._material.sound || "dirt", + // sound: this.behaviorId.replace(":", "."), }; } @@ -116,7 +116,9 @@ export default class BlockEntry { group = "itemGroup.name.glass"; } else if (this._material.label?.startsWith("metal") === true) { group = "itemGroup.name.copper"; - } else if (this._material.label === "emissive") { + } else if ( + this._material.label === "emissive" || this._material.label === "dot_lit" + ) { group = "itemGroup.name.stainedClay"; } @@ -184,7 +186,7 @@ export default class BlockEntry { "minecraft:material_instances": { "*": { texture: this.resourceId, - render_method: (isGlass) ? "blend" : "opaque", + render_method: isGlass ? "blend" : "opaque", face_dimming: this._material.label !== "emissive", ambient_occlusion: this._material.label === "emissive", }, @@ -221,4 +223,31 @@ export default class BlockEntry { return Object.fromEntries(eventData); } + + serialize() { + return { + id: this.id, + behaviorId: this.behaviorId, + resourceId: this.resourceId, + name: this.name, + textureSet: this.textureSet, + blocksData: this.blocksData, + terrainData: this.terrainData, + material: JSON.stringify(this._material), + level: this._level, + }; + } + + static deserialize(data: ReturnType) { + const block = new BlockEntry( + { + name: data.name, + color: data.id.split("_")[0], + }, + JSON.parse(data.material), + data.level, + ); + + return block; + } } diff --git a/src/components/FlipbookEntry.ts b/src/components/FlipbookEntry.ts index e9bbc706..810d715f 100644 --- a/src/components/FlipbookEntry.ts +++ b/src/components/FlipbookEntry.ts @@ -1,9 +1,4 @@ -import type { - IBlock, - IMaterial, - LanguageId, - RGB, -} from "../../typings/types.ts"; +import type { IBlock, IMaterial, LanguageId, RGB } from "../../types/index.ts"; import BlockEntry from "./BlockEntry.ts"; diff --git a/src/components/ImagePrinter.ts b/src/components/ImagePrinter.ts index 4a56a161..81daf1e0 100644 --- a/src/components/ImagePrinter.ts +++ b/src/components/ImagePrinter.ts @@ -1,25 +1,21 @@ -import type { Axis } from "../../typings/types.ts"; +import type { Axis } from "../../types/index.ts"; import { type Frame, GIF, Image } from "imagescript/mod.ts"; import { extname, join } from "path/mod.ts"; import { sprintf } from "fmt/printf.ts"; -import { EOL } from "fs/mod.ts"; import { materials } from "../store/_materials.ts"; import BlockEntry from "./BlockEntry.ts"; import { BLOCK_VERSION, DIR_BP } from "../store/_config.ts"; -import { hex2rgb } from "https://crux.land/3RdawE"; -//import { encode, Int } from "https://deno.land/x/nbtrex@1.3.0/mod.ts"; -//import { encode, Int, stringify } from "npm:nbt-ts@1.3.5"; -// import * as nbt from "npm:prismarine-nbt@2.2.1"; -import type { IMaterial, RGB } from "../../typings/types.ts"; -import * as NBT from "https://cdn.jsdelivr.net/npm/nbtify/dist/index.min.js"; - -const MAX_PRINT_SIZE = 3 * 16; +import { hex2rgb } from "https://crux.land/api/get/3RdawE.ts"; +import * as NBT from "nbtify"; +import type { RGB } from "../../types/index.ts"; + +import colorDb from "../store/db.json" assert { type: "json" }; + const MASK_COLOR = [ Image.rgbaToColor(255, 255, 255, 0), Image.rgbaToColor(0, 0, 0, 0), ]; // Image.rgbToColor(...hex2rgb("#ff00ff")); -const FUNCTIONS_NAMESPACE = "printer"; -const DIR_FUNCTIONS = join(DIR_BP, "functions", FUNCTIONS_NAMESPACE); + const DIR_STRUCTURES = join(DIR_BP, "structures"); function colorDistance(color1: RGB, color2: RGB) { @@ -29,102 +25,100 @@ function colorDistance(color1: RGB, color2: RGB) { ); } -// export function createStructureTag() { -// const block_palette: Array = []; -// return { -// format_version: new Int(1), -// size: [1, 1, 1] as ListTag, -// structure_world_origin: [0, 0, 0] as ListTag, -// structure: { -// block_indices: [[0, 0, 0], [ -// -1, -// -1, -// -1, -// ]] as ListTag>, -// entities: [] as ListTag, -// palette: { -// default: { -// block_palette, -// block_position_data: {}, -// }, -// }, -// }, -// }; -// } - -// function placeBlock(name: string, version = 17825806) { -// return -// } - +/** + * Convert GIF / Image to .mcstructure file + * @param name - Name of .mcstructure file and structure itself + * @param frames - The GIF or image source as an array + * @param palette - The list of blocks permitted to be used in the structure + */ export async function constructDecoded( name: string, frames: GIF | Array, palette: BlockEntry[], ) { - const frameCount = frames.length; - const positionData = []; - const layer = []; - const waterLayer = []; - let idx = 0; + /** + * Block palette + */ + const blockPalette: Array<{ + version: number; + name: string; + states: Record; + }> = []; + + /** + * Block position data. First element is the position index. Second element is the block entity data. + */ + // const positionData: Array< + // [number, Record>] + // > = []; + + /** + * Structure size (X, Y, Z) + */ + const size: [number, number, number] = [ + frames[0].width, + frames[0].height, + frames.length, + ]; + + if (frames[0].width !== frames[0].height) { + const newSize = Math.max(frames[0].width, frames[0].height); + size[0] = newSize; + size[1] = newSize; + } - let xDim = 0; - let yDim = 0; + const [width, height, depth] = size; - const blockPalette = []; + /** + * Block indices primary layer + */ + const layer = Array.from({ length: width * height * depth }, () => -1); + const waterLayer = layer.slice(); - for (let z = 0; z < frameCount; z++) { + for (let z = 0; z < depth; z++) { const img = frames[z]; for (const [x, y, c] of img.iterateWithColors()) { const nearest = getNearestColor( Image.colorToRGB(c), palette)?.behaviorId ?? - "minecraft:cobblestone"; - layer.push(z, Math.abs(y - img.height), x); - waterLayer.push(-1, -1, -1); - - blockPalette.push( - { - version: BLOCK_VERSION, - name: nearest, - //states: nbt.comp({}), - }, - ); + getNearestVanillaBlock( Image.colorToRGB(c)); + + const key = (z * width * height) + (y * width) + (width - x - 1); - positionData.push([idx, { - block_entity_data: {}, - }]); + let blockIdx = blockPalette.findIndex(({ name }) => name === nearest); - xDim = Math.max(xDim, x); - yDim = Math.max(yDim, y); - idx++; + if (blockIdx === -1) { + blockIdx = blockPalette.push( + { + version: BLOCK_VERSION, + name: nearest, + states: {}, + }, + ) - 1; + } + + layer[key] = blockIdx; } } const tag = { format_version: 1, - size: [ - xDim, - yDim, - frameCount, - ], + size, structure_world_origin: [0, 0, 0], structure: { - block_indices: [layer, waterLayer], - //entities: nbt.list([]), + block_indices: [layer.filter((i) => i !== -1), waterLayer], + entities: [], palette: { default: { block_palette: blockPalette, - block_position_data: Object - .fromEntries( - positionData, - ), + block_position_data: {}, }, }, }, }; - const nbtBuffer = await NBT.write(tag, { - name, + const nbtBuffer = await NBT.write(NBT.parse(JSON.stringify(tag)), { + name: name.replace(/[\s\/]/g, "_").toLowerCase(), endian: "little", compression: null, bedrockLevel: null, @@ -154,6 +148,50 @@ export function getNearestColor( )[1]; } +export function getNearestVanillaBlock( + color: RGB, +): string { + const vanillaBlocks = Object.keys(colorDb); + const values = Object.values(colorDb); + const allowedBlocks: string[] = [ + "stone", + "cobblestone", + "dirt", + ]; + + // Reverse concrete names. For example "concrete_black" to "black_concrete" and "concrete_powder_black" to "black_concrete_powder" + const reversedVanillaBlocks = vanillaBlocks.map((block) => { + if ( + block.startsWith("concrete") || block.startsWith("wool") || + block.startsWith("stained_glass") + ) { + const [type, color] = block.split("_", 2); + return `${color}_${type}`; + } + + return block; + }); + + // Join reversed vanilla block names with the values + const reversedVanillaBlocksAndValues = vanillaBlocks.filter((block) => + allowedBlocks.includes(block) || + (block.includes("concrete") && !block.includes("powder")) || + block.includes("glass") || block.includes("wool") + ).map((block) => { + const index = vanillaBlocks.indexOf(block); + const { dominantColorHex, averageColorHex } = values[index]; + return { + resourceId: reversedVanillaBlocks[index], + behaviorId: `minecraft:${reversedVanillaBlocks[index]}`, + hexColor: () => averageColorHex ?? dominantColorHex, + } as BlockEntry; + }); + + const vanillaBlock = getNearestColor(color, reversedVanillaBlocksAndValues); + + return vanillaBlock.resourceId ?? "minecraft:air"; +} + function writeFill( x: number, y: number, @@ -195,6 +233,12 @@ export async function decode( const isLightPixel = (c: RGB) => c[0] > 0.5 && c[1] > 0.5 && c[2] > 0.5; +const convertPixelDepth = (c: RGB, limit = 15): number => { + limit = Math.max(1, Math.min(256, limit)); + const depth = c[0] * limit; + return Math.max(0, Math.min(limit, Math.round(depth))); +}; + export function convertImage( img: Image | Frame, palette: BlockEntry[], @@ -202,7 +246,8 @@ export function convertImage( axis: Axis = "z", absolutePosition = false, mask?: Image, -): string[] { + depthMap?: Image, +) { const func: string[] = []; for (const [x, y, c] of img.iterateWithColors()) { @@ -214,9 +259,13 @@ export function convertImage( continue; } + const z = depthMap + ? convertPixelDepth( Image.colorToRGB(depthMap.getPixelAt(x, y))) + : 0; + const nearest = getNearestColor( Image.colorToRGB(c), palette)?.behaviorId ?? - "cobblestone"; + getNearestVanillaBlock( Image.colorToRGB(c)); if (absolutePosition) { func.push( @@ -231,7 +280,7 @@ export function convertImage( writeFill( Math.abs((x + offset[0]) - img.width), // Starts print column at left Math.abs((y + offset[1]) - img.height), // Starts print row at top - offset[2], + z + offset[2], nearest, axis, ), @@ -241,62 +290,6 @@ export function convertImage( return func; } -async function printDecoded( - name: string, - idx: number, - img: Image | Frame, - palette: BlockEntry[], - offset: number[], -) { - const axes = ["x", "y", "z"] as const; - - // TODO: Add 10000 line limit - - return await Promise.all( - materials.flatMap(async ({ label }: IMaterial) => { - const materialPalette = palette.filter(({ material }: BlockEntry) => - label === material.label - ); - - return await Promise.all(axes.map(async (axis) => { - const func: string[] = []; - - for (const [x, y, c] of img.iterateWithColors()) { - const nearest = MASK_COLOR.includes(c) - ? "air" - : getNearestColor( Image.colorToRGB(c), materialPalette) - ?.behaviorId ?? "cobblestone"; - - func.push( - writeFill( - Math.abs((x + offset[0]) - img.width), // Starts print column at left - Math.abs((y + offset[1]) - img.height), // Starts print row at top - offset[2], - nearest, - axis, - ), - ); - } - - // FIXME: Use sprintf - const filename = `${name + (idx ? `_${idx}` : "")}_${ - label || "" - }_${axis}`; - - await Deno.writeTextFile( - join( - DIR_FUNCTIONS, - `${filename}.mcfunction`, - ), - func.join(EOL.CRLF), - ); - - return filename; - })); - }), - ); -} - export async function decodeUrl({ href }: URL): Promise { const res = await fetch(href); const data = new Uint8Array(await res.arrayBuffer()); @@ -305,48 +298,3 @@ export async function decodeUrl({ href }: URL): Promise { ? [await Image.decode(data)] : (await GIF.decode(data, false)); } - -/** - * @deprecated - */ -export async function pixelPrinter( - name: string, - imageUrl: URL, - palette: BlockEntry[], - chunks = 2, -) { - const frames = await decodeUrl(imageUrl); - - const size = Math.min(MAX_PRINT_SIZE, chunks * 16); - - let fnNames: Array = []; - let idx = 0; - const results = []; - - for await (const frame of frames) { - if (frame.width > size) { - frame.resize(size, Image.RESIZE_AUTO, Image.RESIZE_NEAREST_NEIGHBOR); - } - - // Align frames end-to-end - //const offsets = [(idx * frame.width) + 1, 0, idx + 1]; - - // Align frames as stack - const offsets = [0, 0, idx]; - - results.push( - await printDecoded( - name, - idx, - frame, - palette, - offsets, - ), - ); - idx++; - } - - fnNames = results.flat(2); - - return fnNames; -} diff --git a/src/components/_assemble.ts b/src/components/_assemble.ts index 6004c906..3f3ba7d8 100644 --- a/src/components/_assemble.ts +++ b/src/components/_assemble.ts @@ -1,4 +1,4 @@ -import type { IBlock, IMaterial } from "../../typings/types.ts"; +import type { IBlock, IMaterial } from "../../types/index.ts"; import { filteredBlocks } from "../store/_blocks.ts"; import BlockEntry from "./BlockEntry.ts"; import { materials } from "../store/_materials.ts"; diff --git a/src/components/_languages.ts b/src/components/_languages.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/_render.ts b/src/components/_render.ts index b092bccd..98b2fed0 100644 --- a/src/components/_render.ts +++ b/src/components/_render.ts @@ -1,6 +1,5 @@ -import { DIR_RP, DIR_SRC } from "../store/_config.ts"; +import { DIR_SRC } from "../store/_config.ts"; import BlockEntry from "./BlockEntry.ts"; -import { decode, Image } from "imagescript/mod.ts"; import { createCanvas, loadImage } from "canvas/mod.ts"; import { convertRgbToHsl, diff --git a/src/components/_scripts.ts b/src/components/_scripts.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/_server.ts b/src/components/_server.ts new file mode 100644 index 00000000..aa18e804 --- /dev/null +++ b/src/components/_server.ts @@ -0,0 +1,27 @@ +import type BlockEntry from "./BlockEntry.ts"; +import { camelCase } from "https://deno.land/x/case@2.2.0/mod.ts"; + +export async function writeServerVariables(res: BlockEntry[]) { + const vars: Record< + string, + Record + > = { + colors: {}, + }; + const secrets = {}; + + res.forEach((block) => { + const blockId = camelCase(block.id); + vars.colors[blockId] = block.color; + }); + + await Deno.writeTextFile( + "./server/variables.json", + JSON.stringify(vars, null, 2), + ); + + await Deno.writeTextFile( + "./server/secrets.json", + JSON.stringify(secrets, null, 2), + ); +} diff --git a/src/components/_setup.ts b/src/components/_setup.ts index 24521525..c97c62d5 100644 --- a/src/components/_setup.ts +++ b/src/components/_setup.ts @@ -1,6 +1,12 @@ import { emptyDir, ensureDir, expandGlob } from "fs/mod.ts"; import { basename, join } from "path/mod.ts"; -import { DIR_BP, DIR_DOCS, DIR_RP, DIR_SRC } from "../store/_config.ts"; +import { + DIR_BP, + DIR_DOCS, + DIR_RP, + DIR_SERVER, + DIR_SRC, +} from "../store/_config.ts"; import { decode, type Image } from "imagescript/mod.ts"; export default async function setup() { @@ -10,31 +16,37 @@ export default async function setup() { await Promise.all( [ + DIR_SERVER, `${DIR_BP}/biomes`, `${DIR_BP}/blocks`, `${DIR_BP}/items`, `${DIR_BP}/scripts`, `${DIR_BP}/scripts/client`, - `${DIR_BP}/scripts/gametests`, + // `${DIR_BP}/scripts/gametests`, `${DIR_BP}/scripts/server`, `${DIR_BP}/structures`, + `${DIR_BP}/structures/art`, `${DIR_BP}/structures/stargazers`, - `${DIR_BP}/functions`, - `${DIR_BP}/functions/tags`, - `${DIR_BP}/functions/trails`, - `${DIR_BP}/functions/printer`, - `${DIR_BP}/functions/printer/stargazers`, + // `${DIR_BP}/functions`, + // `${DIR_BP}/functions/tags`, + // `${DIR_BP}/functions/trails`, + // `${DIR_BP}/functions/printer`, + // `${DIR_BP}/functions/printer/stargazers`, `${DIR_BP}/entities`, + `${DIR_BP}/biomes`, `${DIR_BP}/animations`, `${DIR_RP}/textures/blocks`, `${DIR_RP}/models/blocks`, `${DIR_RP}/texts`, - DIR_DOCS, - `${DIR_DOCS}/assets`, - `${DIR_DOCS}/assets/blocks`, - `${DIR_DOCS}/materials`, - `${DIR_DOCS}/functions`, - `${DIR_DOCS}/flipbooks`, + `${DIR_RP}/lighting`, + `${DIR_RP}/sounds`, + `${DIR_RP}/sounds/blocks`, + // DIR_DOCS, + // `${DIR_DOCS}/assets`, + // `${DIR_DOCS}/assets/blocks`, + // `${DIR_DOCS}/materials`, + // `${DIR_DOCS}/functions`, + // `${DIR_DOCS}/flipbooks`, ].map( (dir) => ensureDir(dir), ), @@ -46,50 +58,73 @@ export default async function setup() { ), ); normalMap.resize(16, 16); - await Deno.writeFile( - join(DIR_RP, "textures", "blocks", "block_normal.png"), - await normalMap.encode(), - ); - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "big_brick_normal.png"), - join(DIR_RP, "/textures/blocks/big_brick_normal.png"), - ); + await Promise.all([ + Deno.writeFile( + join(DIR_RP, "textures", "blocks", "block_normal.png"), + await normalMap.encode(), + ), + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "big_brick_normal.png"), + join(DIR_RP, "/textures/blocks/big_brick_normal.png"), + ), + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "big_brick_mer.png"), + join(DIR_RP, "/textures/blocks/big_brick_mer.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "big_brick_mer.png"), - join(DIR_RP, "/textures/blocks/big_brick_mer.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "negative_mer.png"), + join(DIR_RP, "/textures/blocks/negative_mer.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "big_brick_lit_mer.png"), - join(DIR_RP, "/textures/blocks/big_brick_lit_mer.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "negative_normal.png"), + join(DIR_RP, "/textures/blocks/negative_normal.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "brick_normal.png"), - join(DIR_RP, "/textures/blocks/brick_normal.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "positive_mer.png"), + join(DIR_RP, "/textures/blocks/positive_mer.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "metal_normal.png"), - join(DIR_RP, "/textures/blocks/metal_normal.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "positive_normal.png"), + join(DIR_RP, "/textures/blocks/positive_normal.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "dot_normal.png"), - join(DIR_RP, "/textures/blocks/dot_normal.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "brick_normal.png"), + join(DIR_RP, "/textures/blocks/brick_normal.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "pyramid_normal.png"), - join(DIR_RP, "/textures/blocks/pyramid_normal.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "metal_normal.png"), + join(DIR_RP, "/textures/blocks/metal_normal.png"), + ), - await Deno.copyFile( - join(DIR_SRC, "assets", "materials", "planks_normal.png"), - join(DIR_RP, "/textures/blocks/planks_normal.png"), - ); + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "dot_normal.png"), + join(DIR_RP, "/textures/blocks/dot_normal.png"), + ), + + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "pyramid_normal.png"), + join(DIR_RP, "/textures/blocks/pyramid_normal.png"), + ), + Deno.copyFile( + join(DIR_SRC, "assets", "materials", "planks_normal.png"), + join(DIR_RP, "/textures/blocks/planks_normal.png"), + ), + Deno.copyFile( + join(DIR_SRC, "assets", "img", "pack_icon.png"), + join(DIR_RP, "/pack_icon.png"), + ), + Deno.copyFile( + join(DIR_SRC, "assets", "img", "pack_icon.png"), + join(DIR_BP, "/pack_icon.png"), + ), + ]); const dotMer = await decode( await Deno.readFile(join(DIR_SRC, "assets", "materials", "dot_mer.png")), @@ -107,10 +142,10 @@ export default async function setup() { join(DIR_RP, "/textures/blocks/dot_glowing_mer.png"), ); - await Deno.copyFile( - join(DIR_SRC, "assets", "models", "pane.geo.json"), - join(DIR_RP, "/models/blocks/pane.geo.json"), - ); + // await Deno.copyFile( + // join(DIR_SRC, "assets", "models", "pane.geo.json"), + // join(DIR_RP, "/models/blocks/pane.geo.json"), + // ); // Copy all textures in the assets/blocks directory for await ( @@ -165,14 +200,4 @@ export default async function setup() { ); } } - - // TODO: Generate pack icon with each build - await Deno.copyFile( - join(DIR_SRC, "assets", "img", "pack_icon.png"), - join(DIR_RP, "/pack_icon.png"), - ); - await Deno.copyFile( - join(DIR_SRC, "assets", "img", "pack_icon.png"), - join(DIR_BP, "/pack_icon.png"), - ); } diff --git a/src/components/biomes.ts b/src/components/biomes.ts new file mode 100644 index 00000000..6817ac43 --- /dev/null +++ b/src/components/biomes.ts @@ -0,0 +1,96 @@ +import { join } from "path/mod.ts"; +import { + DIR_BP, + DIR_DOCS, + DIR_RP, + MIP_LEVELS, + NAMESPACE, +} from "../store/_config.ts"; +import type BlockEntry from "../components/BlockEntry.ts"; + +function groupPalette(palette: BlockEntry[], groupBy: keyof BlockEntry) { + const groups: BlockEntry[][] = []; + + palette.forEach((block) => { + const group = groups.find((g) => g[0][groupBy] === block[groupBy]); + if (group) { + group.push(block); + return; + } + + groups.push([block]); + }); + + return groups; +} + +export async function createBiomes(palette: BlockEntry[]) { + // Calculate color temperature + const colorTemperature = (color: BlockEntry) => { + const rgb = color.hexColor().match(/\w\w/g)?.map((b) => parseInt(b, 16)); + if (!rgb) return 0; + const [r, g, b] = rgb; + return (r * 299 + g * 587 + b * 114) / 1000; + }; + + const groupedByColor = groupPalette(palette, "color"); + const groupedByMaterial = groupPalette(palette, "material"); + + if (groupedByColor.length > 155) { + console.warn( + `There are ${groupedByColor.length} colors, but only 155 biomes are supported by Minecraft.`, + ); + groupedByColor.length = 155; + } + + await Promise.all(groupedByColor.map(async (group) => { + const color = group[0].color; + // Convert color temperature to celsius + const temperature = Math.round(colorTemperature(group[0]) / 2.55) - 100; + + const biomeData = { + "format_version": "1.20.20", + "minecraft:biome": { + description: { + identifier: `biome_${color.toLowerCase().replace(/\s+/g, "_")}`, + }, + "components": { + overworld: {}, + beach: {}, + "minecraft:climate": { + temperature, + }, + "minecraft:overworld_height": { + noise_type: "river", + }, + "minecraft:surface_parameters": { + "top_material": group.filter((block) => { + return block.material.label === "rough" && block.level === 100 && + block.tint === 400; + })[0], + "sea_floor_material": group.filter((block) => { + return block.material.label === "emissive" && block.level === 50; + })[0], + "mid_material": group.filter((block) => { + return block.material.label === "glass" && block.level === 50 && + block.tint === 400; + })[0], + "floor_material": group.filter((block) => { + return block.material.label === "dot" && block.level === 50 && + block.tint === 400; + })[0], + "foundation_material": group.filter((block) => { + return block.material.label === "stud" && block.level === 50 && + block.tint === 400; + })[0], + }, + }, + }, + }; + + await Deno.writeTextFile( + `${DIR_BP}/biomes/biome_${color}.json`, + JSON.stringify(biomeData), + ); + })); +} diff --git a/src/components/compile.ts b/src/components/compile.ts index 8b7da4e4..25ea612f 100644 --- a/src/components/compile.ts +++ b/src/components/compile.ts @@ -1,21 +1,32 @@ import * as esbuild from "https://deno.land/x/esbuild@v0.15.16/wasm.js"; import { basename, extname, join } from "path/mod.ts"; import { DIR_BP, DIR_SRC } from "../store/_config.ts"; +import BlockEntry from "./BlockEntry.ts"; -export default async function compile( - src: string, - dir: "client" | "server" | "gametests" = "client", -) { +export default async function compile(src: string, { + dir = "client", + dest, + res, +}: { + dir?: "client" | "server" | "gametests"; + dest?: string; + res?: BlockEntry[]; +}) { const contents = await Deno.readTextFile(join(DIR_SRC, "scripts", src)); const name = basename(src, extname(src)); - const transformed = await esbuild.transform(contents, { - loader: "ts", - sourcemap: true, - sourcefile: src.toString(), - sourcesContent: true, - tsconfigRaw: `{ + const transformed = await esbuild.transform( + contents.replace( + /\$BLOCKS\s*=\s*\[\]/g, + `$BLOCKS = ${JSON.stringify((res ?? []).map((b) => b.serialize()))}`, + ), + { + loader: "ts", + sourcemap: true, + sourcefile: src.toString(), + sourcesContent: true, + tsconfigRaw: `{ "compilerOptions":{ "target":"es6", "moduleResolution":"node", @@ -35,7 +46,7 @@ export default async function compile( "noImplicitUseStrict":false, "outDir":"build/", "rootDir": ".", - "baseUrl":"behavior_packs/", + "baseUrl":"development_behavior_packs/", "listFiles":false, "noEmitHelpers":true }, @@ -44,16 +55,18 @@ export default async function compile( ], "compileOnSave":false }`, - }); + }, + ); await Deno.writeTextFile( - join(DIR_BP, "scripts", dir, `${name}.js`), - transformed.code, + join(dest ?? DIR_BP, "scripts", dir, `${name}.js`), + transformed.code.replace(/npm:@/g, "@"), ); await Deno.writeTextFile( - join(DIR_BP, "scripts", dir, `${name}.js.map`), + join(dest ?? DIR_BP, "scripts", dir, `${name}.js.map`), transformed.map, ); + esbuild.stop(); return `scripts/${dir}/${name}.js`; diff --git a/src/components/deferred.ts b/src/components/deferred.ts new file mode 100644 index 00000000..c916a904 --- /dev/null +++ b/src/components/deferred.ts @@ -0,0 +1,26 @@ +import { join } from "path/mod.ts"; +import { DIR_RP } from "../store/_config.ts"; +import type BlockEntry from "../components/BlockEntry.ts"; + +export async function writeDeferredLighting(palette: BlockEntry[]) { + const deferredLighting = { + format_version: [1, 0, 0], + point_lights: { + colors: Object.fromEntries( + palette.filter((block) => { + return block.material.lightEmission(1) > 0 || + block.material.emissive(1) > 0 || + block.material.label?.includes("lit") || + block.material.label?.includes("emissive"); + }).map((block) => { + return [block.behaviorId, block.hexColor()]; + }), + ), + }, + }; + + await Deno.writeTextFile( + join(DIR_RP, "lighting", "global.json"), + JSON.stringify(deferredLighting, null, 2), + ); +} diff --git a/src/components/deploy.ts b/src/components/deploy.ts index 69996aca..3c9ab03b 100644 --- a/src/components/deploy.ts +++ b/src/components/deploy.ts @@ -1,33 +1,14 @@ +import { load } from "https://deno.land/std@0.204.0/dotenv/mod.ts"; import { join } from "path/mod.ts"; import { copy, emptyDir } from "fs/mod.ts"; import { DIR_DIST, NAMESPACE } from "/src/store/_config.ts"; - +import { getConfig } from "../_utils.ts"; +const env = await load(); +const bdsPath = env.BDS_PATH ?? (Deno.env.get("BDS_PATH") || + getConfig("BDS", "")); const appData = Deno.env.get("LOCALAPPDATA") || "%LocalAppData%"; -const comMojang = join( - appData, - "Packages", - "Microsoft.MinecraftUWP_8wekyb3d8bbwe", - "LocalState", - "games", - "com.mojang", -); - -const buildBehaviorPacks = join(DIR_DIST, `${NAMESPACE} BP`); -const buildResourcePacks = join(DIR_DIST, `${NAMESPACE} RP`); - -const devBehaviorPacks = join( - comMojang, - "development_behavior_packs", - `${NAMESPACE} BP`, -); -const devResourcePacks = join( - comMojang, - "development_resource_packs", - `${NAMESPACE} RP`, -); - -export async function deployToDev() { +export async function deployToDev(preview = false) { if ( Deno.build.os !== "windows" || Deno.env.get("GITHUB_ACTIONS") !== undefined ) { @@ -36,18 +17,71 @@ export async function deployToDev() { ); } - await resetDev(); + const comMojang = join( + appData, + "Packages", + preview + ? "Microsoft.MinecraftWindowsBeta_8wekyb3d8bbwe" + : "Microsoft.MinecraftUWP_8wekyb3d8bbwe", + "LocalState", + "games", + "com.mojang", + ); + + const buildBehaviorPacks = join(DIR_DIST, `${NAMESPACE} BP`); + const buildResourcePacks = join(DIR_DIST, `${NAMESPACE} RP`); + + const devBehaviorPacks = join( + comMojang, + "development_behavior_packs", + `${NAMESPACE} BP`, + ); + const devResourcePacks = join( + comMojang, + "development_resource_packs", + `${NAMESPACE} RP`, + ); + + await resetDev([ + devBehaviorPacks, + devResourcePacks, + ]); return Promise.all([ copy(buildBehaviorPacks, devBehaviorPacks, { overwrite: true }), copy(buildResourcePacks, devResourcePacks, { overwrite: true }), ]); } -export async function resetDev() { - const paths = [ - devBehaviorPacks, - devResourcePacks, - ]; +export async function resetDev(paths: string[] = []) { await Promise.all(paths.map((dir) => emptyDir(dir))); //await Promise.all(paths.map((dir) => ensureDir(dir))); } + +export async function deployToDedicatedServer() { + if (!bdsPath) { + throw Error( + "Can not deploy to dedicated server without BDS_PATH environment variable.", + ); + } + + const bp = join( + bdsPath.toString(), + "development_behavior_packs", + `${NAMESPACE} BP`, + ); + const buildBehaviorPacks = join(DIR_DIST, `${NAMESPACE} BP`); + const buildResourcePacks = join(DIR_DIST, `${NAMESPACE} RP`); + const rp = join( + bdsPath.toString(), + "development_resource_packs", + `${NAMESPACE} RP`, + ); + await resetDev([ + bp, + rp, + ]); + return await Promise.all([ + copy(buildBehaviorPacks, bp, { overwrite: true }), + copy(buildResourcePacks, rp, { overwrite: true }), + ]); +} diff --git a/src/components/editor.ts b/src/components/editor.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/flipbook.ts b/src/components/flipbook.ts index 9f2e1cad..451f31b1 100644 --- a/src/components/flipbook.ts +++ b/src/components/flipbook.ts @@ -10,7 +10,7 @@ import type { MinecraftTerrainData, PackSizes, RGB, -} from "../../typings/types.ts"; +} from "../../types/index.ts"; import { DIR_BP, DIR_DOCS, DIR_RP } from "../store/_config.ts"; import { materials } from "../store/_materials.ts"; @@ -60,10 +60,10 @@ async function flipbookData( return { flipbook_texture: `textures/blocks/${flipbookBlock.id}`, - frames: flipbookFrames, - atlas_tile: flipbookBlock.id, - ticks_per_frame: 1, //Math.min(10, Math.floor(frameCount * 1.666)), - blend_frames: false, + // frames: flipbookFrames, + atlas_tile: flipbookBlock.resourceId, + ticks_per_frame: 10, + // blend_frames: true, }; } @@ -139,6 +139,16 @@ export async function writeFlipbooks( await makeAtlas(frames), ); + // await Deno.writeFile( + // join( + // DIR_DOCS, + // "flipbooks", + // formatFlipbookName(lastBlock.color, lastBlock.material) + // .trim().replace(/[_ ]+/g, "_") + ".gif", + // ), + // await makeGif(frames), + // ); + const flipMaterials = materials.filter(({ label }: IMaterial) => !label?.startsWith("glass") ); diff --git a/src/components/manifest.ts b/src/components/manifest.ts index 32b15f57..7c4f2f74 100644 --- a/src/components/manifest.ts +++ b/src/components/manifest.ts @@ -1,12 +1,15 @@ import { inc, type ReleaseType } from "semver/mod.ts"; import { join } from "path/mod.ts"; -import type { PackModule } from "../../typings/types.ts"; +import type { PackModule } from "../../types/index.ts"; import { BP_MODULE_UUID, BP_PACK_UUID, DIR_BP, - DIR_ROOT, DIR_RP, + EDITOR_BP_MODULE_UUID, + EDITOR_RP_MODULE_UUID, + MODULE_SERVER_UI_VERSION, + MODULE_SERVER_VERSION, PACK_DESCRIPTION, PACK_NAME, RP_MODULE_UUID, @@ -17,7 +20,6 @@ import { semverVector } from "../_utils.ts"; import { DIR_SRC } from "../store/_config.ts"; async function getBuildVersion( - releaseType: ReleaseType = "patch", defaultVersion = "1.0.0", ): Promise<{ RP: number[]; BP: number[] }> { const { RP, BP } = JSON.parse( @@ -26,27 +28,26 @@ async function getBuildVersion( return { RP: semverVector( - inc(RP || defaultVersion, releaseType) ?? defaultVersion, + RP || defaultVersion, ), BP: semverVector( - inc(BP || defaultVersion, releaseType) ?? defaultVersion, + BP || defaultVersion, ), }; } export async function createManifests( scripts?: Array, - releaseType?: ReleaseType, ) { - const { RP: rpVersion, BP: bpVersion } = await getBuildVersion(releaseType); + const { RP: rpVersion, BP: bpVersion } = await getBuildVersion(); const metadata = { authors: [ "Jason J. Gardner", ], - // generated_with: { - // rainbow_magic: rpVersion.map((v) => v.toString()), - // }, + generated_with: { + rainbow_magic: [rpVersion.join(".")], + }, } as const; await Deno.writeTextFile( @@ -75,7 +76,7 @@ export async function createManifests( version: bpVersion, }, ], - capabilities: ["raytraced"], + capabilities: ["raytraced", "pbr"], metadata, }, null, @@ -102,24 +103,23 @@ export async function createManifests( if (scripts && scripts.length) { dependencies.push({ module_name: "@minecraft/server", - version: "1.0.0-beta", + version: "1.7.0-beta", }, { - module_name: "@minecraft/server-gametest", - version: "1.0.0-beta", - }, { - module_name: "@minecraft/server-ui", + module_name: "@minecraft/server-net", version: "1.0.0-beta", }); for (const script of scripts) { + const v = script.version?.toString().split(".").slice(0, 3).map((v) => + parseInt(v, 10) + ); + modules.push({ ...script, ...{ type: script.type ?? "script", language: script.language ?? "javascript", uuid: script.uuid ?? crypto.randomUUID(), - version: (script.version ?? "1.0.0").toString().split(".").map((v) => - parseInt(v, 10) - ).slice(0, 3), + version: [v?.[0] ?? 1, v?.[1] ?? 0, v?.[2] ?? 0], }, }); } @@ -140,11 +140,78 @@ export async function createManifests( modules, dependencies, metadata, + // capabilities: [ + // "editorExtension", + // ], }, null, 2, ), ); + // Editor manifests + + // await Deno.writeTextFile( + // join(DIR_RP, "manifest.json"), + // JSON.stringify( + // { + // format_version: 2, + // header: { + // name: PACK_NAME, + // description: PACK_DESCRIPTION, + // uuid: , + // version: rpVersion, + // min_engine_version: TARGET_VERSION, + // }, + // modules: [ + // { + // description: `${PACK_NAME} generated textures`, + // type: "resources", + // uuid: EDITOR_RP_MODULE_UUID, + // version: rpVersion, + // }, + // ], + // dependencies: [ + // { + // uuid: BP_PACK_UUID, + // version: bpVersion, + // }, + // ], + // metadata, + // }, + // null, + // 2, + // ), + // ); + + // dependencies.push({ + // module_name: "@minecraft/server-editor", + // version: "0.1.0-beta" + // }); + + // await Deno.writeTextFile( + // join(DIR_BP, "manifest.json"), + // JSON.stringify( + // { + // format_version: 2, + // header: { + // name: `${PACK_NAME} Editor Behavior Pack`, + // description: `${PACK_NAME} editor data dependency`, + // uuid: , + // version: bpVersion, + // min_engine_version: TARGET_VERSION, + // }, + // modules, + // dependencies, + // metadata, + // capabilities: [ + // "editorExtension", + // ], + // }, + // null, + // 2, + // ), + // ); + return { RP: rpVersion.join("."), BP: bpVersion.join(".") }; } diff --git a/src/components/models/index.ts b/src/components/models/index.ts new file mode 100644 index 00000000..34cf5f0b --- /dev/null +++ b/src/components/models/index.ts @@ -0,0 +1,26 @@ +class Vector { + x: number; + y: number; + z: number; + constructor(x: number, y: number, z: number) { + this.x = Math.round(x); + this.y = Math.round(y); + this.z = Math.round(z); + } +} + +class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r: number, g: number, b: number, a = 1) { + this.r = Math.max(0, Math.min(255, r)); + this.g = Math.max(0, Math.min(255, g)); + this.b = Math.max(0, Math.min(255, b)); + this.a = a; + } +} + +export { Color, Vector }; diff --git a/src/components/printer.ts b/src/components/printer.ts index 4aa3653b..4bc09625 100644 --- a/src/components/printer.ts +++ b/src/components/printer.ts @@ -1,19 +1,17 @@ import "dotenv/load.ts"; -import { decode, type GIF, type Image } from "imagescript/mod.ts"; -import { basename, extname, toFileUrl } from "path/mod.ts"; -import { walk } from "fs/walk.ts"; +import { basename, extname, join } from "path/mod.ts"; +import { decode, type GIF, Image } from "imagescript/mod.ts"; +import { ensureDir, walk } from "fs/mod.ts"; import { Octokit } from "https://cdn.skypack.dev/@octokit/core"; import BlockEntry from "./BlockEntry.ts"; -import { constructDecoded, pixelPrinter } from "./ImagePrinter.ts"; -import { DIR_PIXEL_ART } from "/src/store/_config.ts"; -import { image as markdownImage, Markdown } from "deno_markdown/mod.ts"; - +import { constructDecoded } from "./ImagePrinter.ts"; +import { DIR_BP, DIR_PIXEL_ART } from "/src/store/_config.ts"; +const DIR_STRUCTURES = join(DIR_BP, "structures"); async function githubAvatars( owner: string, repo: string, palette: BlockEntry[], -): Promise { - const sponsorChunkSize = 4; +): Promise { const octokit = new Octokit(); const { status, data } = await octokit.request( @@ -29,9 +27,17 @@ async function githubAvatars( throw Error("Failed fetching GitHub data"); } - const markdown = new Markdown(); + // const markdown = new Markdown(); + + // markdown.header("GitHub Stargazers", 3); - markdown.header("GitHub Stargazers", 3); + const materials = [ + ...new Set( + palette.map(({ material }: BlockEntry) => + material.label ?? material.name.en_US + ), + ), + ]; await Promise.allSettled( data.map(async ( @@ -41,50 +47,47 @@ async function githubAvatars( [key: string]: string | boolean; }, ) => { - try { - const fns = await pixelPrinter( - `stargazers/${res.login}`, - new URL(res.avatar_url), - palette, - sponsorChunkSize, - ); - - markdown.header(res.login, 5).paragraph( - markdownImage(res.login, res.avatar_url), - ); - fns.forEach((fn) => - markdown.codeBlock(`/function printer/${fn}`, "mcfunction") - ); - } catch (err) { - console.error('Failed printing name: "%s"; Reason: %s', err); - } - try { const data = new Uint8Array( await (await fetch(res.avatar_url)).arrayBuffer(), ); - const avatar = (await decode(data, true)) as Image; + const avatar = ((await decode(data, true)) as Image).resize(64, 64) + .rotate(90); + + await ensureDir( + join( + DIR_STRUCTURES, + "stargazers", + res.login, + ), + ); + await Promise.all(materials.map(async (material) => { + console.log("Constructing %s avatar for %s", material, res.login); + + await constructDecoded( + `stargazers/${res.login}/${res.login}_${material}`, + [avatar], + palette.filter(({ material: { label } }: BlockEntry) => + label === material + ), + ); + })); - constructDecoded( - `stargazers/${res.login}`, - [avatar.resize(64, 64)], - palette, + await constructDecoded( + `stargazers/${res.login}/${res.login}_vanilla`, + [avatar], + [], ); } catch (err) { console.error('Failed constructing name: "%s"; Reason: %s', err); } }), ); - - return markdown.content; } export default async function print( palette: BlockEntry[], - chunks = 6, -): Promise { - const markdown = new Markdown(); - +): Promise { /** * Block palette containing filtered materials. Excludes very bright blocks and lower levels. */ @@ -92,47 +95,58 @@ export default async function print( material.label !== "emissive" || level <= 60 ); - //const printChunks = Math.max(1, Math.min(16, chunks)); - try { - markdown.header("Pixel Art", 3); + const materials = [ + ...new Set( + palette.map(({ material }: BlockEntry) => + material.label ?? material.name.en_US + ), + ), + ]; // Print images in pixel_art directory for await (const entry of walk(DIR_PIXEL_ART)) { if (entry.isFile) { const name = basename(entry.path, extname(entry.path)); - const remoteUrl = new URL( - `https://raw.githubusercontent.com/jasonjgardner/minecraft-rtx-rainbow/main/src/assets/pixel_art/${entry.path}`, + await ensureDir( + join( + DIR_STRUCTURES, + "art", + name, + ), ); - markdown.header(name, 5).paragraph(markdownImage(name, remoteUrl.href)); - - try { - const data = await Deno.readFile(entry.path); - const decoded = await decode(data); - const img = extname(entry.path) === ".gif" - ? ( decoded) - : [ decoded]; + const data = await Deno.readFile(entry.path); + const decoded = await decode(data); + const img = extname(entry.path) === ".gif" + ? ( decoded) + : [ decoded]; - constructDecoded( - `art_${basename(entry.path, extname(entry.path))}`, - img, - palette, - ); - } catch (err) { - console.error(`Failed constructing ${name}: "%s"`, err); - } - - const fns = await pixelPrinter( - name, - toFileUrl(entry.path), - printablePalette, - chunks, + const imgResized = img.map((img) => + img.resize(Image.RESIZE_AUTO, Math.min(16 * 8, img.height)) ); - fns.forEach((fn) => - markdown.codeBlock(`/function printer/pixel_art/${fn}`, "mcfunction") + await Promise.all(materials.map(async (material) => { + try { + console.log("Constructing artwork %s from %s", name, material); + + await constructDecoded( + `art/${name}/${name}_${material}`, + imgResized, + palette.filter(({ material: { label } }: BlockEntry) => + label === material + ), + ); + } catch (err) { + console.error(`Failed constructing ${name}: "%s"`, err); + } + })); + + await constructDecoded( + `art/${name}/${name}_vanilla`, + img, + [], ); } } @@ -153,7 +167,7 @@ export default async function print( if (actionRepo.length > 1) { try { // Print images from GitHub API - markdown.content += await githubAvatars( + await githubAvatars( actionRepo[0], actionRepo[1], printablePalette, @@ -162,6 +176,4 @@ export default async function print( console.error("Failed adding Stargazers: %s", err); } } - - return markdown.content; } diff --git a/src/components/sounds.test.ts b/src/components/sounds.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/components/sounds.ts b/src/components/sounds.ts new file mode 100644 index 00000000..cc493d2c --- /dev/null +++ b/src/components/sounds.ts @@ -0,0 +1,217 @@ +import type BlockEntry from "./BlockEntry.ts"; +import { join } from "$std/path/mod.ts"; +import { DIR_RP, NAMESPACE } from "../store/_config.ts"; +import { Image } from "imagescript/mod.ts"; + +const NOTES_FREQUENCIES: Record = { + "C": 261.63, + "C#": 277.18, + "D": 293.66, + "D#": 311.13, + "E": 329.63, + "F": 349.23, + "F#": 369.99, + "G": 392.00, + "G#": 415.30, + "A": 440.00, + "A#": 466.16, + "B": 493.88, +}; + +// Convert a note to its major triad frequencies +function noteToMajorTriad(note: string): number[] { + const notes = Object.keys(NOTES_FREQUENCIES); + const index = notes.indexOf(note); + if (index === -1) throw new Error("Note not found"); + + const third = notes[(index + 4) % 12]; + const fifth = notes[(index + 7) % 12]; + + return [ + NOTES_FREQUENCIES[note], + NOTES_FREQUENCIES[third], + NOTES_FREQUENCIES[fifth], + ]; +} + +// Generate tone using FFmpeg and save as OGG +async function generateAndSave( + filename: string, + chordFrequencies: number[], + duration: number, +) { + if (chordFrequencies.some((f) => isNaN(f))) { + throw new Error("Frequency must be a number"); + } + + const audioSources = chordFrequencies.map((freq, idx) => + `aevalsrc=0.5*sin(2*PI*${freq}*t):d=${duration}[a${idx}]` + ).join(";"); + + const process = new Deno.Command("ffmpeg.exe", { + args: [ + "-filter_complex", + `${audioSources};${ + chordFrequencies.map((_, idx) => `[a${idx}]`).join("") + }amix=inputs=${chordFrequencies.length}`, + "-c:a", + "libopus", + `${filename}.ogg`, + ], + }); + + const { stdout, stderr } = await process.output(); + + const decoder = new TextDecoder(); + if (stderr) { + console.error(decoder.decode(stderr)); + } + + if (stdout) { + console.log(decoder.decode(stdout)); + } +} + +async function hexColorToAudioChord(block: BlockEntry) { + const hexColor = block.hexColor().toUpperCase().replace("#", ""); + const noteIndex = parseInt(hexColor, 16) % 12; + + // Change octave based on material + // const octave = Math.floor(block.material.minimumLevel / 100) + 1; + + const note = Object.keys(NOTES_FREQUENCIES)[noteIndex]; + // let note = Object.keys(NOTES_FREQUENCIES)[noteIndex]; + // if (octave > 1) { + // note = `${note}${octave}`; + // } + + // Convert note to major triad chord frequencies + const chordFrequencies = noteToMajorTriad(note); + + const duration = Math.max(1, Math.min(3, ((block.level / 900) / 100) * 2)); + + // Generate and save audio for the chord + await generateAndSave( + join(DIR_RP, "sounds", "blocks", `${block.resourceId}`), + chordFrequencies, + duration, + ); +} + +function audioChordToHexColor(chord: string[]): string { + const frequencies = chord.map((c) => { + const notes = c.split(",").map((n) => n.trim()); + + const freqs = notes.map((n) => { + const noteIndex = Object.keys(NOTES_FREQUENCIES).indexOf(n); + if (noteIndex !== -1) { + return Object.values(NOTES_FREQUENCIES)[noteIndex]; + } + + return 0; + }); + + return freqs.reduce((a, b) => a + b, 0); + }); + + const color = frequencies.reduce((a, b) => a + b, 0).toString(16); + + return color; +} + +function imageFromChords(chords: string[][]) { + const image = (new Image(100, 100)).fill(0x000000); + + chords.forEach((chord, idx) => { + const color = audioChordToHexColor(chord); + + image.setPixelAt(idx, 0, parseInt(color, 16)); + }); + + return image.encode(); +} + +export default async function generateSounds(res: BlockEntry[]) { + const defs: Record; + }> = {}; + + res.forEach(async (block) => { + const id = block.behaviorId.replace(":", "."); + + if (!defs[id]) { + defs[id] = { + category: "block", + sounds: [], + }; + } + + defs[id].sounds.push({ + name: `blocks/${block.resourceId}`, + volume: Math.max(0.01, +((block.level / 900) / 100).toFixed(3)), + }); + + await hexColorToAudioChord(block); + }); + + await Deno.writeTextFile( + join(DIR_RP, "sounds", "sound_definitions.json"), + JSON.stringify( + { + format_version: "1.14.0", + sound_definitions: defs, + }, + null, + 2, + ), + ); +} + +if (import.meta.main) { + // Generate image of "Mary had a little lamb" + const chords = [ + [ + "E,G#,B", + "D,G,B", + "C,E,G", + "D,G,B", + "E,G,B", + "E,G,B", + "E,G,B", + "D,G,B", + "D,G,B", + "D,G,B", + "E,G,B", + "G,B,D", + "G,B,D", + "E,G,B", + "D,G,B", + "C,E,G", + ], + [ + "E,G,B", + "D,G,B", + "C,E,G", + "D,G,B", + "E,G,B", + "E,G,B", + "E,G,B", + "D,G,B", + "D,G,B", + "D,G,B", + "E,G,B", + "G,B,D", + "G,B,D", + "E,G,B", + "D,G,B", + "C,E,G", + ], + ]; + + const image = await imageFromChords(chords); + await Deno.writeFile("test.png", image); +} diff --git a/src/mod.ts b/src/mod.ts index 5cf2b795..3629bbaf 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -2,7 +2,7 @@ import "dotenv/load.ts"; import { join } from "path/mod.ts"; import { sprintf } from "fmt/printf.ts"; import { EOL } from "fs/mod.ts"; -import { Markdown } from "deno_markdown/mod.ts"; +// import { Markdown } from "deno_markdown/mod.ts"; import type { Color, FlipbookComponent, @@ -11,24 +11,18 @@ import type { MinecraftData, MinecraftTerrainData, PackModule, -} from "/typings/types.ts"; +} from "/types/index.ts"; import { DIR_BP, DIR_DOCS, DIR_RP, MIP_LEVELS, NAMESPACE, - RELEASE_TYPE, } from "./store/_config.ts"; import { getConfig } from "/src/_utils.ts"; import BlockEntry from "./components/BlockEntry.ts"; -import { - colorTrails, - entityTrailFunction, - rainbowTrailFunction, -} from "./components/mcfunctions.ts"; import { writeFlipbooks } from "./components/flipbook.ts"; -import { deployToDev } from "./components/deploy.ts"; +import { deployToDedicatedServer, deployToDev } from "./components/deploy.ts"; import setup from "./components/_setup.ts"; //import { createItems } from "./components/items.ts"; import { createManifests } from "./components/manifest.ts"; @@ -36,7 +30,11 @@ import print from "./components/printer.ts"; import render from "./components/_render.ts"; import assemble from "./components/_assemble.ts"; import compile from "./components/compile.ts"; +// import { createBiomes } from "./components/biomes.ts"; +import generateSounds from "./components/sounds.ts"; +import { writeDeferredLighting } from "./components/deferred.ts"; +const kv = await Deno.openKv(); let textureData: MinecraftTerrainData = {}; const itemTextureData: MinecraftTerrainData = {}; let blocksData: MinecraftData = {}; @@ -45,10 +43,25 @@ let languages: LanguagesContainer = { en_US: [], }; -const excludeMaterials = (getConfig("excludeMaterials", "glass_pane") ?? "") - .toString().split(","); +const excludeMaterials = + (getConfig("excludeMaterials", "glass_pane,brick_lit") ?? "") + .toString().split(","); + +const stored = + (await kv.get>>(["blocks"]))?.value; + +const lib = stored?.map((storedBlock) => BlockEntry.deserialize(storedBlock)) ?? + undefined; -const res = assemble(excludeMaterials); +const res: BlockEntry[] = lib ?? assemble(excludeMaterials); + +if (!lib && res.length > 0) { + res.forEach(async (block) => { + await kv.set([NAMESPACE, "blocks", block.id], block.serialize(), { + expireIn: 60 * 60, + }); + }); +} await setup(); @@ -67,17 +80,17 @@ const blockLibrary: Record = {}; const len = res.length; -const blocksTableHeader = [ - "Block", - "ID", - "Material", - "Color", - "Tint", - "Level", - "Preview", -]; +// const blocksTableHeader = [ +// "Block", +// "ID", +// "Material", +// "Color", +// "Tint", +// "Level", +// "Preview", +// ]; -const blocksTableData: Record> = {}; +// const blocksTableData: Record> = {}; for (let itr = 0; itr < len; itr++) { const block: BlockEntry = res[itr]; @@ -98,64 +111,36 @@ for (let itr = 0; itr < len; itr++) { ); } - /// Write behavior block - await Deno.writeTextFile( - `${DIR_BP}/blocks/${block.id}.json`, - block.toString( - itr === 0 ? res[len - 1] : res[itr - 1], - itr === len - 1 ? res[0] : res[itr + 1], - ), - ); - - const rendered = await render(block, 16); + const rendered = await render(block, 32); if (!rendered) { throw new Error(`Failed to render block: ${block.id}`); } - await Deno.writeFile( - `${DIR_RP}/textures/blocks/${block.id}.png`, - rendered, - ); - - await Deno.writeFile( - `${DIR_DOCS}/assets/blocks/${block.resourceId}.png`, - rendered, - ); - - const blockMaterial = block.material?.name[ "en_US"] ?? "Generic"; - - if (!blocksTableData[blockMaterial]) { - blocksTableData[blockMaterial] = []; - } - - blocksTableData[blockMaterial].push([ - block.name[ "en_US"], - block.behaviorId, - blockMaterial, - block.color, - `${block.tint}`, - `${block.level}`, - `![${block.resourceId}](../assets/blocks/${block.resourceId}.png)`, - ]); - - /// Write texture - await Deno.writeTextFile( - `${DIR_RP}/textures/blocks/${block.id}.texture_set.json`, - JSON.stringify( - { - format_version: "1.16.100", - "minecraft:texture_set": block.textureSet, - }, - null, - 2, + await Promise.all([ + Deno.writeFile( + `${DIR_RP}/textures/blocks/${block.id}.png`, + rendered, ), - ); - - /// Add to functions - // mcfunctions.rainbowstack.push( - // `setblock ${block.setPosition(itr)} ${block.behaviorId}`, - // ); + Deno.writeTextFile( + `${DIR_RP}/textures/blocks/${block.id}.texture_set.json`, + JSON.stringify( + { + format_version: "1.16.100", + "minecraft:texture_set": block.textureSet, + }, + null, + 2, + ), + ), + Deno.writeTextFile( + `${DIR_BP}/blocks/${block.id}.json`, + block.toString( + itr === 0 ? res[len - 1] : res[itr - 1], + itr === len - 1 ? res[0] : res[itr + 1], + ), + ), + ]); if ( atlasGroup.length > 1 && @@ -164,7 +149,6 @@ for (let itr = 0; itr < len; itr++) { ) { let flipbooksJson; // FIXME: Dumbass dependencies injection - // TODO: Create GIF preview for docs [blocksData, textureData, languages, flipbooksJson] = await writeFlipbooks( atlasGroup, { @@ -184,112 +168,98 @@ for (let itr = 0; itr < len; itr++) { lastColor = block.color; atlasGroup.push(block); } - -for (const key in blocksTableData) { - (new Markdown().header(key, 1).table([ - blocksTableHeader, - ...blocksTableData[key], - ])).write( - `${DIR_DOCS}/materials/`, - key, - ); -} - -const tickers: string[] = []; - -await Deno.writeTextFile( - `${DIR_BP}/functions/tick.json`, - JSON.stringify({ - "values": tickers, - }), -); - -await Deno.writeTextFile( - `${DIR_BP}/functions/rainbow_trail.mcfunction`, - rainbowTrailFunction(), -); - -await Deno.writeTextFile( - `${DIR_BP}/functions/entity_trail.mcfunction`, - await entityTrailFunction(blockLibrary), -); - -const colorFunctions = [...new Set(await colorTrails(blockLibrary))]; -const colorTrailsDoc = new Markdown().header("Color Trails", 1); -colorTrailsDoc.codeBlock( - colorFunctions.map((fn) => `/function ${fn}`).join(EOL.CRLF), - "mcfunction", -); -colorTrailsDoc.write(`${DIR_DOCS}/functions/`, "color_trails"); - -await Deno.writeTextFile( - join(DIR_RP, "blocks.json"), - JSON.stringify({ format_version: [1, 1, 0], ...blocksData }, null, 2), -); -await Deno.writeTextFile( - join(DIR_RP, "/textures/terrain_texture.json"), - JSON.stringify( - { - num_mip_levels: MIP_LEVELS, - padding: MIP_LEVELS * 2, - resource_pack_name: NAMESPACE, - texture_name: "atlas.terrain", - texture_data: textureData, - }, - null, - 2, - ), +let flipbooksJson; +[blocksData, textureData, languages, flipbooksJson] = await writeFlipbooks( + atlasGroup, + { + blocksData, + textureData, + languages, + }, ); -await Deno.writeTextFile( - join(DIR_RP, "/textures/flipbook_textures.json"), - JSON.stringify(flipbooks.flat(), null, 2), -); +if (flipbooksJson) { + flipbooks.push(flipbooksJson); +} -await Deno.writeTextFile( - `${DIR_RP}/textures/item_texture.json`, - JSON.stringify( - { - resource_pack_name: NAMESPACE, - texture_name: "atlas.items", - texture_data: itemTextureData, - }, - null, - 2, - ), -); +const languageQueue = []; for (const languageKey in languages) { - await Deno.writeTextFile( + languageQueue.push(Deno.writeTextFile( `${DIR_RP}/texts/${languageKey}.lang`, languages[ languageKey].join(EOL.CRLF), - ); -} - -await Deno.writeTextFile( - `${DIR_RP}/texts/languages.json`, - JSON.stringify(Object.keys(languages)), -); - -try { - await print(res); -} catch (e) { - console.error(e); + )); } -const scripts: Array = []; +await Promise.all([ + ...languageQueue, + Deno.writeTextFile( + `${DIR_RP}/texts/languages.json`, + JSON.stringify(Object.keys(languages)), + ), + Deno.writeTextFile( + join(DIR_RP, "blocks.json"), + JSON.stringify({ format_version: [1, 1, 0], ...blocksData }, null, 2), + ), + Deno.writeTextFile( + join(DIR_RP, "/textures/terrain_texture.json"), + JSON.stringify( + { + num_mip_levels: MIP_LEVELS, + padding: MIP_LEVELS * 2, + resource_pack_name: NAMESPACE, + texture_name: "atlas.terrain", + texture_data: textureData, + }, + null, + 2, + ), + ), + Deno.writeTextFile( + join(DIR_RP, "/textures/flipbook_textures.json"), + JSON.stringify(flipbooks.flat(1), null, 2), + ), + Deno.writeTextFile( + `${DIR_RP}/textures/item_texture.json`, + JSON.stringify( + { + resource_pack_name: NAMESPACE, + texture_name: "atlas.items", + texture_data: itemTextureData, + }, + null, + 2, + ), + ), + writeDeferredLighting(res), + print(res), + // generateSounds(res), +]); // try { -// scripts.push({ -// entry: await compile("hello.ts"), -// version: [1, 0, 0], -// }); +// await createBiomes(res); // } catch (err) { -// console.error("Failed compiling script: %s", err); +// console.error(err); // } -await createManifests(scripts, RELEASE_TYPE); +const scripts: Array = []; + +try { + scripts.push({ + entry: await compile("main.ts", { + res, + }), + version: [1, 0, 0], + }); +} catch (err) { + console.error("Failed compiling script: %s", err); +} + +await createManifests(scripts); if (getConfig("DEPLOY", "false") !== "false") { - await deployToDev(); + await Promise.all([ + deployToDev(getConfig("preview", "false") !== "false"), + deployToDedicatedServer(), + ]); } diff --git a/src/scripts/main.ts b/src/scripts/main.ts new file mode 100644 index 00000000..def2db9f --- /dev/null +++ b/src/scripts/main.ts @@ -0,0 +1,158 @@ +/// @deno-types="npm:@types/mojang-gametest" +/// @deno-types="npm:@minecraft/server" +/// @deno-types="npm:@minecraft/server-net" +import { + type Block, + type Entity, + GameMode, + type Player, + system, + world, +} from "@minecraft/server"; +import { + http, + HttpClient, + HttpHeader, + HttpRequest, + HttpRequestMethod, + type HttpResponse, +} from "@minecraft/server-net"; +const $BLOCKS = []; +const SERVER_URL = "http://127.0.0.1:8000/api/bds/commands"; +let tickSpeed = 10; + +function getBlockName(color: string, material: string, level = 50, tint = 500) { + return `rainbow:${color}_${tint}_${material}_${level}`; +} + +let processing = false; + +function requestCommands(params: any) { + const request = (new HttpRequest(SERVER_URL)).setMethod( + HttpRequestMethod.POST, + ) + .setBody(JSON.stringify(params)).setHeaders([ + new HttpHeader("Content-Type", "application/json"), + ]); + + request.body = JSON.stringify(params); + request.method = HttpRequestMethod.POST; + + return request; +} + +function processCommandResponse({ body }: HttpResponse) { + const overworld = world.getDimension("overworld"); + const players = overworld.getPlayers({ + excludeGameModes: [GameMode.spectator], + }); + const { command, target } = JSON.parse(body); + const commands = command.split(";"); + for (const player of players) { + if (player.name !== target) { + continue; + } + + for (const command of commands) { + player.runCommand(command); + } + } + + processing = false; +} + +function getHttpCommand() { + processing = true; + // HttpClient.prototype.request(requestCommands({ + // players, + // })) + http.get(SERVER_URL).then(processCommandResponse).catch((err) => { + console.warn(err); + processing = false; + }); +} + +// function mainTick() { + +// system.run(mainTick); +// } + +// system.run(mainTick); + +system.runInterval(function intervalTick() { + getHttpCommand(); +}, tickSpeed); + +world.afterEvents.projectileHitBlock.subscribe((hitEvent) => { + console.log( + `Hit ${hitEvent.location.x}, ${hitEvent.location.y}, ${hitEvent.location.z}`, + ); + http.get( + `${SERVER_URL}/hit?x=${hitEvent.location.x}&y=${hitEvent.location.y}&z=${hitEvent.location.z}`, + ).then(processCommandResponse).catch((err) => { + console.warn(err); + processing = false; + }); +}); + +world.afterEvents.chatSend.subscribe(({ message, sender, getTargets }: { + message: string; + sender: Player; + getTargets: () => Player[]; +}) => { + http.get( + `${SERVER_URL}/chat?message=${ + encodeURIComponent(message) + }&sender=${sender.name}&targets=${ + getTargets()?.map( + (player: Player) => player.name, + ) + }`, + ).then(processCommandResponse).catch((err) => { + console.warn(err); + processing = false; + }); +}); + +world.afterEvents.playerPlaceBlock.subscribe(({ block, player }: { + block: Block; + player: Player; +}) => { + http.get( + `${SERVER_URL}/block?x=${block.x}&y=${block.y}&z=${block.z}&player=${player.name}`, + ).then(processCommandResponse).catch((err) => { + console.warn(err); + processing = false; + }); +}); + +system.afterEvents.scriptEventReceive.subscribe(({ + id, + message, + sourceBlock, + sourceEntity, + sourceType, + initiator, +}: { + id: string; + message: string; + sourceBlock: Block; + sourceEntity: Entity; + sourceType: string; + initiator: Entity; +}) => { + console.log( + `Script Event: ${id} ${message} ${sourceBlock} ${sourceEntity} ${sourceType} ${initiator}`, + ); + http.request(requestCommands({ + id, + message, + sourceBlock, + sourceEntity, + sourceType, + initiator, + })).then(processCommandResponse).catch((err) => { + console.warn(err); + processing = false; + }); +}); diff --git a/src/store/_blocks.ts b/src/store/_blocks.ts index bab8eefb..8d623eff 100644 --- a/src/store/_blocks.ts +++ b/src/store/_blocks.ts @@ -1,4 +1,4 @@ -import type { IBlock } from "../../typings/types.ts"; +import type { IBlock } from "../../types/index.ts"; const blocks: IBlock[] = [ { name: "red_50", enabled: true, color: "#ffebee" }, { name: "red_100", enabled: true, color: "#ffcdd2" }, @@ -256,8 +256,8 @@ const blocks: IBlock[] = [ { name: "grey_900", enabled: true, color: "#212121" }, ]; -export const filteredBlocks = Object.values(blocks).filter(({ enabled }: IBlock) => - enabled === true -); +export const filteredBlocks = Object.values(blocks).filter(( + { enabled }: IBlock, +) => enabled === true); export default blocks; diff --git a/src/store/_config.ts b/src/store/_config.ts index 58f0a02d..ba91155c 100644 --- a/src/store/_config.ts +++ b/src/store/_config.ts @@ -20,6 +20,10 @@ export const DIR_DIST = join(DIR_ROOT, "/build"); export const DIR_DOCS = join(DIR_DIST, "/docs"); +export const DIR_SERVER = join(DIR_DIST, "/bds"); + +export const DIR_EDITOR = join(DIR_DIST, "/editor"); + function getUuid(rp = true, pack = true): string { const key = `${rp ? "RP" : "BP"}_${pack ? "PACK" : "MODULE"}_UUID`; const envVar = Deno.env.get(key); @@ -45,6 +49,12 @@ export const BP_PACK_UUID = getUuid(false); export const RP_MODULE_UUID = getUuid(true, false); export const BP_MODULE_UUID = getUuid(false, false); +export const EDITOR_RP_MODULE_UUID = Deno.env.get("EDITOR_RP_MODULE_UUID") ?? + crypto.randomUUID(); + +export const EDITOR_BP_MODULE_UUID = Deno.env.get("EDITOR_BP_MODULE_UUID") ?? + crypto.randomUUID(); + export const NAMESPACE = `${getConfig("NAMESPACE", "rainbow")}`; export const BEHAVIOR_BLOCK_FORMAT_VERSION = "1.19.40"; @@ -59,10 +69,19 @@ export const PACK_DESCRIPTION = getConfig( export const DIR_RP = join(DIR_DIST, `/${NAMESPACE} RP`); export const DIR_BP = join(DIR_DIST, `/${NAMESPACE} BP`); +export const DIR_EDITOR_RP = join(DIR_EDITOR, `/${NAMESPACE} Editor RP`); +export const DIR_EDITOR_BP = join(DIR_EDITOR, `/${NAMESPACE} Editor BP`); + export const TARGET_VERSION = semverVector( - Deno.env.get("TARGET_VERSION") || "1.19.50", + Deno.env.get("TARGET_VERSION") || "1.20.40", ); +export const MODULE_SERVER_VERSION = "1.7.0"; + +export const MODULE_SERVER_GAMETEST_VERSION = "1.0.0-beta"; + +export const MODULE_SERVER_UI_VERSION = "1.2.0-beta"; + const ART_DIR = getConfig("ART_DIR"); export const DIR_PIXEL_ART = typeof ART_DIR === "string" @@ -71,4 +90,4 @@ export const DIR_PIXEL_ART = typeof ART_DIR === "string" export const RELEASE_TYPE = getConfig("RELEASE_TYPE", "patch"); -export const BLOCK_VERSION = 17959425; +export const BLOCK_VERSION = 18090528; diff --git a/src/store/_materials.ts b/src/store/_materials.ts index b745935a..cfa9f67e 100644 --- a/src/store/_materials.ts +++ b/src/store/_materials.ts @@ -1,7 +1,8 @@ -import type { IMaterial } from "../../typings/types.ts"; +import type { IMaterial } from "../../types/index.ts"; import { channelPercentage } from "../_utils.ts"; export const materials: IMaterial[] = [ { + uuid: "82596330-64ea-4f9c-bdb2-7080036ae1db", name: { en_US: "Plastic" }, label: "rough", normal: "brick_normal", @@ -20,17 +21,19 @@ export const materials: IMaterial[] = [ roughness: (idx: number) => channelPercentage(100 - idx), opacity: () => 1, shading: [ - { - texture: "plastic_soft-light", - blend: "soft-light", - }, - { - blend: "multiply", - texture: "plastic_multiply", - }, + // { + // texture: "plastic_soft-light", + // blend: "soft-light", + // }, + // { + // blend: "multiply", + // texture: "plastic_multiply", + // }, ], + octaves: 3, }, { + uuid: "baf7e0ba-57e4-440e-9557-abcca23f65d4", name: { en_US: "Metallic" }, label: "metal", normal: "metal_normal", @@ -50,17 +53,19 @@ export const materials: IMaterial[] = [ roughness: () => 0, opacity: () => 1, shading: [ - { - blend: ["color-dodge", "overlay"], - texture: "metal_overlay", - }, - { - blend: ["color-burn", "overlay"], - texture: "metal_reflectivity", - }, + // { + // blend: ["color-dodge", "overlay"], + // texture: "metal_overlay", + // }, + // { + // blend: ["color-burn", "overlay"], + // texture: "metal_reflectivity", + // }, ], + octaves: 3, }, { + uuid: "4ecaa878-a15c-4d97-b73b-f2d80a7954eb", name: { en_US: "Planks", }, @@ -84,8 +89,10 @@ export const materials: IMaterial[] = [ blend: "overlay", texture: "planks_multiply", }], + octaves: 3, }, { + uuid: "1bfedaa6-eec2-48ad-b1d0-ace6aede6f95", name: { en_US: "Stud", }, @@ -105,8 +112,10 @@ export const materials: IMaterial[] = [ emissive: () => 0, roughness: () => 25, opacity: () => 1, + octaves: 3, }, { + uuid: "1a4619d9-1897-4b0c-8fe6-eb5c99dc5074", name: { en_US: "Brick", }, @@ -131,8 +140,10 @@ export const materials: IMaterial[] = [ blend: "multiply", texture: "big_brick", }], + octaves: 3, }, { + uuid: "4a049e57-cb34-4973-8ae5-b4f0ba2f9335", name: { en_US: "Brick Lit", }, @@ -157,8 +168,10 @@ export const materials: IMaterial[] = [ blend: "multiply", texture: "big_brick", }], + octaves: 3, }, { + uuid: "8e0e0737-d02d-42f4-8298-14e2374347e8", name: { en_US: "Glowing" }, label: "emissive", normal: "block_normal", @@ -172,18 +185,20 @@ export const materials: IMaterial[] = [ explosionResistance: () => 0, lightAbsorption: (_itr: number) => 10, lightEmission: (itr: number) => - Math.max(0, Math.min(15, Math.round(itr * 0.9))), + Math.max(0, Math.min(15, Math.ceil(itr * 0.9))), metalness: () => 0, emissive: (idx: number) => - Math.min(80, Math.floor(channelPercentage(idx) * 0.75)), + Math.max(50, Math.min(80, Math.floor(channelPercentage(idx) * 0.666))), roughness: () => channelPercentage(30), opacity: () => 1, shading: [{ blend: "overlay", texture: "glowing", }], + octaves: 3, }, { + uuid: "aed2fc55-cfc4-4048-8609-8304c59759c0", name: { en_US: "Glass" }, label: "glass", normal: "block_normal", @@ -205,8 +220,10 @@ export const materials: IMaterial[] = [ blend: ["overlay", "multiply"], texture: "glass", }], + octaves: 3, }, { + uuid: "9f576c0a-6a7d-402d-84c0-2257faba4fd3", name: { en_US: "Glass Pane" }, label: "glass_pane", normal: "block_normal", @@ -229,26 +246,10 @@ export const materials: IMaterial[] = [ blend: ["overlay", "multiply"], texture: "glass", }], + octaves: 3, }, - // { - // name: { en_US: "Dot" }, - // label: "dot", - // normal: "dot_normal", - // sound: "pop", - // friction: () => 0.5, - // minimumLevel: 0, - // maximumLevel: 100, - // endStep: 100, - // step: 25, - // explosionResistance: () => 1, - // lightAbsorption: () => 15, - // lightEmission: () => 0, - // metalness: () => 0, - // emissive: () => 0, - // roughness: () => 0, - // opacity: () => 1, - // } { + uuid: "e7620d80-112c-41db-b159-31446174f76c", name: { en_US: "Dot" }, label: "dot", normal: "dot_normal", @@ -273,12 +274,14 @@ export const materials: IMaterial[] = [ blend: "multiply", }, ], + octaves: 3, }, { + uuid: "884b3443-0241-41a7-90e3-a53ba215a279", name: { en_US: "Lit Dot" }, label: "lit_dot", normal: "dot_normal", - mer: "dot_glowing_mer", + mer: "lit_dot_mer", sound: "pop", friction: (_idx: number) => 0.5, minimumLevel: 50, @@ -299,5 +302,62 @@ export const materials: IMaterial[] = [ blend: "multiply", }, ], + octaves: 3, + }, + { + uuid: "238f73d9-5261-456c-a8f4-a82683261dba", + name: { en_US: "Negative" }, + label: "negative", + normal: "negative_normal", + mer: "negative_mer", + sound: "glass", + friction: (_idx: number) => 0.4, + minimumLevel: 50, + maximumLevel: 100, + endStep: 100, + step: 25, + steps: [50], + explosionResistance: (_idx: number) => 1, + lightAbsorption: (_itr: number) => 0, + lightEmission: () => 0.4, + metalness: () => 0, + emissive: () => 0, + roughness: (_idx: number) => 55, + opacity: () => 1, + shading: [ + { + texture: "negative_overlay", + blend: "multiply", + }, + ], + octaves: 2, + }, + { + uuid: "783e37fe-3a37-48a4-9acb-90b2840608df", + name: { en_US: "Positive" }, + label: "positive", + normal: "positive_normal", + mer: "positive_mer", + sound: "glass", + friction: (_idx: number) => 0.6, + minimumLevel: 50, + maximumLevel: 100, + endStep: 100, + step: 25, + steps: [50], + explosionResistance: (_idx: number) => 1, + lightAbsorption: (_itr: number) => 0, + lightEmission: () => 0.4, + metalness: () => 0, + emissive: () => 0, + roughness: (_idx: number) => 55, + opacity: () => 1, + shading: [ + { + texture: "positive_dodge", + blend: "multiply", + }, + ], + octaves: 2, }, ]; diff --git a/src/store/db.json b/src/store/db.json new file mode 100644 index 00000000..e8c4a508 --- /dev/null +++ b/src/store/db.json @@ -0,0 +1,2914 @@ +{ + "amethyst_block": { + "dominantColor": [ + 114, + 89, + 177, + 255 + ], + "dominantColorHex": "#7259b1", + "averageColor": [ + 133, + 97, + 191, + 255 + ], + "averageColorHex": "#8561bf" + }, + "amethyst_cluster": { + "dominantColor": [ + 114, + 89, + 177, + 255 + ], + "dominantColorHex": "#7259b1", + "averageColor": [ + 163, + 126, + 207, + 255 + ], + "averageColorHex": "#a37ecf" + }, + "ancient_debris": { + "dominantColor": [ + 87, + 46, + 42, + 255 + ], + "dominantColorHex": "#572e2a", + "averageColor": [ + 95, + 63, + 55, + 255 + ], + "averageColorHex": "#5f3f37" + }, + "angler_pottery_pattern": { + "dominantColor": [ + 127, + 72, + 59, + 255 + ], + "dominantColorHex": "#7f483b", + "averageColor": [ + 126, + 72, + 57, + 255 + ], + "averageColorHex": "#7e4839" + }, + "anvil": { + "dominantColor": [ + 60, + 60, + 60, + 255 + ], + "dominantColorHex": "#3c3c3c", + "averageColor": [ + 68, + 68, + 68, + 255 + ], + "averageColorHex": "#444444" + }, + "archer_pottery_pattern": { + "dominantColor": [ + 116, + 65, + 53, + 255 + ], + "dominantColorHex": "#744135", + "averageColor": [ + 124, + 71, + 56, + 255 + ], + "averageColorHex": "#7c4738" + }, + "arms_up_pottery_pattern": { + "dominantColor": [ + 121, + 68, + 56, + 255 + ], + "dominantColorHex": "#794438", + "averageColor": [ + 125, + 72, + 57, + 255 + ], + "averageColorHex": "#7d4839" + }, + "azalea_leaves": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 90, + 114, + 44, + 255 + ], + "averageColorHex": "#5a722c" + }, + "azalea_leaves_flowers": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 99, + 111, + 60, + 255 + ], + "averageColorHex": "#636f3c" + }, + "azalea_leaves_flowers_opaque": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 91, + 104, + 56, + 255 + ], + "averageColorHex": "#5b6838" + }, + "azalea_leaves_opaque": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 82, + 105, + 42, + 255 + ], + "averageColorHex": "#52692a" + }, + "flowering_azalea": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 91, + 109, + 42, + 255 + ], + "averageColorHex": "#5b6d2a" + }, + "azalea": { + "dominantColor": [ + 82, + 101, + 43, + 255 + ], + "dominantColorHex": "#52652b", + "averageColor": [ + 93, + 117, + 45, + 255 + ], + "averageColorHex": "#5d752d" + }, + "bamboo_block": { + "dominantColor": [ + 126, + 138, + 55, + 255 + ], + "dominantColorHex": "#7e8a37", + "averageColor": [ + 127, + 144, + 57, + 255 + ], + "averageColorHex": "#7f9039" + }, + "bamboo": { + "dominantColor": [ + 95, + 142, + 3, + 255 + ], + "dominantColorHex": "#5f8e03", + "averageColor": [ + 93, + 144, + 19, + 255 + ], + "averageColorHex": "#5d9013" + }, + "bamboo_mosaic_stairs": { + "dominantColor": [ + 199, + 169, + 76, + 255 + ], + "dominantColorHex": "#c7a94c", + "averageColor": [ + 190, + 170, + 78, + 255 + ], + "averageColorHex": "#beaa4e" + }, + "bamboo_stairs": { + "dominantColor": [ + 199, + 169, + 76, + 255 + ], + "dominantColorHex": "#c7a94c", + "averageColor": [ + 193, + 173, + 80, + 255 + ], + "averageColorHex": "#c1ad50" + }, + "barrel": { + "dominantColor": [ + 135, + 95, + 58, + 255 + ], + "dominantColorHex": "#875f3a", + "averageColor": [ + 107, + 81, + 50, + 255 + ], + "averageColorHex": "#6b5132" + }, + "basalt": { + "dominantColor": [ + 78, + 75, + 78, + 255 + ], + "dominantColorHex": "#4e4b4e", + "averageColor": [ + 73, + 72, + 77, + 255 + ], + "averageColorHex": "#49484d" + }, + "beehive": { + "dominantColor": [ + 182, + 144, + 92, + 255 + ], + "dominantColorHex": "#b6905c", + "averageColor": [ + 157, + 126, + 75, + 255 + ], + "averageColorHex": "#9d7e4b" + }, + "beehive_front_honey": { + "dominantColor": [ + 182, + 144, + 92, + 255 + ], + "dominantColorHex": "#b6905c", + "averageColor": [ + 165, + 129, + 72, + 255 + ], + "averageColorHex": "#a58148" + }, + "beetroots_stage_0": { + "dominantColor": [ + 77, + 138, + 39, + 255 + ], + "dominantColorHex": "#4d8a27", + "averageColor": [ + 65, + 137, + 41, + 255 + ], + "averageColorHex": "#418929" + }, + "beetroots_stage_1": { + "dominantColor": [ + 77, + 138, + 39, + 255 + ], + "dominantColorHex": "#4d8a27", + "averageColor": [ + 66, + 138, + 41, + 255 + ], + "averageColorHex": "#428a29" + }, + "beetroots_stage_2": { + "dominantColor": [ + 77, + 138, + 39, + 255 + ], + "dominantColorHex": "#4d8a27", + "averageColor": [ + 69, + 130, + 39, + 255 + ], + "averageColorHex": "#458227" + }, + "beetroots_stage_3": { + "dominantColor": [ + 77, + 138, + 39, + 255 + ], + "dominantColorHex": "#4d8a27", + "averageColor": [ + 93, + 91, + 30, + 255 + ], + "averageColorHex": "#5d5b1e" + }, + "bee_nest": { + "dominantColor": [ + 216, + 159, + 82, + 255 + ], + "dominantColorHex": "#d89f52", + "averageColor": [ + 196, + 150, + 77, + 255 + ], + "averageColorHex": "#c4964d" + }, + "bee_nest_front_honey": { + "dominantColor": [ + 199, + 130, + 68, + 255 + ], + "dominantColorHex": "#c78244", + "averageColor": [ + 194, + 151, + 75, + 255 + ], + "averageColorHex": "#c2974b" + }, + "big_dripleaf": { + "dominantColor": [ + 104, + 122, + 47, + 255 + ], + "dominantColorHex": "#687a2f", + "averageColor": [ + 91, + 115, + 45, + 255 + ], + "averageColorHex": "#5b732d" + }, + "blackstone_wall": { + "dominantColor": [ + 57, + 54, + 66, + 255 + ], + "dominantColorHex": "#393642", + "averageColor": [ + 42, + 35, + 40, + 255 + ], + "averageColorHex": "#2a2328" + }, + "blade_pottery_pattern": { + "dominantColor": [ + 121, + 68, + 56, + 255 + ], + "dominantColorHex": "#794438", + "averageColor": [ + 129, + 74, + 58, + 255 + ], + "averageColorHex": "#814a3a" + }, + "blast_furnace": { + "dominantColor": [ + 85, + 85, + 85, + 255 + ], + "dominantColorHex": "#555555", + "averageColor": [ + 107, + 107, + 107, + 255 + ], + "averageColorHex": "#6b6b6b" + }, + "lit_blast_furnace": { + "dominantColor": [ + 85, + 85, + 85, + 255 + ], + "dominantColorHex": "#555555", + "averageColor": [ + 107, + 107, + 107, + 255 + ], + "averageColorHex": "#6b6b6b" + }, + "blue_ice": { + "dominantColor": [ + 103, + 160, + 252, + 255 + ], + "dominantColorHex": "#67a0fc", + "averageColor": [ + 116, + 167, + 253, + 255 + ], + "averageColorHex": "#74a7fd" + }, + "bone_block": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 229, + 225, + 207, + 255 + ], + "averageColorHex": "#e5e1cf" + }, + "bookshelf": { + "dominantColor": [ + 177, + 136, + 81, + 255 + ], + "dominantColorHex": "#b18851", + "averageColor": [ + 117, + 94, + 59, + 255 + ], + "averageColorHex": "#755e3b" + }, + "border": { + "dominantColor": [ + 171, + 46, + 46, + 255 + ], + "dominantColorHex": "#ab2e2e", + "averageColor": [ + 181, + 57, + 47, + 255 + ], + "averageColorHex": "#b5392f" + }, + "brewer_pottery_pattern": { + "dominantColor": [ + 127, + 72, + 59, + 255 + ], + "dominantColorHex": "#7f483b", + "averageColor": [ + 126, + 72, + 57, + 255 + ], + "averageColorHex": "#7e4839" + }, + "brewing_stand": { + "dominantColor": [ + 101, + 92, + 92, + 255 + ], + "dominantColorHex": "#655c5c", + "averageColor": [ + 116, + 105, + 105, + 255 + ], + "averageColorHex": "#746969" + }, + "brick_stairs": { + "dominantColor": [ + 150, + 83, + 67, + 255 + ], + "dominantColorHex": "#965343", + "averageColor": [ + 150, + 97, + 83, + 255 + ], + "averageColorHex": "#966153" + }, + "bubble_column_inner_a": { + "dominantColor": [ + 28, + 201, + 255, + 255 + ], + "dominantColorHex": "#1cc9ff", + "averageColor": [ + 67, + 195, + 255, + 255 + ], + "averageColorHex": "#43c3ff" + }, + "bubble_column_inner_b": { + "dominantColor": [ + 28, + 201, + 255, + 255 + ], + "dominantColorHex": "#1cc9ff", + "averageColor": [ + 67, + 195, + 255, + 255 + ], + "averageColorHex": "#43c3ff" + }, + "bubble_column_outer_a": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_b": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_c": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_d": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_e": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_f": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_g": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "bubble_column_outer_h": { + "dominantColor": [ + 117, + 222, + 255, + 255 + ], + "dominantColorHex": "#75deff", + "averageColor": [ + 137, + 248, + 42, + 255 + ], + "averageColorHex": "#89f82a" + }, + "budding_amethyst": { + "dominantColor": [ + 114, + 89, + 177, + 255 + ], + "dominantColorHex": "#7259b1", + "averageColor": [ + 132, + 96, + 186, + 255 + ], + "averageColorHex": "#8460ba" + }, + "allow": { + "dominantColor": [ + 134, + 106, + 67, + 255 + ], + "dominantColorHex": "#866a43", + "averageColor": [ + 136, + 115, + 83, + 255 + ], + "averageColorHex": "#887353" + }, + "deny": { + "dominantColor": [ + 105, + 105, + 105, + 255 + ], + "dominantColorHex": "#696969", + "averageColor": [ + 111, + 111, + 111, + 255 + ], + "averageColorHex": "#6f6f6f" + }, + "burn_pottery_pattern": { + "dominantColor": [ + 127, + 72, + 59, + 255 + ], + "dominantColorHex": "#7f483b", + "averageColor": [ + 127, + 73, + 57, + 255 + ], + "averageColorHex": "#7f4939" + }, + "cake_inner": { + "dominantColor": [ + 104, + 46, + 32, + 255 + ], + "dominantColorHex": "#682e20", + "averageColor": [ + 133, + 84, + 60, + 255 + ], + "averageColorHex": "#85543c" + }, + "yellow_candle_cake": { + "dominantColor": [ + 134, + 63, + 35, + 255 + ], + "dominantColorHex": "#863f23", + "averageColor": [ + 203, + 151, + 121, + 255 + ], + "averageColorHex": "#cb9779" + }, + "calcite": { + "dominantColor": [ + 178, + 178, + 178, + 255 + ], + "dominantColorHex": "#b2b2b2", + "averageColor": [ + 223, + 224, + 220, + 255 + ], + "averageColorHex": "#dfe0dc" + }, + "calibrated_sculk_sensor_amethyst": { + "dominantColor": [ + 168, + 138, + 241, + 255 + ], + "dominantColorHex": "#a88af1", + "averageColor": [ + 198, + 160, + 225, + 255 + ], + "averageColorHex": "#c6a0e1" + }, + "calibrated_sculk_sensor": { + "dominantColor": [ + 133, + 105, + 201, + 255 + ], + "dominantColorHex": "#8569c9", + "averageColor": [ + 59, + 69, + 101, + 255 + ], + "averageColorHex": "#3b4565" + }, + "camera": { + "dominantColor": [ + 72, + 72, + 72, + 255 + ], + "dominantColorHex": "#484848", + "averageColor": [ + 53, + 53, + 53, + 255 + ], + "averageColorHex": "#353535" + }, + "carried_waterlily": { + "dominantColor": [ + 67, + 100, + 37, + 255 + ], + "dominantColorHex": "#436425", + "averageColor": [ + 67, + 102, + 36, + 255 + ], + "averageColorHex": "#436624" + }, + "carrots_stage3": { + "dominantColor": [ + 49, + 111, + 42, + 255 + ], + "dominantColorHex": "#316f2a", + "averageColor": [ + 81, + 124, + 37, + 255 + ], + "averageColorHex": "#517c25" + }, + "carrots_stage_0": { + "dominantColor": [ + 49, + 111, + 42, + 255 + ], + "dominantColorHex": "#316f2a", + "averageColor": [ + 44, + 110, + 39, + 255 + ], + "averageColorHex": "#2c6e27" + }, + "carrots_stage_1": { + "dominantColor": [ + 41, + 93, + 35, + 255 + ], + "dominantColorHex": "#295d23", + "averageColor": [ + 52, + 120, + 40, + 255 + ], + "averageColorHex": "#347828" + }, + "carrots_stage_2": { + "dominantColor": [ + 41, + 93, + 35, + 255 + ], + "dominantColorHex": "#295d23", + "averageColor": [ + 56, + 113, + 37, + 255 + ], + "averageColorHex": "#387125" + }, + "carrots_stage_3": { + "dominantColor": [ + 49, + 111, + 42, + 255 + ], + "dominantColorHex": "#316f2a", + "averageColor": [ + 81, + 124, + 37, + 255 + ], + "averageColorHex": "#517c25" + }, + "cartography_table": { + "dominantColor": [ + 100, + 63, + 28, + 255 + ], + "dominantColorHex": "#643f1c", + "averageColor": [ + 67, + 43, + 20, + 255 + ], + "averageColorHex": "#432b14" + }, + "lava_cauldron": { + "dominantColor": [ + 194, + 194, + 194, + 255 + ], + "dominantColorHex": "#c2c2c2", + "averageColor": [ + 36, + 36, + 36, + 255 + ], + "averageColorHex": "#242424" + }, + "cauldron_water_placeholder": { + "dominantColor": [ + 194, + 194, + 194, + 255 + ], + "dominantColorHex": "#c2c2c2", + "averageColor": [ + 36, + 36, + 36, + 255 + ], + "averageColorHex": "#242424" + }, + "cherry_leaves": { + "dominantColor": [ + 234, + 146, + 196, + 255 + ], + "dominantColorHex": "#ea92c4", + "averageColor": [ + 229, + 172, + 194, + 255 + ], + "averageColorHex": "#e5acc2" + }, + "cherry_leaves_opaque": { + "dominantColor": [ + 205, + 102, + 161, + 255 + ], + "dominantColorHex": "#cd66a1", + "averageColor": [ + 226, + 162, + 188, + 255 + ], + "averageColorHex": "#e2a2bc" + }, + "cherry_wood": { + "dominantColor": [ + 79, + 50, + 64, + 255 + ], + "dominantColorHex": "#4f3240", + "averageColor": [ + 54, + 33, + 44, + 255 + ], + "averageColorHex": "#36212c" + }, + "cherry_wall_sign": { + "dominantColor": [ + 204, + 127, + 127, + 255 + ], + "dominantColorHex": "#cc7f7f", + "averageColor": [ + 226, + 178, + 172, + 255 + ], + "averageColorHex": "#e2b2ac" + }, + "chiseled_bookshelf_empty": { + "dominantColor": [ + 177, + 136, + 81, + 255 + ], + "dominantColorHex": "#b18851", + "averageColor": [ + 90, + 70, + 41, + 255 + ], + "averageColorHex": "#5a4629" + }, + "chiseled_bookshelf_occupied": { + "dominantColor": [ + 177, + 136, + 81, + 255 + ], + "dominantColorHex": "#b18851", + "averageColor": [ + 121, + 94, + 69, + 255 + ], + "averageColorHex": "#795e45" + }, + "chiseled_bookshelf": { + "dominantColor": [ + 145, + 110, + 65, + 255 + ], + "dominantColorHex": "#916e41", + "averageColor": [ + 175, + 141, + 86, + 255 + ], + "averageColorHex": "#af8d56" + }, + "chiseled_nether_bricks": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 47, + 23, + 28, + 255 + ], + "averageColorHex": "#2f171c" + }, + "chiseled_polished_blackstone": { + "dominantColor": [ + 75, + 73, + 80, + 255 + ], + "dominantColorHex": "#4b4950", + "averageColor": [ + 53, + 48, + 56, + 255 + ], + "averageColorHex": "#353038" + }, + "chorus_flower": { + "dominantColor": [ + 85, + 50, + 87, + 255 + ], + "dominantColorHex": "#553257", + "averageColor": [ + 151, + 120, + 151, + 255 + ], + "averageColorHex": "#977897" + }, + "chorus_flower_dead": { + "dominantColor": [ + 85, + 50, + 87, + 255 + ], + "dominantColorHex": "#553257", + "averageColor": [ + 96, + 61, + 94, + 255 + ], + "averageColorHex": "#603d5e" + }, + "chorus_plant": { + "dominantColor": [ + 85, + 50, + 87, + 255 + ], + "dominantColorHex": "#553257", + "averageColor": [ + 93, + 57, + 93, + 255 + ], + "averageColorHex": "#5d395d" + }, + "clay": { + "dominantColor": [ + 158, + 164, + 173, + 255 + ], + "dominantColorHex": "#9ea4ad", + "averageColor": [ + 160, + 166, + 179, + 255 + ], + "averageColorHex": "#a0a6b3" + }, + "coal_block": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 16, + 15, + 15, + 255 + ], + "averageColorHex": "#100f0f" + }, + "coal_ore": { + "dominantColor": [ + 113, + 113, + 113, + 255 + ], + "dominantColorHex": "#717171", + "averageColor": [ + 105, + 105, + 105, + 255 + ], + "averageColorHex": "#696969" + }, + "coarse_dirt": { + "dominantColor": [ + 120, + 81, + 57, + 255 + ], + "dominantColorHex": "#785139", + "averageColor": [ + 119, + 85, + 59, + 255 + ], + "averageColorHex": "#77553b" + }, + "stone_stairs": { + "dominantColor": [ + 133, + 133, + 133, + 255 + ], + "dominantColorHex": "#858585", + "averageColor": [ + 127, + 127, + 127, + 255 + ], + "averageColorHex": "#7f7f7f" + }, + "mossy_cobblestone_stairs": { + "dominantColor": [ + 81, + 90, + 55, + 255 + ], + "dominantColorHex": "#515a37", + "averageColor": [ + 110, + 118, + 94, + 255 + ], + "averageColorHex": "#6e765e" + }, + "cocoa_stage_0": { + "dominantColor": [ + 117, + 108, + 52, + 255 + ], + "dominantColorHex": "#756c34", + "averageColor": [ + 133, + 134, + 62, + 255 + ], + "averageColorHex": "#85863e" + }, + "cocoa_stage_1": { + "dominantColor": [ + 102, + 73, + 34, + 255 + ], + "dominantColorHex": "#664922", + "averageColor": [ + 146, + 110, + 56, + 255 + ], + "averageColorHex": "#926e38" + }, + "cocoa_stage_2": { + "dominantColor": [ + 201, + 138, + 82, + 255 + ], + "dominantColorHex": "#c98a52", + "averageColor": [ + 156, + 94, + 43, + 255 + ], + "averageColorHex": "#9c5e2b" + }, + "compost": { + "dominantColor": [ + 104, + 62, + 24, + 255 + ], + "dominantColorHex": "#683e18", + "averageColor": [ + 146, + 98, + 42, + 255 + ], + "averageColorHex": "#92622a" + }, + "composter": { + "dominantColor": [ + 131, + 78, + 46, + 255 + ], + "dominantColorHex": "#834e2e", + "averageColor": [ + 112, + 70, + 31, + 255 + ], + "averageColorHex": "#70461f" + }, + "compost_ready": { + "dominantColor": [ + 128, + 99, + 73, + 255 + ], + "dominantColorHex": "#806349", + "averageColor": [ + 174, + 130, + 74, + 255 + ], + "averageColorHex": "#ae824a" + }, + "concrete_black": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 8, + 10, + 15, + 255 + ], + "averageColorHex": "#080a0f" + }, + "concrete_blue": { + "dominantColor": [ + 45, + 54, + 140, + 255 + ], + "dominantColorHex": "#2d368c", + "averageColor": [ + 44, + 46, + 143, + 255 + ], + "averageColorHex": "#2c2e8f" + }, + "concrete_brown": { + "dominantColor": [ + 91, + 53, + 29, + 255 + ], + "dominantColorHex": "#5b351d", + "averageColor": [ + 96, + 59, + 31, + 255 + ], + "averageColorHex": "#603b1f" + }, + "concrete_cyan": { + "dominantColor": [ + 20, + 117, + 133, + 255 + ], + "dominantColorHex": "#147585", + "averageColor": [ + 21, + 119, + 136, + 255 + ], + "averageColorHex": "#157788" + }, + "concrete_gray": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 54, + 57, + 61, + 255 + ], + "averageColorHex": "#36393d" + }, + "concrete_green": { + "dominantColor": [ + 74, + 86, + 34, + 255 + ], + "dominantColorHex": "#4a5622", + "averageColor": [ + 73, + 91, + 36, + 255 + ], + "averageColorHex": "#495b24" + }, + "concrete_light_blue": { + "dominantColor": [ + 34, + 139, + 192, + 255 + ], + "dominantColorHex": "#228bc0", + "averageColor": [ + 35, + 137, + 198, + 255 + ], + "averageColorHex": "#2389c6" + }, + "concrete_lime": { + "dominantColor": [ + 103, + 162, + 23, + 255 + ], + "dominantColorHex": "#67a217", + "averageColor": [ + 94, + 168, + 24, + 255 + ], + "averageColorHex": "#5ea818" + }, + "concrete_magenta": { + "dominantColor": [ + 163, + 46, + 158, + 255 + ], + "dominantColorHex": "#a32e9e", + "averageColor": [ + 169, + 48, + 159, + 255 + ], + "averageColorHex": "#a9309f" + }, + "concrete_orange": { + "dominantColor": [ + 218, + 83, + 0, + 255 + ], + "dominantColorHex": "#da5300", + "averageColor": [ + 224, + 97, + 0, + 255 + ], + "averageColorHex": "#e06100" + }, + "concrete_pink": { + "dominantColor": [ + 211, + 95, + 140, + 255 + ], + "dominantColorHex": "#d35f8c", + "averageColor": [ + 213, + 101, + 142, + 255 + ], + "averageColorHex": "#d5658e" + }, + "concrete_powder_black": { + "dominantColor": [ + 58, + 58, + 62, + 255 + ], + "dominantColorHex": "#3a3a3e", + "averageColor": [ + 25, + 26, + 31, + 255 + ], + "averageColorHex": "#191a1f" + }, + "concrete_powder_blue": { + "dominantColor": [ + 66, + 75, + 160, + 255 + ], + "dominantColorHex": "#424ba0", + "averageColor": [ + 70, + 73, + 166, + 255 + ], + "averageColorHex": "#4649a6" + }, + "concrete_powder_brown": { + "dominantColor": [ + 118, + 76, + 51, + 255 + ], + "dominantColorHex": "#764c33", + "averageColor": [ + 125, + 84, + 53, + 255 + ], + "averageColorHex": "#7d5435" + }, + "concrete_powder_cyan": { + "dominantColor": [ + 36, + 143, + 149, + 255 + ], + "dominantColorHex": "#248f95", + "averageColor": [ + 36, + 147, + 157, + 255 + ], + "averageColorHex": "#24939d" + }, + "concrete_powder_gray": { + "dominantColor": [ + 77, + 81, + 84, + 255 + ], + "dominantColorHex": "#4d5154", + "averageColor": [ + 76, + 81, + 84, + 255 + ], + "averageColorHex": "#4c5154" + }, + "concrete_powder_green": { + "dominantColor": [ + 92, + 107, + 46, + 255 + ], + "dominantColorHex": "#5c6b2e", + "averageColor": [ + 97, + 119, + 44, + 255 + ], + "averageColorHex": "#61772c" + }, + "concrete_powder_light_blue": { + "dominantColor": [ + 73, + 190, + 210, + 255 + ], + "dominantColorHex": "#49bed2", + "averageColor": [ + 74, + 180, + 213, + 255 + ], + "averageColorHex": "#4ab4d5" + }, + "concrete_powder_lime": { + "dominantColor": [ + 119, + 178, + 39, + 255 + ], + "dominantColorHex": "#77b227", + "averageColor": [ + 125, + 189, + 41, + 255 + ], + "averageColorHex": "#7dbd29" + }, + "concrete_powder_magenta": { + "dominantColor": [ + 193, + 81, + 188, + 255 + ], + "dominantColorHex": "#c151bc", + "averageColor": [ + 192, + 83, + 184, + 255 + ], + "averageColorHex": "#c053b8" + }, + "concrete_powder_orange": { + "dominantColor": [ + 223, + 120, + 27, + 255 + ], + "dominantColorHex": "#df781b", + "averageColor": [ + 227, + 131, + 31, + 255 + ], + "averageColorHex": "#e3831f" + }, + "concrete_powder_pink": { + "dominantColor": [ + 227, + 144, + 176, + 255 + ], + "dominantColorHex": "#e390b0", + "averageColor": [ + 228, + 153, + 181, + 255 + ], + "averageColorHex": "#e499b5" + }, + "concrete_powder_purple": { + "dominantColor": [ + 121, + 53, + 172, + 255 + ], + "dominantColorHex": "#7935ac", + "averageColor": [ + 131, + 55, + 177, + 255 + ], + "averageColorHex": "#8337b1" + }, + "concrete_powder_red": { + "dominantColor": [ + 160, + 50, + 50, + 255 + ], + "dominantColorHex": "#a03232", + "averageColor": [ + 168, + 54, + 50, + 255 + ], + "averageColorHex": "#a83632" + }, + "concrete_powder_silver": { + "dominantColor": [ + 155, + 155, + 152, + 255 + ], + "dominantColorHex": "#9b9b98", + "averageColor": [ + 154, + 154, + 148, + 255 + ], + "averageColorHex": "#9a9a94" + }, + "concrete_powder_white": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 225, + 227, + 227, + 255 + ], + "averageColorHex": "#e1e3e3" + }, + "concrete_powder_yellow": { + "dominantColor": [ + 228, + 185, + 46, + 255 + ], + "dominantColorHex": "#e4b92e", + "averageColor": [ + 232, + 199, + 54, + 255 + ], + "averageColorHex": "#e8c736" + }, + "concrete_purple": { + "dominantColor": [ + 90, + 32, + 153, + 255 + ], + "dominantColorHex": "#5a2099", + "averageColor": [ + 100, + 31, + 156, + 255 + ], + "averageColorHex": "#641f9c" + }, + "concrete_red": { + "dominantColor": [ + 137, + 32, + 32, + 255 + ], + "dominantColorHex": "#892020", + "averageColor": [ + 142, + 32, + 32, + 255 + ], + "averageColorHex": "#8e2020" + }, + "concrete_silver": { + "dominantColor": [ + 121, + 120, + 113, + 255 + ], + "dominantColorHex": "#797871", + "averageColor": [ + 125, + 125, + 115, + 255 + ], + "averageColorHex": "#7d7d73" + }, + "concrete_white": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 207, + 213, + 214, + 255 + ], + "averageColorHex": "#cfd5d6" + }, + "concrete_yellow": { + "dominantColor": [ + 239, + 166, + 19, + 255 + ], + "dominantColorHex": "#efa613", + "averageColor": [ + 240, + 175, + 21, + 255 + ], + "averageColorHex": "#f0af15" + }, + "waxed_copper": { + "dominantColor": [ + 199, + 106, + 84, + 255 + ], + "dominantColorHex": "#c76a54", + "averageColor": [ + 192, + 107, + 79, + 255 + ], + "averageColorHex": "#c06b4f" + }, + "copper_ore": { + "dominantColor": [ + 125, + 125, + 125, + 255 + ], + "dominantColorHex": "#7d7d7d", + "averageColor": [ + 124, + 125, + 120, + 255 + ], + "averageColorHex": "#7c7d78" + }, + "coral_blue": { + "dominantColor": [ + 46, + 79, + 220, + 255 + ], + "dominantColorHex": "#2e4fdc", + "averageColor": [ + 49, + 87, + 206, + 255 + ], + "averageColorHex": "#3157ce" + }, + "coral_blue_dead": { + "dominantColor": [ + 131, + 121, + 119, + 255 + ], + "dominantColorHex": "#837977", + "averageColor": [ + 130, + 123, + 119, + 255 + ], + "averageColorHex": "#827b77" + }, + "coral_fan_blue": { + "dominantColor": [ + 46, + 79, + 220, + 255 + ], + "dominantColorHex": "#2e4fdc", + "averageColor": [ + 50, + 91, + 208, + 255 + ], + "averageColorHex": "#325bd0" + }, + "coral_fan_blue_dead": { + "dominantColor": [ + 150, + 145, + 140, + 255 + ], + "dominantColorHex": "#96918c", + "averageColor": [ + 143, + 135, + 130, + 255 + ], + "averageColorHex": "#8f8782" + }, + "coral_fan_pink": { + "dominantColor": [ + 189, + 69, + 149, + 255 + ], + "dominantColorHex": "#bd4595", + "averageColor": [ + 202, + 84, + 153, + 255 + ], + "averageColorHex": "#ca5499" + }, + "coral_fan_pink_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 233, + 221, + 215, + 255 + ], + "averageColorHex": "#e9ddd7" + }, + "coral_fan_purple": { + "dominantColor": [ + 151, + 28, + 158, + 255 + ], + "dominantColorHex": "#971c9e", + "averageColor": [ + 160, + 32, + 159, + 255 + ], + "averageColorHex": "#a0209f" + }, + "coral_fan_purple_dead": { + "dominantColor": [ + 150, + 145, + 140, + 255 + ], + "dominantColorHex": "#96918c", + "averageColor": [ + 185, + 177, + 172, + 255 + ], + "averageColorHex": "#b9b1ac" + }, + "coral_fan_red": { + "dominantColor": [ + 119, + 26, + 44, + 255 + ], + "dominantColorHex": "#771a2c", + "averageColor": [ + 158, + 34, + 45, + 255 + ], + "averageColorHex": "#9e222d" + }, + "coral_fan_red_dead": { + "dominantColor": [ + 90, + 88, + 87, + 255 + ], + "dominantColorHex": "#5a5857", + "averageColor": [ + 159, + 150, + 146, + 255 + ], + "averageColorHex": "#9f9692" + }, + "coral_fan_yellow": { + "dominantColor": [ + 206, + 171, + 60, + 255 + ], + "dominantColorHex": "#ceab3c", + "averageColor": [ + 205, + 183, + 61, + 255 + ], + "averageColorHex": "#cdb73d" + }, + "coral_fan_yellow_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 5, + 249, + 243, + 255 + ], + "averageColorHex": "#05f9f3" + }, + "coral_pink": { + "dominantColor": [ + 189, + 69, + 149, + 255 + ], + "dominantColorHex": "#bd4595", + "averageColor": [ + 207, + 91, + 159, + 255 + ], + "averageColorHex": "#cf5b9f" + }, + "coral_pink_dead": { + "dominantColor": [ + 102, + 100, + 99, + 255 + ], + "dominantColorHex": "#666463", + "averageColor": [ + 124, + 117, + 114, + 255 + ], + "averageColorHex": "#7c7572" + }, + "coral_plant_blue": { + "dominantColor": [ + 46, + 79, + 220, + 255 + ], + "dominantColorHex": "#2e4fdc", + "averageColor": [ + 47, + 83, + 197, + 255 + ], + "averageColorHex": "#2f53c5" + }, + "coral_plant_blue_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 118, + 111, + 107, + 255 + ], + "averageColorHex": "#766f6b" + }, + "coral_plant_pink": { + "dominantColor": [ + 154, + 48, + 118, + 255 + ], + "dominantColorHex": "#9a3076", + "averageColor": [ + 197, + 84, + 152, + 255 + ], + "averageColorHex": "#c55498" + }, + "coral_plant_pink_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 133, + 125, + 120, + 255 + ], + "averageColorHex": "#857d78" + }, + "coral_plant_purple": { + "dominantColor": [ + 151, + 28, + 158, + 255 + ], + "dominantColorHex": "#971c9e", + "averageColor": [ + 161, + 23, + 159, + 255 + ], + "averageColorHex": "#a1179f" + }, + "coral_plant_purple_dead": { + "dominantColor": [ + 131, + 121, + 119, + 255 + ], + "dominantColorHex": "#837977", + "averageColor": [ + 132, + 124, + 120, + 255 + ], + "averageColorHex": "#847c78" + }, + "coral_plant_red": { + "dominantColor": [ + 119, + 26, + 44, + 255 + ], + "dominantColorHex": "#771a2c", + "averageColor": [ + 166, + 37, + 46, + 255 + ], + "averageColorHex": "#a6252e" + }, + "coral_plant_red_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 136, + 128, + 124, + 255 + ], + "averageColorHex": "#88807c" + }, + "coral_plant_yellow": { + "dominantColor": [ + 206, + 171, + 60, + 255 + ], + "dominantColorHex": "#ceab3c", + "averageColor": [ + 209, + 186, + 62, + 255 + ], + "averageColorHex": "#d1ba3e" + }, + "coral_plant_yellow_dead": { + "dominantColor": [ + 150, + 145, + 140, + 255 + ], + "dominantColorHex": "#96918c", + "averageColor": [ + 142, + 135, + 129, + 255 + ], + "averageColorHex": "#8e8781" + }, + "coral_purple": { + "dominantColor": [ + 175, + 36, + 182, + 255 + ], + "dominantColorHex": "#af24b6", + "averageColor": [ + 183, + 32, + 180, + 255 + ], + "averageColorHex": "#b720b4" + }, + "coral_purple_dead": { + "dominantColor": [ + 131, + 121, + 119, + 255 + ], + "dominantColorHex": "#837977", + "averageColor": [ + 131, + 123, + 119, + 255 + ], + "averageColorHex": "#837b77" + }, + "coral_red": { + "dominantColor": [ + 160, + 33, + 58, + 255 + ], + "dominantColorHex": "#a0213a", + "averageColor": [ + 162, + 35, + 48, + 255 + ], + "averageColorHex": "#a22330" + }, + "coral_red_dead": { + "dominantColor": [ + 131, + 121, + 119, + 255 + ], + "dominantColorHex": "#837977", + "averageColor": [ + 131, + 123, + 119, + 255 + ], + "averageColorHex": "#837b77" + }, + "coral_yellow": { + "dominantColor": [ + 210, + 188, + 56, + 255 + ], + "dominantColorHex": "#d2bc38", + "averageColor": [ + 216, + 199, + 66, + 255 + ], + "averageColorHex": "#d8c742" + }, + "coral_yellow_dead": { + "dominantColor": [ + 111, + 102, + 98, + 255 + ], + "dominantColorHex": "#6f6662", + "averageColor": [ + 133, + 126, + 122, + 255 + ], + "averageColorHex": "#857e7a" + }, + "cracked_nether_bricks": { + "dominantColor": [ + 0, + 0, + 0, + 255 + ], + "dominantColorHex": "#000000", + "averageColor": [ + 40, + 20, + 23, + 255 + ], + "averageColorHex": "#281417" + }, + "cracked_polished_blackstone_bricks": { + "dominantColor": [ + 57, + 54, + 66, + 255 + ], + "dominantColorHex": "#393642", + "averageColor": [ + 44, + 37, + 43, + 255 + ], + "averageColorHex": "#2c252b" + }, + "crafting_table": { + "dominantColor": [ + 182, + 144, + 92, + 255 + ], + "dominantColorHex": "#b6905c", + "averageColor": [ + 128, + 102, + 63, + 255 + ], + "averageColorHex": "#80663f" + }, + "crimson_fungus": { + "dominantColor": [ + 190, + 52, + 65, + 255 + ], + "dominantColorHex": "#be3441", + "averageColor": [ + 141, + 44, + 29, + 255 + ], + "averageColorHex": "#8d2c1d" + }, + "crimson_nylium": { + "dominantColor": [ + 111, + 50, + 50, + 255 + ], + "dominantColorHex": "#6f3232", + "averageColor": [ + 107, + 26, + 26, + 255 + ], + "averageColorHex": "#6b1a1a" + }, + "crimson_roots": { + "dominantColor": [ + 139, + 5, + 56, + 255 + ], + "dominantColorHex": "#8b0538", + "averageColor": [ + 133, + 8, + 43, + 255 + ], + "averageColorHex": "#85082b" + }, + "crying_obsidian": { + "dominantColor": [ + 81, + 6, + 163, + 255 + ], + "dominantColorHex": "#5106a3", + "averageColor": [ + 32, + 10, + 60, + 255 + ], + "averageColorHex": "#200a3c" + }, + "waxed_double_cut_copper_slab": { + "dominantColor": [ + 225, + 117, + 106, + 255 + ], + "dominantColorHex": "#e1756a", + "averageColor": [ + 191, + 106, + 80, + 255 + ], + "averageColorHex": "#bf6a50" + }, + "danger_pottery_pattern": { + "dominantColor": [ + 121, + 68, + 56, + 255 + ], + "dominantColorHex": "#794438", + "averageColor": [ + 127, + 73, + 57, + 255 + ], + "averageColorHex": "#7f4939" + }, + "deadbush": { + "dominantColor": [ + 101, + 76, + 43, + 255 + ], + "dominantColorHex": "#654c2b", + "averageColor": [ + 107, + 78, + 40, + 255 + ], + "averageColorHex": "#6b4e28" + }, + "chiseled_deepslate": { + "dominantColor": [ + 75, + 76, + 78, + 255 + ], + "dominantColorHex": "#4b4c4e", + "averageColor": [ + 54, + 54, + 54, + 255 + ], + "averageColorHex": "#363636" + }, + "cobbled_deepslate_wall": { + "dominantColor": [ + 70, + 70, + 75, + 255 + ], + "dominantColorHex": "#46464b", + "averageColor": [ + 77, + 77, + 80, + 255 + ], + "averageColorHex": "#4d4d50" + }, + "cracked_deepslate_bricks": { + "dominantColor": [ + 85, + 85, + 85, + 255 + ], + "dominantColorHex": "#555555", + "averageColor": [ + 64, + 64, + 65, + 255 + ], + "averageColorHex": "#404041" + }, + "cracked_deepslate_tiles": { + "dominantColor": [ + 64, + 64, + 64, + 255 + ], + "dominantColorHex": "#404040", + "averageColor": [ + 52, + 52, + 52, + 255 + ], + "averageColorHex": "#343434" + }, + "infested_deepslate": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 80, + 80, + 82, + 255 + ], + "averageColorHex": "#505052" + }, + "deepslate_bricks": { + "dominantColor": [ + 85, + 85, + 85, + 255 + ], + "dominantColorHex": "#555555", + "averageColor": [ + 70, + 70, + 71, + 255 + ], + "averageColorHex": "#464647" + }, + "deepslate_coal_ore": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 74, + 74, + 76, + 255 + ], + "averageColorHex": "#4a4a4c" + }, + "deepslate_copper_ore": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 92, + 93, + 89, + 255 + ], + "averageColorHex": "#5c5d59" + }, + "deepslate_diamond_ore": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 83, + 106, + 106, + 255 + ], + "averageColorHex": "#536a6a" + }, + "deepslate_emerald_ore": { + "dominantColor": [ + 97, + 97, + 97, + 255 + ], + "dominantColorHex": "#616161", + "averageColor": [ + 78, + 104, + 87, + 255 + ], + "averageColorHex": "#4e6857" + }, + "deepslate_gold_ore": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 115, + 102, + 78, + 255 + ], + "averageColorHex": "#73664e" + }, + "deepslate_iron_ore": { + "dominantColor": [ + 58, + 58, + 62, + 255 + ], + "dominantColorHex": "#3a3a3e", + "averageColor": [ + 106, + 99, + 94, + 255 + ], + "averageColorHex": "#6a635e" + }, + "deepslate_lapis_ore": { + "dominantColor": [ + 97, + 97, + 97, + 255 + ], + "dominantColorHex": "#616161", + "averageColor": [ + 79, + 90, + 115, + 255 + ], + "averageColorHex": "#4f5a73" + }, + "lit_deepslate_redstone_ore": { + "dominantColor": [ + 80, + 80, + 80, + 255 + ], + "dominantColorHex": "#505050", + "averageColor": [ + 104, + 73, + 74, + 255 + ], + "averageColorHex": "#68494a" + }, + "deepslate_tiles": { + "dominantColor": [ + 64, + 64, + 64, + 255 + ], + "dominantColorHex": "#404040", + "averageColor": [ + 54, + 54, + 55, + 255 + ], + "averageColorHex": "#363637" + }, + "polished_deepslate_wall": { + "dominantColor": [ + 85, + 85, + 85, + 255 + ], + "dominantColorHex": "#555555", + "averageColor": [ + 72, + 72, + 73, + 255 + ], + "averageColorHex": "#484849" + } +} \ No newline at end of file diff --git a/src/versions.json b/src/versions.json index 5aa3a5ad..cfaa85d2 100644 --- a/src/versions.json +++ b/src/versions.json @@ -1,4 +1,4 @@ { - "RP": "3.0.0", - "BP": "3.0.0" + "RP": "2.4.0", + "BP": "2.4.0" } diff --git a/test/jasonjgardner.mcstructure b/test/jasonjgardner.mcstructure new file mode 100644 index 0000000000000000000000000000000000000000..a53667002f418369bd9f488c0e846aa1d7d94b88 GIT binary patch literal 33984 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 bdAr Promise; + octaves?: number; + uuid: string; } export interface IBlock { diff --git a/server/types.d.ts b/types/server.ts similarity index 100% rename from server/types.d.ts rename to types/server.ts