From 4b9b19b02d57d02f3abd7612dccd8ce2780cacfc Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Mon, 12 Jun 2023 19:49:45 -0400 Subject: [PATCH 01/11] Fixed typo in cc.image.nft docs --- .../data/computercraft/lua/rom/modules/main/cc/image/nft.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua index 2df2ed962..154332f22 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua @@ -2,7 +2,7 @@ -- -- SPDX-License-Identifier: MPL-2.0 ---- Read and draw nbt ("Nitrogen Fingers Text") images. +--- Read and draw nft ("Nitrogen Fingers Text") images. -- -- nft ("Nitrogen Fingers Text") is a file format for drawing basic images. -- Unlike the images that @{paintutils.parseImage} uses, nft supports coloured From 5722e51735864bcaca27b0e036c97e6cb241f298 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 13 Jun 2023 06:04:35 -0400 Subject: [PATCH 02/11] Fix missing curly brace in instrument list --- .../shared/peripheral/speaker/SpeakerPeripheral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index 9064ea644..c68e4ad71 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -190,7 +190,7 @@ public abstract class SpeakerPeripheral implements IPeripheral { * The speaker supports [all of Minecraft's noteblock instruments](https://minecraft.fandom.com/wiki/Note_Block#Instruments). * These are: *

- * {@code "harp"}, {@code "basedrum"}, {@code "snare"}, {@code "hat"}, {@code "bass"}, @code "flute"}, + * {@code "harp"}, {@code "basedrum"}, {@code "snare"}, {@code "hat"}, {@code "bass"}, {@code "flute"}, * {@code "bell"}, {@code "guitar"}, {@code "chime"}, {@code "xylophone"}, {@code "iron_xylophone"}, * {@code "cow_bell"}, {@code "didgeridoo"}, {@code "bit"}, {@code "banjo"} and {@code "pling"}. * From 77ac04cb7ac3456c98f70998864c8b84343e7c5a Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 15 Jun 2023 18:32:30 +0100 Subject: [PATCH 03/11] Fix changelog notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also exclude data generator cache from the Forge jar. Didn't have any better place to put this 😳. --- .../main/resources/data/computercraft/lua/rom/help/changelog.md | 2 +- .../main/resources/data/computercraft/lua/rom/help/whatsnew.md | 2 +- projects/forge/build.gradle.kts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md index 19561664c..098267351 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -7,7 +7,7 @@ * Java methods now coerce values to strings consistently with Lua. * Add custom timeout support to the HTTP API. * Support custom proxies for HTTP requests (Lemmmy). -* The `speaker` program now errors when playing HTTP files. +* The `speaker` program now errors when playing HTML files. * `edit` now shows an error message when editing read-only files. * Update Ukranian translation (SirEdvin). diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index 3407fccd6..1512a2ae6 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -7,7 +7,7 @@ New features in CC: Tweaked 1.105.0 * Java methods now coerce values to strings consistently with Lua. * Add custom timeout support to the HTTP API. * Support custom proxies for HTTP requests (Lemmmy). -* The `speaker` program now errors when playing HTTP files. +* The `speaker` program now errors when playing HTML files. * `edit` now shows an error message when editing read-only files. * Update Ukranian translation (SirEdvin). diff --git a/projects/forge/build.gradle.kts b/projects/forge/build.gradle.kts index 589e44f0d..9291f2e4a 100644 --- a/projects/forge/build.gradle.kts +++ b/projects/forge/build.gradle.kts @@ -206,6 +206,7 @@ tasks.processResources { filesMatching("META-INF/mods.toml") { expand(mapOf("forgeVersion" to libs.versions.forge.get(), "file" to mapOf("jarVersion" to modVersion))) } + exclude(".cache") } tasks.jar { From c7f3d4f45d55401d3dbfcebb61f87ea7c810ece4 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 15 Jun 2023 18:54:34 +0100 Subject: [PATCH 04/11] Port fs.find to CraftOS Also add support for "?" style wildcards. Closes #1455. --- .../dan200/computercraft/core/apis/FSAPI.java | 22 ----- .../core/filesystem/FileSystem.java | 54 ++---------- .../data/computercraft/lua/rom/apis/fs.lua | 87 +++++++++++++++++++ .../resources/test-rom/spec/apis/fs_spec.lua | 47 ++++++++++ 4 files changed, 140 insertions(+), 70 deletions(-) diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/projects/core/src/main/java/dan200/computercraft/core/apis/FSAPI.java index fd75c2f92..c300bd0df 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -443,28 +443,6 @@ public class FSAPI implements ILuaAPI { } } - /** - * Searches for files matching a string with wildcards. - *

- * This string is formatted like a normal path string, but can include any - * number of wildcards ({@code *}) to look for files matching anything. - * For example, rom/*/command* will look for any path starting with - * {@code command} inside any subdirectory of {@code /rom}. - * - * @param path The wildcard-qualified path to search for. - * @return A list of paths that match the search string. - * @throws LuaException If the path doesn't exist. - * @cc.since 1.6 - */ - @LuaFunction - public final String[] find(String path) throws LuaException { - try (var ignored = environment.time(Metrics.FS_OPS)) { - return getFileSystem().find(path); - } catch (FileSystemException e) { - throw new LuaException(e.getMessage()); - } - } - /** * Returns the capacity of the drive the path is located on. * diff --git a/projects/core/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java b/projects/core/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java index d43918aaa..139e69e2f 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java +++ b/projects/core/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java @@ -166,47 +166,6 @@ public class FileSystem { return array; } - private void findIn(String dir, List matches, Pattern wildPattern) throws FileSystemException { - var list = list(dir); - for (var entry : list) { - var entryPath = dir.isEmpty() ? entry : dir + "/" + entry; - if (wildPattern.matcher(entryPath).matches()) { - matches.add(entryPath); - } - if (isDir(entryPath)) { - findIn(entryPath, matches, wildPattern); - } - } - } - - public synchronized String[] find(String wildPath) throws FileSystemException { - // Match all the files on the system - wildPath = sanitizePath(wildPath, true); - - // If we don't have a wildcard at all just check the file exists - var starIndex = wildPath.indexOf('*'); - if (starIndex == -1) { - return exists(wildPath) ? new String[]{ wildPath } : new String[0]; - } - - // Find the all non-wildcarded directories. For instance foo/bar/baz* -> foo/bar - var prevDir = wildPath.substring(0, starIndex).lastIndexOf('/'); - var startDir = prevDir == -1 ? "" : wildPath.substring(0, prevDir); - - // If this isn't a directory then just abort - if (!isDir(startDir)) return new String[0]; - - // Scan as normal, starting from this directory - var wildPattern = Pattern.compile("^\\Q" + wildPath.replaceAll("\\*", "\\\\E[^\\\\/]*\\\\Q") + "\\E$"); - List matches = new ArrayList<>(); - findIn(startDir, matches, wildPattern); - - // Return matches - var array = new String[matches.size()]; - matches.toArray(array); - return array; - } - public synchronized boolean exists(String path) throws FileSystemException { path = sanitizePath(path); var mount = getMount(path); @@ -400,21 +359,20 @@ public class FileSystem { private static final Pattern threeDotsPattern = Pattern.compile("^\\.{3,}$"); + // IMPORTANT: Both arrays are sorted by ASCII value. + private static final char[] specialChars = new char[]{ '"', '*', ':', '<', '>', '?', '|' }; + private static final char[] specialCharsAllowWildcards = new char[]{ '"', ':', '<', '>', '|' }; + public static String sanitizePath(String path, boolean allowWildcards) { // Allow windowsy slashes path = path.replace('\\', '/'); // Clean the path or illegal characters. - final var specialChars = new char[]{ - '"', ':', '<', '>', '?', '|', // Sorted by ascii value (important) - }; - var cleanName = new StringBuilder(); + var allowedChars = allowWildcards ? specialCharsAllowWildcards : specialChars; for (var i = 0; i < path.length(); i++) { var c = path.charAt(i); - if (c >= 32 && Arrays.binarySearch(specialChars, c) < 0 && (allowWildcards || c != '*')) { - cleanName.append(c); - } + if (c >= 32 && Arrays.binarySearch(allowedChars, c) < 0) cleanName.append(c); } path = cleanName.toString(); diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/fs.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/fs.lua index 1a0fb4b53..0643fd120 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/fs.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/fs.lua @@ -133,6 +133,93 @@ function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs) return {} end +local function find_aux(path, parts, i, out) + local part = parts[i] + if not part then + -- If we're at the end of the pattern, ensure our path exists and append it. + if fs.exists(path) then out[#out + 1] = path end + elseif part.exact then + -- If we're an exact match, just recurse into this directory. + return find_aux(fs.combine(path, part.contents), parts, i + 1, out) + else + -- Otherwise we're a pattern. Check we're a directory, then recurse into each + -- matching file. + if not fs.isDir(path) then return end + + local files = fs.list(path) + for j = 1, #files do + local file = files[j] + if file:find(part.contents) then find_aux(fs.combine(path, file), parts, i + 1, out) end + end + end +end + +local find_escape = { + -- Escape standard Lua pattern characters + ["^"] = "%^", ["$"] = "%$", ["("] = "%(", [")"] = "%)", ["%"] = "%%", + ["."] = "%.", ["["] = "%[", ["]"] = "%]", ["+"] = "%+", ["-"] = "%-", + -- Aside from our wildcards. + ["*"] = ".*", + ["?"] = ".", +} + +--[[- Searches for files matching a string with wildcards. + +This string looks like a normal path string, but can include wildcards, which +can match multiple paths: + + - "?" matches any single character in a file name. + - "*" matches any number of characters. + +For example, `rom/*/command*` will look for any path starting with `command` +inside any subdirectory of `/rom`. + +Note that these wildcards match a single segment of the path. For instance +`rom/*.lua` will include `rom/startup.lua` but _not_ include `rom/programs/list.lua`. + +@tparam string path The wildcard-qualified path to search for. +@treturn { string... } A list of paths that match the search string. +@throws If the supplied path was invalid. +@since 1.6 +@changed 1.106.0 Added support for the `?` wildcard. + +@usage List all Markdown files in the help folder + + fs.find("rom/help/*.md") +]] +function fs.find(pattern) + expect(1, pattern, "string") + + pattern = fs.combine(pattern) -- Normalise the path, removing ".."s. + + -- If the pattern is trying to search outside the computer root, just abort. + -- This will fail later on anyway. + if pattern == ".." or pattern:sub(1, 3) == "../" then + error("/" .. pattern .. ": Invalid Path", 2) + end + + -- If we've no wildcards, just check the file exists. + if not pattern:find("[*?]") then + if fs.exists(pattern) then return { pattern } else return {} end + end + + local parts = {} + for part in pattern:gmatch("[^/]+") do + if part:find("[*?]") then + parts[#parts + 1] = { + exact = false, + contents = "^" .. part:gsub(".", find_escape) .. "$", + } + else + parts[#parts + 1] = { exact = true, contents = part } + end + end + + local out = {} + find_aux("", parts, 1, out) + return out +end + --- Returns true if a path is mounted to the parent filesystem. -- -- The root filesystem "/" is considered a mount, along with disk folders and diff --git a/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua index ca540985f..156dafdd5 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua @@ -87,6 +87,53 @@ describe("The fs library", function() end) end) + describe("fs.find", function() + it("fails on invalid paths", function() + expect.error(fs.find, ".."):eq("/..: Invalid Path") + expect.error(fs.find, "../foo/bar"):eq("/../foo/bar: Invalid Path") + end) + + it("returns nothing on non-existent files", function() + expect(fs.find("no/such/file")):same {} + expect(fs.find("no/such/*")):same {} + expect(fs.find("no/*/file")):same {} + end) + + it("returns a single file", function() + expect(fs.find("rom")):same { "rom" } + expect(fs.find("rom/motd.txt")):same { "rom/motd.txt" } + end) + + it("supports the '*' wildcard", function() + expect(fs.find("rom/*")):same { + "rom/apis", + "rom/autorun", + "rom/help", + "rom/modules", + "rom/motd.txt", + "rom/programs", + "rom/startup.lua", + } + expect(fs.find("rom/*/command")):same { + "rom/apis/command", + "rom/modules/command", + "rom/programs/command", + } + + expect(fs.find("rom/*/lua*")):same { + "rom/help/lua.txt", + "rom/programs/lua.lua", + } + end) + + it("supports the '?' wildcard", function() + expect(fs.find("rom/programs/mo??.lua")):same { + "rom/programs/motd.lua", + "rom/programs/move.lua", + } + end) + end) + describe("fs.combine", function() it("removes . and ..", function() expect(fs.combine("./a/b")):eq("a/b") From df591cd7c64ce4c7192e3aa7c6ec3bf16dc54921 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 15 Jun 2023 18:57:25 +0100 Subject: [PATCH 05/11] Allow extending UpgradeDataProvider Closes #1477 --- .../computercraft/api/upgrades/UpgradeDataProvider.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java index 8797aee88..7178e8a3a 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java @@ -24,6 +24,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -104,7 +105,7 @@ public abstract class UpgradeDataProvider> addUpgrade); @Override - public final CompletableFuture run(CachedOutput cache) { + public CompletableFuture run(CachedOutput cache) { var base = output.getOutputFolder().resolve("data"); Set seen = new HashSet<>(); @@ -127,7 +128,7 @@ public abstract class UpgradeDataProvider Date: Fri, 16 Jun 2023 21:03:01 +0000 Subject: [PATCH 06/11] Translations for Russian (ru_ru) Co-authored-by: Andrew71 --- .../assets/computercraft/lang/ru_ru.json | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json b/projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json index 3fd443ace..8495d2376 100644 --- a/projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json +++ b/projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json @@ -115,5 +115,67 @@ "upgrade.minecraft.diamond_pickaxe.adjective": "Π”ΠΎΠ±Ρ‹Π²Π°ΡŽΡ‰Π°Ρ", "upgrade.minecraft.diamond_shovel.adjective": "ΠšΠΎΠΏΠ°ΡŽΡ‰Π°Ρ", "upgrade.minecraft.diamond_sword.adjective": "БоСвая", - "gui.computercraft.pocket_computer_overlay": "ΠšΠ°Ρ€ΠΌΠ°Π½Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚. Π§Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΡŒ, Π½Π°ΠΆΠΌΠΈ ESC." + "gui.computercraft.pocket_computer_overlay": "ΠšΠ°Ρ€ΠΌΠ°Π½Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚. Π§Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΡŒ, Π½Π°ΠΆΠΌΠΈ ESC.", + "gui.computercraft.config.command_require_creative.tooltip": "Π’Ρ€Π΅Π±ΠΎΠ²Π°Ρ‚ΡŒ творчСский Ρ€Π΅ΠΆΠΈΠΌ ΠΈ ΠΏΡ€Π°Π²Π° ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π° для взаимодСйствия с\nΠΊΠΎΠΌΠ°Π½Π΄Π½Ρ‹ΠΌΠΈ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°ΠΌΠΈ. Π­Ρ‚ΠΎ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ для ΠšΠΎΠΌΠ°Π½Π΄Π½Ρ‹Ρ… Π±Π»ΠΎΠΊΠΎΠ² ванильной ΠΈΠ³Ρ€Ρ‹.", + "gui.computercraft.config.default_computer_settings.tooltip": "Π Π°Π·Π΄Π΅Π»Π΅Π½Π½Ρ‹ΠΉ запятыми список систСмных настроСк ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Π½Π° Π½ΠΎΠ²Ρ‹Ρ… ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°Ρ….\nНапримСр: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ всё Π°Π²Ρ‚ΠΎΠ΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅.", + "gui.computercraft.config.execution.computer_threads.tooltip": "УстанавливаСт количСство ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Ρ‹. Π‘ΠΎΠ»ΡŒΡˆΠ΅Π΅ число\nΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ большС ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ² смоТСт Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ, Π½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ привСсти ΠΊ Π»Π°Π³Ρƒ.\nΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ΄Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Π±ΠΎΠ»Π΅Π΅ Ρ‡Π΅ΠΌ ΠΎΠ΄Π½ΠΈΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠΌ. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ с ΠΎΡΡ‚ΠΎΡ€ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒΡŽ.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "gui.computercraft.config.execution.max_main_computer_time.tooltip": "Π˜Π΄Π΅Π°Π»ΡŒΠ½Ρ‹ΠΉ максимум Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΎΡ‚Π²Π΅Π΄Π΅Π½ΠΎ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Ρƒ Π½Π° Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π·Π°Π΄Π°Ρ‡, Π² миллисСкундах.\nΠœΡ‹ Π²ΠΏΠΎΠ»Π½Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹ΠΉΠ΄Π΅ΠΌ Π·Π° этот Π»ΠΈΠΌΠΈΡ‚, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·Π°Ρ‚ΡŒ сколько\nΠ²Ρ€Π΅ΠΌΠ΅Π½ΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Ρ‚Ρ€Π°Ρ‡Π΅Π½ΠΎ Π½Π° Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π·Π°Π΄Π°Ρ‡, это лишь Π²Π΅Ρ€Ρ…Π½ΠΈΠΉ Π»ΠΈΠΌΠΈΡ‚ срСднСго значСния Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "gui.computercraft.config.execution.max_main_global_time.tooltip": "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΠΎΡ‚Ρ€Π°Ρ‡Π΅Π½ΠΎ Π½Π° Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π·Π°Π΄Π°Ρ‡ Π·Π° ΠΎΠ΄ΠΈΠ½ Ρ‚ΠΈΠΊ, Π² \nмиллисСкундах. \nΠœΡ‹ Π²ΠΏΠΎΠ»Π½Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹ΠΉΠ΄Π΅ΠΌ Π·Π° этот Π»ΠΈΠΌΠΈΡ‚, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·Π°Ρ‚ΡŒ сколько\nΠ²Ρ€Π΅ΠΌΠ΅Π½ΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Ρ‚Ρ€Π°Ρ‡Π΅Π½ΠΎ Π½Π° Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π·Π°Π΄Π°Ρ‡, это лишь Π²Π΅Ρ€Ρ…Π½ΠΈΠΉ Π»ΠΈΠΌΠΈΡ‚ срСднСго значСния Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "gui.computercraft.config.http.bandwidth.global_download": "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ Π»ΠΈΠΌΠΈΡ‚ Π½Π° скачиваниС", + "gui.computercraft.config.http.bandwidth.global_download.tooltip": "ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π±Π°ΠΉΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ Π·Π° сСкунду. ВсС ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Ρ‹ дСлят эту ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ½ΡƒΡŽ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ. (Π±Π°ΠΉΡ‚Ρ‹ Π² сСкунду)\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "gui.computercraft.config.http.bandwidth.global_upload.tooltip": "ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π±Π°ΠΉΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ Π·Π° сСкунду. ВсС ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Ρ‹ дСлят эту ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ½ΡƒΡŽ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ. (Π±Π°ΠΉΡ‚Ρ‹ Π² сСкунду)\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "tracking_field.computercraft.http_requests.name": "HTTP запросы", + "tracking_field.computercraft.turtle_ops.name": "ΠžΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ", + "gui.computercraft.config.http.enabled.tooltip": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ API \"http\" Π½Π° ΠšΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°Ρ…. Π­Ρ‚ΠΎ Ρ‚Π°ΠΊΠΆΠ΅ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ \"pastebin\" ΠΈ \"wget\", \nΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½ΡƒΠΆΠ½Ρ‹ ΠΌΠ½ΠΎΠ³ΠΈΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ. РСкомСндуСтся ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ это Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹ΠΌ ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ \nΠΊΠΎΠ½Ρ„ΠΈΠ³ \"rules\" для Π±ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ½ΠΊΠΎΠΉ настройки.", + "gui.computercraft.config.http.max_websockets.tooltip": "ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… Π²Π΅Π±-сокСтов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€. УстановитС Π½Π° 0 для Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹Ρ… Π²Π΅Π±-сокСтов.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 1", + "gui.computercraft.config.term_sizes": "Π Π°Π·ΠΌΠ΅Ρ€ Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°", + "gui.computercraft.config.term_sizes.computer.height": "Высота Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°", + "gui.computercraft.config.term_sizes.monitor.height": "Максимальная высота ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€Π°", + "gui.computercraft.config.term_sizes.monitor.width.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 32", + "gui.computercraft.config.turtle.advanced_fuel_limit": "Π›ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π° ΠŸΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹Ρ… Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ", + "gui.computercraft.config.turtle.need_fuel.tooltip": "УстанавливаСт, Π½ΡƒΠΆΠ΄Π°ΡŽΡ‚ΡΡ Π»ΠΈ Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠΊΠΈ Π² Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π΅ для пСрСдвиТСния.", + "gui.computercraft.terminal": "ΠšΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π½Ρ‹ΠΉ Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»", + "tracking_field.computercraft.computer_tasks.name": "Π—Π°Π΄Π°Ρ‡ΠΈ", + "tracking_field.computercraft.server_tasks.name": "Π‘Π΅Ρ€Π²Π΅Ρ€Π½Ρ‹Π΅ Π·Π°Π΄Π°Ρ‡ΠΈ", + "gui.computercraft.upload.no_response": "ΠŸΠ΅Ρ€Π΅Π½ΠΎΡ Ρ„Π°ΠΉΠ»ΠΎΠ²", + "tracking_field.computercraft.avg": "%s (срСднСС)", + "gui.computercraft.config.command_require_creative": "Для использования ΠΊΠΎΠΌΠ°Π½Π΄Π½Ρ‹Ρ… ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ² Π½ΡƒΠΆΠ΅Π½ творчСский Ρ€Π΅ΠΆΠΈΠΌ", + "gui.computercraft.config.computer_space_limit": "Π›ΠΈΠΌΠΈΡ‚ мСста Π½Π° ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°Ρ… (Π² Π±Π°ΠΉΡ‚Π°Ρ…)", + "gui.computercraft.config.computer_space_limit.tooltip": "Π›ΠΈΠΌΠΈΡ‚ мСста Π½Π° дисках ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ² ΠΈ Ρ‡Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ, Π² Π±Π°ΠΉΡ‚Π°Ρ….", + "gui.computercraft.config.default_computer_settings": "Настройки ΠšΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π° ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ", + "gui.computercraft.config.disable_lua51_features": "ΠžΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Lua 5.1", + "gui.computercraft.config.disable_lua51_features.tooltip": "ΠŸΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ· Lua 5.1, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΡƒΠ±Ρ€Π°Π½Ρ‹ Π² Π±ΡƒΠ΄ΡƒΡ‰ΠΈΡ…\nобновлСниях. ПолСзно для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ ΡΠΎΠ²ΠΌΠ΅ΡΡ‚ΠΈΠΌΠΎΡΡ‚ΡŒ Π²ΠΏΠ΅Ρ€Π΅Π΄ Π²Π°ΡˆΠΈΡ… ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ.", + "gui.computercraft.config.execution": "Π’Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅", + "gui.computercraft.config.execution.computer_threads": "ΠŸΠΎΡ‚ΠΎΠΊΠΈ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°", + "gui.computercraft.config.execution.max_main_global_time": "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ Π»ΠΈΠΌΠΈΡ‚ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ Π½Π° Ρ‚ΠΈΠΊ сСрвСра", + "gui.computercraft.config.execution.tooltip": "ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ выполнСния Π·Π°Π΄Π°Ρ‡ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ². Π­Ρ‚Π° настройка прСднСзначаСтся для \nΡ‚ΠΎΠ½ΠΊΠΎΠΉ настройки сСрвСров, ΠΈ Π² основном Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π°.", + "gui.computercraft.config.floppy_space_limit": "Π›ΠΈΠΌΠΈΡ‚ мСста Π½Π° дискСтах (Π±Π°ΠΉΡ‚Ρ‹)", + "gui.computercraft.config.floppy_space_limit.tooltip": "Π›ΠΈΠΌΠΈΡ‚ мСста для хранСния ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ Π½Π° дискСтах, Π² Π±Π°ΠΉΡ‚Π°Ρ….", + "gui.computercraft.config.http": "HTTP", + "gui.computercraft.config.http.bandwidth": "ΠŸΡ€ΠΎΠΏΡƒΡΠΊΠ½Π°Ρ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ", + "gui.computercraft.config.http.bandwidth.global_upload": "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ Π»ΠΈΠΌΠΈΡ‚ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ", + "gui.computercraft.config.http.bandwidth.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΠ²Π°Π΅Ρ‚ ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ½ΡƒΡŽ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡƒΡŽ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°ΠΌΠΈ.", + "gui.computercraft.config.http.enabled": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ HTTP API", + "gui.computercraft.config.http.max_requests": "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… запросов", + "gui.computercraft.config.http.max_requests.tooltip": "ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ http-запросов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ запросы \nΠ±ΡƒΠ΄ΡƒΡ‚ поставлСны Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ ΠΊΠΎΠ³Π΄Π° ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ запросы Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Ρ‹. УстановитС Π½Π° 0 для \nΠ½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹Ρ… запросов.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 0", + "gui.computercraft.config.http.max_websockets": "ΠœΠ°ΠΊΡΠΈΠΌΡƒΠΌ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… Π²Π΅Π±-сокСтов", + "gui.computercraft.config.term_sizes.computer": "ΠšΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€", + "gui.computercraft.config.term_sizes.computer.height.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 255", + "gui.computercraft.config.term_sizes.computer.tooltip": "Π Π°Π·ΠΌΠ΅Ρ€ Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π° Π½Π° ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°Ρ….", + "gui.computercraft.config.term_sizes.computer.width": "Π¨ΠΈΡ€ΠΈΠ½Π° Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°", + "gui.computercraft.config.term_sizes.computer.width.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 255", + "gui.computercraft.config.term_sizes.monitor": "ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€", + "gui.computercraft.config.term_sizes.monitor.height.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 32", + "gui.computercraft.config.term_sizes.monitor.tooltip": "ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΎΠ² (Π² Π±Π»ΠΎΠΊΠ°Ρ…).", + "gui.computercraft.config.term_sizes.monitor.width": "Максимальная ΡˆΠΈΡ€ΠΈΠ½Π° ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΎΠ²", + "gui.computercraft.config.term_sizes.pocket_computer.height": "Высота Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°", + "gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 255", + "gui.computercraft.config.term_sizes.pocket_computer.width": "Π¨ΠΈΡ€ΠΈΠ½Π° Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°", + "gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: 1 ~ 255", + "gui.computercraft.config.turtle": "Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠΊΠΈ", + "gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "Π›ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π° для ΠŸΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹Ρ… Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 0", + "gui.computercraft.config.turtle.need_fuel": "Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊΡƒ Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π°", + "gui.computercraft.config.turtle.normal_fuel_limit": "Π›ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π° Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ", + "gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "Π›ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΏΠ»ΠΈΠ²Π° для Π§Π΅Ρ€Π΅ΠΏΠ°ΡˆΠ΅ΠΊ.\nΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅: > 0", + "gui.computercraft.config.turtle.tooltip": "Π Π°Π·Π½Ρ‹Π΅ настройки, связанныС с Ρ‡Π΅Ρ€Π΅ΠΏΠ°ΡˆΠΊΠ°ΠΌΠΈ." } From 8ccd5a560c565c98406ba59e5b14a6a1b6b09a21 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 17 Jun 2023 10:37:19 +0100 Subject: [PATCH 07/11] Add support for codecs to our data generator system This already exists in both upstream loaders, we just need to abstract over it. --- .../computercraft/data/DataProviders.java | 8 +++++- .../shared/container/BasicContainer.java | 19 -------------- .../data/FabricDataGenerators.java | 25 +++++++++++++++++++ .../dan200/computercraft/data/Generators.java | 17 +++++++++++++ 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java index 5f4223f8e..a4abd7c8f 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java +++ b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java @@ -4,16 +4,20 @@ package dan200.computercraft.data; +import com.mojang.serialization.Codec; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.data.loot.LootTableProvider.SubProviderEntry; import net.minecraft.data.models.BlockModelGenerators; import net.minecraft.data.models.ItemModelGenerators; import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -39,9 +43,11 @@ public final class DataProviders { generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades)); } - interface GeneratorSink { + public interface GeneratorSink { T add(DataProvider.Factory factory); + void addFromCodec(String name, PackType type, String directory, Codec codec, Consumer> output); + void lootTable(List tables); TagsProvider blockTags(Consumer> tags); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/container/BasicContainer.java b/projects/common/src/main/java/dan200/computercraft/shared/container/BasicContainer.java index 5b63e827a..6680725da 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/container/BasicContainer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/container/BasicContainer.java @@ -7,7 +7,6 @@ package dan200.computercraft.shared.container; import net.minecraft.core.NonNullList; import net.minecraft.world.Container; import net.minecraft.world.ContainerHelper; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; /** @@ -16,24 +15,6 @@ import net.minecraft.world.item.ItemStack; public interface BasicContainer extends Container { NonNullList getContents(); - @Override - default int getMaxStackSize() { - return 64; - } - - @Override - default void startOpen(Player player) { - } - - @Override - default void stopOpen(Player player) { - } - - @Override - default boolean canPlaceItem(int slot, ItemStack stack) { - return true; - } - @Override default int getContainerSize() { return getContents().size(); diff --git a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java index 40e5135a8..8c9340bca 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java +++ b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java @@ -4,21 +4,25 @@ package dan200.computercraft.data; +import com.mojang.serialization.Codec; import dan200.computercraft.shared.platform.RegistryWrappers; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; import net.minecraft.core.HolderLookup; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; +import net.minecraft.data.PackOutput; import net.minecraft.data.loot.LootTableProvider; import net.minecraft.data.models.BlockModelGenerators; import net.minecraft.data.models.ItemModelGenerators; import net.minecraft.data.tags.TagsProvider; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; @@ -43,6 +47,27 @@ public class FabricDataGenerators implements DataGeneratorEntrypoint { return generator.addProvider(factory); } + @Override + public void addFromCodec(String name, PackType type, String directory, Codec codec, Consumer> output) { + generator.addProvider((FabricDataOutput out) -> { + var ourType = switch (type) { + case SERVER_DATA -> PackOutput.Target.DATA_PACK; + case CLIENT_RESOURCES -> PackOutput.Target.RESOURCE_PACK; + }; + return new FabricCodecDataProvider(out, ourType, directory, codec) { + @Override + public String getName() { + return name; + } + + @Override + protected void configure(BiConsumer provider) { + output.accept(provider); + } + }; + }); + } + @Override public void lootTable(List tables) { for (var table : tables) { diff --git a/projects/forge/src/main/java/dan200/computercraft/data/Generators.java b/projects/forge/src/main/java/dan200/computercraft/data/Generators.java index 840ae1acd..2ddaf757d 100644 --- a/projects/forge/src/main/java/dan200/computercraft/data/Generators.java +++ b/projects/forge/src/main/java/dan200/computercraft/data/Generators.java @@ -4,6 +4,8 @@ package dan200.computercraft.data; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.platform.RegistryWrappers; import net.minecraft.core.HolderLookup; @@ -14,18 +16,24 @@ import net.minecraft.data.models.BlockModelGenerators; import net.minecraft.data.models.ItemModelGenerators; import net.minecraft.data.tags.ItemTagsProvider; import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraftforge.common.data.BlockTagsProvider; import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.common.data.JsonCodecProvider; import net.minecraftforge.data.event.GatherDataEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import java.util.function.Consumer; @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) @@ -46,6 +54,15 @@ public class Generators { return generator.addProvider(factory); } + @Override + public void addFromCodec(String name, PackType type, String directory, Codec codec, Consumer> output) { + generator.addProvider(out -> { + Map map = new HashMap<>(); + output.accept(map::put); + return new JsonCodecProvider<>(out, existingFiles, ComputerCraftAPI.MOD_ID, JsonOps.INSTANCE, type, directory, codec, map); + }); + } + @Override public void lootTable(List tables) { add(out -> new LootTableProvider(out, Set.of(), tables)); From 7b4ba11fb4b21e614612ab638a0a43d2401f6e7c Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 17 Jun 2023 10:46:34 +0100 Subject: [PATCH 08/11] Allow changing turtle upgrades from the GUI This adds two slots to the right of the turtle interface which contain the left and right upgrades of a turtle. - Add turtle_upgrade_{left,right} indicators, which used as the background texture for the two upgrade slots. In order to use Slot.getNoItemIcon, we need to bake these into the block texture atlas. This is done with the new atlas JSON and a data generator - it's mostly pretty simple, but we do now need a client-side data generator, which is a little ugly to do. - Add a new UpgradeContainer/UpgradeSlot, which exposes a turtle's upgrades in an inventory-like way. - Update the turtle menu and screen to handle these new slots. --- .../client/gui/TurtleScreen.java | 12 +- .../data/client/ClientDataProviders.java | 34 ++++++ .../computercraft/data/DataProviders.java | 9 ++ .../shared/turtle/inventory/TurtleMenu.java | 13 ++- .../turtle/inventory/UpgradeContainer.java | 108 ++++++++++++++++++ .../shared/turtle/inventory/UpgradeSlot.java | 50 ++++++++ .../textures/gui/turtle_advanced.png | Bin 1004 -> 1455 bytes .../textures/gui/turtle_normal.png | Bin 983 -> 1415 bytes .../textures/gui/turtle_upgrade_left.png | Bin 0 -> 129 bytes .../gui/turtle_upgrade_left.png.license | 3 + .../textures/gui/turtle_upgrade_right.png | Bin 0 -> 129 bytes .../gui/turtle_upgrade_right.png.license | 3 + projects/fabric/build.gradle.kts | 2 +- .../assets/minecraft/atlases/blocks.json | 6 + .../assets/minecraft/atlases/blocks.json | 6 + 15 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_left.png.license create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license create mode 100644 projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json create mode 100644 projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json diff --git a/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java b/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java index f99bc32ac..f31ff8ab1 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java +++ b/projects/common/src/client/java/dan200/computercraft/client/gui/TurtleScreen.java @@ -26,9 +26,11 @@ public class TurtleScreen extends AbstractComputerScreen { private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png"); private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png"); - private static final int TEX_WIDTH = 254; + private static final int TEX_WIDTH = 278; private static final int TEX_HEIGHT = 217; + private static final int FULL_TEX_SIZE = 512; + public TurtleScreen(TurtleMenu container, Inventory player, Component title) { super(container, player, title, BORDER); @@ -45,16 +47,16 @@ public class TurtleScreen extends AbstractComputerScreen { protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) { var advanced = family == ComputerFamily.ADVANCED; RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL); - blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT); + blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE); + // Render selected slot var slot = getMenu().getSelectedSlot(); if (slot >= 0) { - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); var slotX = slot % 4; var slotY = slot / 4; blit(transform, - leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, - 0, 217, 24, 24 + leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0, + 0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE ); } diff --git a/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java b/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java new file mode 100644 index 000000000..61d4dd6cf --- /dev/null +++ b/projects/common/src/client/java/dan200/computercraft/data/client/ClientDataProviders.java @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.data.client; + +import dan200.computercraft.data.DataProviders; +import dan200.computercraft.shared.turtle.inventory.UpgradeSlot; +import net.minecraft.client.renderer.texture.atlas.SpriteSources; +import net.minecraft.client.renderer.texture.atlas.sources.SingleFile; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; + +import java.util.List; +import java.util.Optional; + +/** + * A version of {@link DataProviders} which relies on client-side classes. + *

+ * This is called from {@link DataProviders#add(DataProviders.GeneratorSink)}. + */ +public final class ClientDataProviders { + private ClientDataProviders() { + } + + public static void add(DataProviders.GeneratorSink generator) { + generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> { + out.accept(new ResourceLocation("blocks"), List.of( + new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()), + new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()) + )); + }); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java index a4abd7c8f..c5f8aa661 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java +++ b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java @@ -41,6 +41,15 @@ public final class DataProviders { generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels); generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades)); + + // Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider + // and invoke that. + try { + Class.forName("dan200.computercraft.data.client.ClientDataProviders") + .getMethod("add", GeneratorSink.class).invoke(null, generator); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } public interface GeneratorSink { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java index 77116234b..61d2e67d8 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java @@ -4,6 +4,7 @@ package dan200.computercraft.shared.turtle.inventory; +import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; @@ -29,12 +30,13 @@ public final class TurtleMenu extends AbstractComputerMenu { public static final int PLAYER_START_Y = 134; public static final int TURTLE_START_X = SIDEBAR_WIDTH + 175; public static final int PLAYER_START_X = SIDEBAR_WIDTH + BORDER; + public static final int UPGRADE_START_X = SIDEBAR_WIDTH + 254; private final ContainerData data; private TurtleMenu( int id, Predicate canUse, ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData menuData, - Inventory playerInventory, Container inventory, ContainerData data + Inventory playerInventory, Container inventory, Container turtleUpgrades, ContainerData data ) { super(ModRegistry.Menus.TURTLE.get(), id, canUse, family, computer, menuData); this.data = data; @@ -58,19 +60,24 @@ public final class TurtleMenu extends AbstractComputerMenu { for (var x = 0; x < 9; x++) { addSlot(new Slot(playerInventory, x, PLAYER_START_X + x * 18, PLAYER_START_Y + 3 * 18 + 5)); } + + // Turtle upgrades + addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.LEFT, 0, UPGRADE_START_X, PLAYER_START_Y + 1)); + addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.RIGHT, 1, UPGRADE_START_X, PLAYER_START_Y + 1 + 18)); } public static TurtleMenu ofBrain(int id, Inventory player, TurtleBrain turtle) { return new TurtleMenu( // Laziness in turtle.getOwner() is important here! id, p -> turtle.getOwner().stillValid(p), turtle.getFamily(), turtle.getOwner().createServerComputer(), null, - player, turtle.getInventory(), (SingleContainerData) turtle::getSelectedSlot + player, turtle.getInventory(), new UpgradeContainer(turtle), (SingleContainerData) turtle::getSelectedSlot ); } public static TurtleMenu ofMenuData(int id, Inventory player, ComputerContainerData data) { return new TurtleMenu( - id, x -> true, data.family(), null, data, player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainerData(1) + id, x -> true, data.family(), null, data, + player, new SimpleContainer(TurtleBlockEntity.INVENTORY_SIZE), new SimpleContainer(2), new SimpleContainerData(1) ); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java new file mode 100644 index 000000000..22d918626 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.turtle.inventory; + +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.impl.TurtleUpgrades; +import net.minecraft.core.NonNullList; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +import java.util.Arrays; +import java.util.List; + +/** + * A fake {@link Container} which exposes the {@linkplain ITurtleAccess#getUpgrade(TurtleSide) upgrades} a turtle has. + * + * @see TurtleMenu + * @see UpgradeSlot + */ +class UpgradeContainer implements Container { + private static final int SIZE = 2; + + private final ITurtleAccess turtle; + + private final List lastUpgrade = Arrays.asList(null, null); + private final NonNullList lastStack = NonNullList.withSize(2, ItemStack.EMPTY); + + UpgradeContainer(ITurtleAccess turtle) { + this.turtle = turtle; + } + + private TurtleSide getSide(int slot) { + return switch (slot) { + case 0 -> TurtleSide.LEFT; + case 1 -> TurtleSide.RIGHT; + default -> throw new IllegalArgumentException("Invalid slot " + slot); + }; + } + + @Override + public ItemStack getItem(int slot) { + var upgrade = turtle.getUpgrade(getSide(slot)); + + // We don't want to return getCraftingItem directly here, as consumers may mutate the stack (they shouldn't!, + // but if they do it's a pain to track down). To avoid recreating the stack each tick, we maintain a simple + // cache. + if (upgrade == lastUpgrade.get(slot)) return lastStack.get(slot); + + var stack = upgrade == null ? ItemStack.EMPTY : upgrade.getCraftingItem().copy(); + lastUpgrade.set(slot, upgrade); + lastStack.set(slot, stack); + return stack; + } + + @Override + public void setItem(int slot, ItemStack itemStack) { + turtle.setUpgrade(getSide(slot), TurtleUpgrades.instance().get(itemStack)); + } + + @Override + public int getContainerSize() { + return SIZE; + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public boolean isEmpty() { + for (var i = 0; i < SIZE; i++) { + if (!getItem(i).isEmpty()) return false; + } + return true; + } + + @Override + public ItemStack removeItem(int slot, int count) { + return count <= 0 ? ItemStack.EMPTY : removeItemNoUpdate(slot); + } + + @Override + public ItemStack removeItemNoUpdate(int slot) { + var current = getItem(slot); + setItem(slot, ItemStack.EMPTY); + return current; + } + + @Override + public void setChanged() { + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void clearContent() { + for (var i = 0; i < SIZE; i++) setItem(i, ItemStack.EMPTY); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java new file mode 100644 index 000000000..7947785e1 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.turtle.inventory; + +import com.mojang.datafixers.util.Pair; +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.impl.TurtleUpgrades; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; + +/** + * A slot in the turtle UI which holds the turtle's current upgrade. + * + * @see TurtleMenu + */ +public class UpgradeSlot extends Slot { + public static final ResourceLocation LEFT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_left"); + public static final ResourceLocation RIGHT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_right"); + + private final TurtleSide side; + + public UpgradeSlot(Container container, TurtleSide side, int slot, int xPos, int yPos) { + super(container, slot, xPos, yPos); + this.side = side; + } + + @Override + public boolean mayPlace(ItemStack stack) { + return TurtleUpgrades.instance().get(stack) != null; + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Nullable + @Override + public Pair getNoItemIcon() { + return Pair.of(InventoryMenu.BLOCK_ATLAS, side == TurtleSide.LEFT ? LEFT_UPGRADE : RIGHT_UPGRADE); + } +} diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_advanced.png index 74a149a4744b52abf881f2ab0b81ec57e80bf063..8f7d66500bc458800fa8879114a0b3e886e51b14 100644 GIT binary patch delta 1134 zcmaFEzMfmPGr-TCmrII^fq{Y7)59f*fq@CgWMbf822zKYshj{(ws!DJ+`&{nzo+@4XpA1O?MA&;QNHe<4>N@NDnD7}f>`kG*Ui9gAa@@7@3SQaryx z+e*LRXHP7R=Tx|QTuIH&xsgGDfeDBl7&sUh6&P3=Kzu52>XD3lAjcGuX7|^LA&!|E zMkARFb?CduslUH;FT3Bnx|nUk`?wAA`BL@$^{aweKm0Zg*mM5-r~8to^7X$h0~uX^ zDxN#ja$V-Afj^^I<2_adhD-Z-ZK7%uzCUS{y?Oh|{hj+S|H-NEORtgMvHR13htt{1 zQnsAj|M@{%y0yeiTmdE5xcV={?)KIFHd~bcKfift=Pl9tIXUhZe9v#&FMP6$RsF7D zNrw9bQ&7la34sqYZVFC`c_-_5_6~J%TG_hWf$`40OFy3SEikBC#=ruNv@K_3%UE=8?^o`)_P4(1g<3hw(px`2 zZ?|^Hz9E>x+rV(+i%7xhonKG>Y)gBx``zQG@3I@Vz2Q10f)VHcm*oCte8b@-ci>)z zvAY2oXe39t0#IcimL0L@kM#s?c>d#)gK?M5ZL>YpLw!cgH_wt zE#|AcIzAsgx@OgB3yqrp(eoHqt#CKoS@z~-XW_}umIos1-`=?VLUM))iSr#`;^*Wa|d|e%T?iibzKf~twHt7S!Ak(vIkD7b#cGpN| z_}mul@P*fB7DLs?Ga4y;2~u;nHeX)Zc(Ry1Vom(pd729T49n-v&+b03DLLU1%Z2?B zyKgn$az9wfq*0yyUw)VE_n!q)p&{$7Q>RRyoAvQ`Sisk5SEv17c2?M74xb;J;B@_C z-F`>bSie4}%P7PuYG5<>$eMe7CkwZ}xHh?N%G08U=824xCowugco-U3d6}R5r&Gr{|6k&U?Nhu&DCvc5< z$b^ek2f4c}iav(9p8Ega(JaL0%_rB(Zl@~mEz2~LH=DbB^XA!?@7{D<9O&wsJmZx` z)~W}8?eDGiWBiu9?DEPshTlM|UK}}f*2-Z`aKk@IhF(U8$pWmd?BaY2CJVCK z)bE(Vpg7;!=JW4!7o-11e0);~)bpqE_n(Yi&(}Wv;@zK`t^bFd)Aa?Pm9 zE1ezSnvunzww0TqgYScX^`*oA14PRtSYR4~w1Z2-$yj9*RffsY9-IyZ4VgRrE+l0E zB_!_G>!^Fb4BI_-gXB8Z7}ody9Tc+ss_H9#Z2kUm!xZ!PryVt`pO#9hut%l-sy_Gg z-h;g#lT7cQTiskAUN>zz6XTa6?X$~Umre_x?6Lfib$M3szxIgY1Gi!yWnA9A^=822 z2e;=2ecQI}^zNcb&2joS0u}PtJ)84B@7dRb$D0pk=PC<1#Cc{WuDhFOb~#D=?9wYy jdCNUa9R(p^V#ZI-m65-k7a5nF1%;QVtDnm{r-UW|$K)Lc diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_normal.png index 83b6a882ed5ea9844cc4d44fea53cc471baccffa..f676c56b20d2173381b54b6ecef670edc0c0b78c 100644 GIT binary patch literal 1415 zcmd5(Sx}p06#f2?whT(yUO+p5u?Ar*ty^k~w4nqjTcAx)9Eb@45-5aF5}-nXU!BTQ z5eA&L$Yz1Cgb)gW!LmmdanP9-!LTF{@)2+uPUI zH!(3WJ3A|pNap9~7Zw&47Z;b7mZVatOeR}iUS3&Qk;~<)tE&oyVr^|rsZ^>|s*Q~e zjYhM#w|8)GaCmsA*Xs=igVAU-nM`K0nHM%wYgr89UMG?OUOCSI08I%d_>pfdYGtw; z(m-c;m3>-UR*mOGp8XwlB?2{UL(R-_2|vlXF*P%p8Jg8QP?2LF<`nu6jhj^Ozh4+1 zEvPy_BXCwYUci13aNN+lUl)?3Q6M3aqMkq$6h^_S$~VaRVs#T~~L^ z@>;)Ux|C;r3r?VxbZT;4kC*dd`l=SP2Pk{k)8g^K8bIy<4TFC@kQ<;EUCs`ObX!Ln z|ACBaaHU>1Ju5GN8?H#(EhuUK@tMS*4=$ZCtVazOn5y@GK|1q8$#_r6O(gi>PTy0N z=P33`A3)=ucYextHOVvdZ?@^&-G)e;X}2Itq6os{}}Y5p1qn%&=C z1~&8bI{+bxBUo!UFP*8?lf*={(Wg#Le&pm=iR}H;RYgidWX9tNbT|j|I%c}qT?3&< z$2{Fs?u0)LDmQ>Qp0J zK(tCI_XQ}qv`$pr)UEE%M z>5zf;v2O6hcSFvlzGZicgPri2!1&xHBn%3>P-Usp{N%RmM-}+ z>vmy3?#*OWv^LOZI8zmsUI+MdNp|T^b1&BflK51$mzpxitxq%joYCt26$2Xz{G~#3 ztX)?@*X4H|D6+XBE5BmYylHOop`dYy)gMq0ONg?q-2wj-8?7-~ERN@z`zJ(GBR*51F7sa5g|b8c7RWaE06|)-@kuP z)HzoF=g*(NfB*jb_wWDz|B8=hX8>iGOM?7@85sZV%+P9KU|?F|>EaktaqI2fY=2=# z5w-`fx;R24qeKE%bFi=1&{A60v_0$f|NpLGU7Z_ha)eJ+-dpBrEN?b<`R2`&C%?;C zeRb8QWu|_nXJ%=1F=_dtOUZDpddm4q6=L_7e=1E zqE+u?%xthWn8Bf}VdhG%-8@W4$`q6jeEFwrqReplwg;y}QA6ZPuG>DmKn23D{~J22 z{`Ffk^H?=!_{rb)LMLWDzQ@mI|0|xo^zXam+K}t*UvF1U@z`?u%leKAi>my+?~d+$ z8}~&i?pTphLtV)!r^pz+r6s4mcGut0dirzoB#sB=CRMxTXEMAB@4Z*oe|sjwEcY^$XR1hd#(# z{PLpHGQkZOGiR+?62%c~e|yV!lgmLr9@HhD`adU|@5Y|WZ7-ict64TRVUPWa*N6B1 z{pI6aJ7;m7{moE?vZ~vH{AEeXN^<>iv!iD+B(0rtbZOr9yL#rCG4O%BaI=U97aPN_ Xe;ho)$+ACynixD?{an^LB{Ts56R09N literal 0 HcmV?d00001 diff --git a/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license new file mode 100644 index 000000000..05aed57f6 --- /dev/null +++ b/projects/common/src/main/resources/assets/computercraft/textures/gui/turtle_upgrade_right.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers + +SPDX-License-Identifier: MPL-2.0 diff --git a/projects/fabric/build.gradle.kts b/projects/fabric/build.gradle.kts index f2aef9cb8..13b8e7065 100644 --- a/projects/fabric/build.gradle.kts +++ b/projects/fabric/build.gradle.kts @@ -119,7 +119,7 @@ loom { register("data") { configName = "Datagen" - server() + client() runDir("run/dataGen") property("cct.pretty-json") diff --git a/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json b/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json new file mode 100644 index 000000000..d9d236296 --- /dev/null +++ b/projects/fabric/src/generated/resources/assets/minecraft/atlases/blocks.json @@ -0,0 +1,6 @@ +{ + "sources": [ + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"} + ] +} diff --git a/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json b/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json new file mode 100644 index 000000000..d9d236296 --- /dev/null +++ b/projects/forge/src/generated/resources/assets/minecraft/atlases/blocks.json @@ -0,0 +1,6 @@ +{ + "sources": [ + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, + {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"} + ] +} From ccfed0059bcf33c8078ffb569918c39a3f25c5da Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 17 Jun 2023 18:01:42 +0100 Subject: [PATCH 09/11] Render the computer cursor as emissive - Split the front face of the computer model into two layers - one for the main texture, and one for the cursor. This is actually a simplification of what we had before, which is nice. - Make the cursor layer render as an emissive quad, meaning it glows in the dark. This is very easy on Forge (just some model JSON) and very hard on Fabric (requires a custom model loader). --- .../cc-tweaked.java-convention.gradle.kts | 1 + .../data/BlockModelProvider.java | 25 ++- .../models/block/computer_on.json | 39 ++++ .../models/block/computer_on.json.license | 3 + .../block/computer_advanced_front_blink.png | Bin 476 -> 0 bytes .../block/computer_advanced_front_on.png | Bin 417 -> 0 bytes .../textures/block/computer_blink.png | Bin 0 -> 110 bytes ...k.png.mcmeta => computer_blink.png.mcmeta} | 0 .../block/computer_command_front_blink.png | Bin 534 -> 0 bytes .../computer_command_front_blink.png.mcmeta | 6 - .../block/computer_command_front_on.png | Bin 426 -> 0 bytes .../block/computer_normal_front_blink.png | Bin 214 -> 0 bytes .../computer_normal_front_blink.png.mcmeta | 6 - .../block/computer_normal_front_on.png | Bin 199 -> 0 bytes .../textures/block/computer_on.png | Bin 0 -> 89 bytes projects/fabric/build.gradle.kts | 1 - .../client/ComputerCraftClient.java | 6 +- .../client/model/EmissiveComputerModel.java | 172 ++++++++++++++++++ .../block/computer_advanced_blinking.json | 5 +- .../models/block/computer_advanced_on.json | 5 +- .../block/computer_command_blinking.json | 5 +- .../models/block/computer_command_on.json | 5 +- .../block/computer_normal_blinking.json | 5 +- .../models/block/computer_normal_on.json | 5 +- projects/forge/build.gradle.kts | 1 - .../block/computer_advanced_blinking.json | 5 +- .../models/block/computer_advanced_on.json | 5 +- .../block/computer_command_blinking.json | 5 +- .../models/block/computer_command_on.json | 5 +- .../block/computer_normal_blinking.json | 5 +- .../models/block/computer_normal_on.json | 5 +- 31 files changed, 276 insertions(+), 44 deletions(-) create mode 100644 projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json create mode 100644 projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json.license delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_blink.png delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_on.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_blink.png rename projects/common/src/main/resources/assets/computercraft/textures/block/{computer_advanced_front_blink.png.mcmeta => computer_blink.png.mcmeta} (100%) delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png.mcmeta delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_on.png delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png.mcmeta delete mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_on.png create mode 100644 projects/common/src/main/resources/assets/computercraft/textures/block/computer_on.png create mode 100644 projects/fabric/src/client/java/dan200/computercraft/client/model/EmissiveComputerModel.java diff --git a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts index 9279f11aa..d8dfb6b4e 100644 --- a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts @@ -104,6 +104,7 @@ tasks.withType(JavaCompile::class.java).configureEach { tasks.processResources { exclude("**/*.license") + exclude(".cache") } tasks.withType(AbstractArchiveTask::class.java).configureEach { diff --git a/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java b/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java index 7b40cd2dd..d05d8c308 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/BlockModelProvider.java @@ -37,6 +37,14 @@ import static net.minecraft.data.models.model.ModelLocationUtils.getModelLocatio import static net.minecraft.data.models.model.TextureMapping.getBlockTexture; class BlockModelProvider { + private static final TextureSlot CURSOR = TextureSlot.create("cursor"); + + private static final ModelTemplate COMPUTER_ON = new ModelTemplate( + Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")), + Optional.empty(), + TextureSlot.FRONT, TextureSlot.SIDE, TextureSlot.TOP, CURSOR + ); + private static final ModelTemplate MONITOR_BASE = new ModelTemplate( Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/monitor_base")), Optional.empty(), @@ -142,11 +150,18 @@ class BlockModelProvider { private static void registerComputer(BlockModelGenerators generators, ComputerBlock block) { generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block) .with(createHorizontalFacingDispatch()) - .with(createModelDispatch(ComputerBlock.STATE, state -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix( - block, "_" + state.getSerializedName(), - TextureMapping.orientableCube(block).put(TextureSlot.FRONT, getBlockTexture(block, "_front" + state.getTexture())), - generators.modelOutput - ))) + .with(createModelDispatch(ComputerBlock.STATE, state -> switch (state) { + case OFF -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix( + block, "_" + state.getSerializedName(), + TextureMapping.orientableCube(block), + generators.modelOutput + ); + case ON, BLINKING -> COMPUTER_ON.createWithSuffix( + block, "_" + state.getSerializedName(), + TextureMapping.orientableCube(block).put(CURSOR, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer" + state.getTexture())), + generators.modelOutput + ); + })) ); generators.delegateItemModel(block, getModelLocation(block, "_blinking")); } diff --git a/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json new file mode 100644 index 000000000..485bc526f --- /dev/null +++ b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json @@ -0,0 +1,39 @@ +{ + "parent": "minecraft:block/block", + "render_type": "cutout", + "textures": { + "particle": "#front" + }, + "display": { + "firstperson_righthand": { + "rotation": [ 0, 135, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + } + }, + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "down": { "texture": "#top", "cullface": "down" }, + "up": { "texture": "#top", "cullface": "up" }, + "north": { "texture": "#front", "cullface": "north" }, + "south": { "texture": "#side", "cullface": "south" }, + "west": { "texture": "#side", "cullface": "west" }, + "east": { "texture": "#side", "cullface": "east" } + } + }, + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "north": { + "texture": "#cursor", + "cullface": "north", + "forge_data": {"block_light": 15, "sky_light": 15} + } + } + } + ] +} diff --git a/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json.license b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json.license new file mode 100644 index 000000000..05aed57f6 --- /dev/null +++ b/projects/common/src/main/resources/assets/computercraft/models/block/computer_on.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers + +SPDX-License-Identifier: MPL-2.0 diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_blink.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_blink.png deleted file mode 100644 index 8c29b0e49a7db4b9ff6077738b1e996fa5ec2702..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmV<20VDp2P)4E;KzV{kb z+luJyju_5?f}-d;eK@zyal75lL<9g;MMN+&R26`>ZK|AxoP#fS*QhE!#)$I?fWzVNU;^(W zec$6(tJQp1mgU1q6jDm~rfE>zte`B*d4Gz`LI5Bx77NVWtl(EQr{Jb(`8>kNxiAx| zs>%Zk?(F+W5}b4R?RHD~=;~~OtH)1>F=hou_y-9}UDuhw`SS_19q`^~_YC3N*LRb> zKWsLfC}svIPI^odV>bYbiI7qv<_`uGneKOI@Hl6nIZ;jl6KotomCrY?a$38AloD>g-&@l(bDaO^mk1$*-?ET@;QPKOgfOrC zNNmpk@pxptUb9>-DT)FSp>11Y%#ZKV2PgXA-ZV~z`@i+UeYo_&r4RmVAAASVP;j(; SaG00?0000ADWxIWP8~Sdw24?}-RfDFw?FfaCFaGa%;`!!VFG zo6SWHnx?rK5);%~NqyhrDp!N1X>J0jtN=*s^%_Z6gIi-$MH0!~N$ClTqmj04NdPId z{QV@%jI`VBxW9k481S@xPAO$JAm1%FaM5+$1)#Ki2B(pf(!9#Td3^l%cfw|f31Yqr zlgJF{JP_ju1aha=N|6M1x_;%nC9_dVfm}#VjK5(DUcUMQz|ZgRSglr+oHM2~rhp5> z5OM=E46q1PgDmIa+Z!a|r-524b~qf=_x<&p{}*>B=loAErP=ZitgFdS6eT8w00000 LNkvXXu0mjf_rJJG diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_blink.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_blink.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb8a6c95bd0a32728b056ab28277f5238132b20 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3HFyJAa%3Ql_3Rjv*f2$q5p159|d3-Y9qR zI9}sP3QmzY8YgL*V8Y^7#1WwSJurpCb(VY4mEK>q0xb*-yI;#c^c8#wWHB&!y85}S Ib4q9e0JG#DcmMzZ literal 0 HcmV?d00001 diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_blink.png.mcmeta b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_blink.png.mcmeta similarity index 100% rename from projects/common/src/main/resources/assets/computercraft/textures/block/computer_advanced_front_blink.png.mcmeta rename to projects/common/src/main/resources/assets/computercraft/textures/block/computer_blink.png.mcmeta diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png deleted file mode 100644 index be746fcb3f77c2b9589a09e09d817638e24582a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534 zcmV+x0_pvUP)yX#SeO>=g#aJ-&aWW^lDtQe8j3P_@vqrvHBT)6LNsnCHTfB8RVDqA^BFDcpbZoNf5<;wj6V z5W$JV2*AswD-=YbY4Hc$L=EkI!{!@J1Q`lP&z0^N=nKDCz}lsBe)?+ z6ANh&mE%o8b7qVXVxkcVF|kxfhXHqI|KJOc4!@xS)dE^P&^@r51GNTvnVEGYS;5j0 z>IBwMiFg_~*uRa{!pV9fN6_nIn2q~fM69lJ-wVv z1?!#T^+YqLn}apnj6nl2D&~f!O!a^{6pxrgO6jTx=k?%1!D$cVUp=^ps~%kS;J^0Z YC!&prAf~vKP5=M^07*qoM6N<$f|4@zLI3~& diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png.mcmeta b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png.mcmeta deleted file mode 100644 index e962dcee6..000000000 --- a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_blink.png.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "animation": { - "frametime": 8, - "frames": [ 0, 1 ] - } -} diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_on.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_command_front_on.png deleted file mode 100644 index 1d5d38f7542e156d9d1ecc56ed531a8396569d8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 426 zcmV;b0agBqP)-G#$Otw& z-F>U-Uc~yL|NfVA==J#{VV+x@WWGKuV^$9gSsl`vfm8EPite6;MOXoPdj1TWg=w{F zwU(T`+BlBY(R=S6DzvUvl3cxiry^ozPhP&k|N8p&)h;Jygp>ime!o8sBoAO=Yt7vO zOw)ASSnD8S?;Wk)E=<#O46G{vE~QY)w+qKF4JCPi-6IQQL(5tZi}74LBy9nOV;nM{ z?eh08AGd%X-#+!$*lK@x?_>+OfAE;sWma0=(7Y2*gc>)ABtK|qokge2`Ww3LXrxcvG2>bEkglUh&@I?Yrobz4^* zlAEU;>H=@d5~*BXJn(8ahb0U2Y>>~t{;;Tc{d-t)E)fn869}-@lPVF&D@y|K4;NRv UNiGHKf&c&j07*qoM6N<$f+mo=^Z)<= diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png deleted file mode 100644 index ba62d067ea0a4d103678cb1113070259a52ab469..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<50VEjg9xVrP=6bp~hG?9>I>DFgu!4ZwbFO&{ zoj6>lXo`7?-8BE=)U@M+730jKbM!-R2VJfeuYGXd#*SU4;96WynAf>;s~&beNXly2 zRMW$;SX}mEgICi34udA|MaG_oEU#46Tk10g-&daNaNPMa=NCi6!?UUna<93q=H8#nHgYA4x+n7_a8`gE N?CI*~vd$@?2>`;CRA>MI diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png.mcmeta b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png.mcmeta deleted file mode 100644 index e962dcee6..000000000 --- a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_blink.png.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "animation": { - "frametime": 8, - "frames": [ 0, 1 ] - } -} diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_on.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_normal_front_on.png deleted file mode 100644 index d87bcac8f2d5ea0c0d6ed637486f7454f2ac314e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UA`aE46LnI`94?1!+JMgexsBfLK zu*pMGCP>FQcE|e*+?+<;G433ZPin$+r&jW3O2}>GUnqH5;Fh|wsHLuw(ey(v(pF9B zP}p)Y_2q1)hcq1UlA1dqE*XCNjtR0$!t}Gi`gm*<=6eY<(TfR w+c#_Ft5vzLd-`X}ow?&{nvm4DT%i0+{QvhZcWxZnCmdKI;Vst0JgzOs{jB1 diff --git a/projects/common/src/main/resources/assets/computercraft/textures/block/computer_on.png b/projects/common/src/main/resources/assets/computercraft/textures/block/computer_on.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad187b997c2bc5f1b2d226d21b2645353fe0792 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr}702@-J+>;(eeD0lHJ l^e{f^CvTcy!lJ>+$gstm`|HdUjS`?*22WQ%mvv4FO#nRN6?*^x literal 0 HcmV?d00001 diff --git a/projects/fabric/build.gradle.kts b/projects/fabric/build.gradle.kts index f2aef9cb8..db8701486 100644 --- a/projects/fabric/build.gradle.kts +++ b/projects/fabric/build.gradle.kts @@ -165,7 +165,6 @@ tasks.processResources { filesMatching("fabric.mod.json") { expand(mapOf("version" to modVersion)) } - exclude(".cache") } tasks.jar { diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java index 292d1ea9a..170034086 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/ComputerCraftClient.java @@ -4,6 +4,7 @@ package dan200.computercraft.client; +import dan200.computercraft.client.model.EmissiveComputerModel; import dan200.computercraft.client.model.turtle.TurtleModelLoader; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.network.client.ClientNetworkContext; @@ -35,9 +36,12 @@ public class ComputerCraftClient { ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register); ClientRegistry.registerMainThread(); - ModelLoadingRegistry.INSTANCE.registerModelProvider((manager, out) -> ClientRegistry.registerExtraModels(out)); ModelLoadingRegistry.INSTANCE.registerResourceProvider(loader -> (path, ctx) -> TurtleModelLoader.load(loader, path)); + ModelLoadingRegistry.INSTANCE.registerResourceProvider(loader -> (path, ctx) -> EmissiveComputerModel.load(loader, path)); + BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_NORMAL.get(), RenderType.cutout()); + BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_COMMAND.get(), RenderType.cutout()); + BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.COMPUTER_ADVANCED.get(), RenderType.cutout()); BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.MONITOR_NORMAL.get(), RenderType.cutout()); BlockRenderLayerMap.INSTANCE.putBlock(ModRegistry.Blocks.MONITOR_ADVANCED.get(), RenderType.cutout()); diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/model/EmissiveComputerModel.java b/projects/fabric/src/client/java/dan200/computercraft/client/model/EmissiveComputerModel.java new file mode 100644 index 000000000..27db7a75b --- /dev/null +++ b/projects/fabric/src/client/java/dan200/computercraft/client/model/EmissiveComputerModel.java @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.model; + +import com.google.gson.JsonObject; +import com.mojang.datafixers.util.Either; +import dan200.computercraft.api.ComputerCraftAPI; +import net.fabricmc.fabric.api.client.model.ModelProviderException; +import net.fabricmc.fabric.api.client.model.ModelResourceProvider; +import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; +import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.RandomSource; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.block.state.BlockState; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Wraps a computer's {@link BlockModel}/{@link BakedModel} to render the computer's cursor as an emissive quad. + *

+ * While Fabric has a quite advanced rendering extension API (including support for custom materials), but unlike Forge + * it doesn't expose this in the model JSON (though externals mods like JMX + * do handle this). + *

+ * Instead, we support emissive quads by injecting a custom {@linkplain ModelResourceProvider model loader/provider} + * which targets a hard-coded list of computer models, and wraps the returned model in a custom + * {@linkplain FabricBakedModel} implementation which renders specific quads as emissive. + *

+ * See also the assets/computercraft/models/block/computer_on.json model, which is the base for all + * emissive computer models. + */ +public final class EmissiveComputerModel { + private static final Set MODELS = Set.of( + "item/computer_advanced", + "block/computer_advanced_on", + "block/computer_advanced_blinking", + "item/computer_command", + "block/computer_command_on", + "block/computer_command_blinking", + "item/computer_normal", + "block/computer_normal_on", + "block/computer_normal_blinking" + ); + + private EmissiveComputerModel() { + } + + public static @Nullable UnbakedModel load(ResourceManager resources, ResourceLocation path) throws ModelProviderException { + if (!path.getNamespace().equals(ComputerCraftAPI.MOD_ID) || !MODELS.contains(path.getPath())) return null; + + JsonObject json; + try (var reader = resources.openAsReader(new ResourceLocation(path.getNamespace(), "models/" + path.getPath() + ".json"))) { + json = GsonHelper.parse(reader).getAsJsonObject(); + } catch (IOException e) { + throw new ModelProviderException("Failed loading model " + path, e); + } + + // Parse a subset of the model JSON + var parent = new ResourceLocation(GsonHelper.getAsString(json, "parent")); + + Map> textures = new HashMap<>(); + if (json.has("textures")) { + var jsonObject = GsonHelper.getAsJsonObject(json, "textures"); + + for (var entry : jsonObject.entrySet()) { + var texture = entry.getValue().getAsString(); + textures.put(entry.getKey(), texture.startsWith("#") + ? Either.right(texture.substring(1)) + : Either.left(new Material(InventoryMenu.BLOCK_ATLAS, new ResourceLocation(texture))) + ); + } + } + + return new Unbaked(parent, textures); + } + + /** + * An {@link UnbakedModel} which wraps the returned model using {@link Baked}. + *

+ * This subclasses {@link BlockModel} to allow using these models as a parent of other models. + */ + private static final class Unbaked extends BlockModel { + Unbaked(ResourceLocation parent, Map> materials) { + super(parent, List.of(), materials, null, null, ItemTransforms.NO_TRANSFORMS, List.of()); + } + + @Override + public BakedModel bake(ModelBaker baker, Function spriteGetter, ModelState state, ResourceLocation location) { + var baked = super.bake(baker, spriteGetter, state, location); + if (!hasTexture("cursor")) return baked; + + var render = RendererAccess.INSTANCE.getRenderer(); + if (render == null) return baked; + + return new Baked( + baked, + spriteGetter.apply(getMaterial("cursor")), + render.materialFinder().find(), + render.materialFinder().emissive(0, true).find() + ); + } + } + + /** + * A {@link FabricBakedModel} which renders quads using the {@code "cursor"} texture as emissive. + */ + private static final class Baked extends ForwardingBakedModel { + private final TextureAtlasSprite cursor; + private final RenderMaterial defaultMaterial; + private final RenderMaterial emissiveMaterial; + + Baked(BakedModel wrapped, TextureAtlasSprite cursor, RenderMaterial defaultMaterial, RenderMaterial emissiveMaterial) { + this.wrapped = wrapped; + this.cursor = cursor; + this.defaultMaterial = defaultMaterial; + this.emissiveMaterial = emissiveMaterial; + } + + @Override + public boolean isVanillaAdapter() { + return false; + } + + @Override + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + emitQuads(context, state, randomSupplier.get()); + } + + @Override + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + emitQuads(context, null, randomSupplier.get()); + } + + private void emitQuads(RenderContext context, @Nullable BlockState state, RandomSource random) { + var emitter = context.getEmitter(); + for (var faceIdx = 0; faceIdx <= ModelHelper.NULL_FACE_ID; faceIdx++) { + var cullFace = ModelHelper.faceFromIndex(faceIdx); + var quads = wrapped.getQuads(state, cullFace, random); + + var count = quads.size(); + for (var i = 0; i < count; i++) { + final var q = quads.get(i); + emitter.fromVanilla(q, q.getSprite() == cursor ? emissiveMaterial : defaultMaterial, cullFace); + emitter.emit(); + } + } + } + } +} diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json index e9fccca86..652f58ae1 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_advanced_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_advanced_front", "side": "computercraft:block/computer_advanced_side", "top": "computercraft:block/computer_advanced_top" } diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json index 497c1337d..fb7ab4898 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_advanced_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_advanced_front", "side": "computercraft:block/computer_advanced_side", "top": "computercraft:block/computer_advanced_top" } diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json index 62d746878..2177d9c7a 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_command_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_command_front", "side": "computercraft:block/computer_command_side", "top": "computercraft:block/computer_command_top" } diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_on.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_on.json index 36c6f0fa6..e4c5d608b 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_on.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_command_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_command_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_command_front", "side": "computercraft:block/computer_command_side", "top": "computercraft:block/computer_command_top" } diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json index 7250268d7..a2f258290 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_normal_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_normal_front", "side": "computercraft:block/computer_normal_side", "top": "computercraft:block/computer_normal_top" } diff --git a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json index da2e671fe..e8dc8eb0c 100644 --- a/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json +++ b/projects/fabric/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_normal_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_normal_front", "side": "computercraft:block/computer_normal_side", "top": "computercraft:block/computer_normal_top" } diff --git a/projects/forge/build.gradle.kts b/projects/forge/build.gradle.kts index 9291f2e4a..589e44f0d 100644 --- a/projects/forge/build.gradle.kts +++ b/projects/forge/build.gradle.kts @@ -206,7 +206,6 @@ tasks.processResources { filesMatching("META-INF/mods.toml") { expand(mapOf("forgeVersion" to libs.versions.forge.get(), "file" to mapOf("jarVersion" to modVersion))) } - exclude(".cache") } tasks.jar { diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json index e9fccca86..652f58ae1 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_advanced_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_advanced_front", "side": "computercraft:block/computer_advanced_side", "top": "computercraft:block/computer_advanced_top" } diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json index 497c1337d..fb7ab4898 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_advanced_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_advanced_front", "side": "computercraft:block/computer_advanced_side", "top": "computercraft:block/computer_advanced_top" } diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json index 62d746878..2177d9c7a 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_command_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_command_front", "side": "computercraft:block/computer_command_side", "top": "computercraft:block/computer_command_top" } diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_on.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_on.json index 36c6f0fa6..e4c5d608b 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_on.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_command_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_command_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_command_front", "side": "computercraft:block/computer_command_side", "top": "computercraft:block/computer_command_top" } diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json index 7250268d7..a2f258290 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_normal_front_blink", + "cursor": "computercraft:block/computer_blink", + "front": "computercraft:block/computer_normal_front", "side": "computercraft:block/computer_normal_side", "top": "computercraft:block/computer_normal_top" } diff --git a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json index da2e671fe..e8dc8eb0c 100644 --- a/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json +++ b/projects/forge/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json @@ -1,7 +1,8 @@ { - "parent": "minecraft:block/orientable", + "parent": "computercraft:block/computer_on", "textures": { - "front": "computercraft:block/computer_normal_front_on", + "cursor": "computercraft:block/computer_on", + "front": "computercraft:block/computer_normal_front", "side": "computercraft:block/computer_normal_side", "top": "computercraft:block/computer_normal_top" } From 953372b1b78cfbf3f8b7760b5d934b3f73c8523b Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 18 Jun 2023 19:30:25 +0100 Subject: [PATCH 10/11] Fix quad order when rendering turtles upside down - Reverse quads in our model transformer and when rendering as a block entity. - Correctly recompute normals when the quads have been inverted. Closes #1283 --- .../client/model/turtle/ModelTransformer.java | 98 +++++++++++++++++++ .../render/TurtleBlockEntityRenderer.java | 65 +++++++++++- .../client/model/TransformedBakedModel.java | 64 ++---------- .../client/model/TransformedBakedModel.java | 26 ++--- 4 files changed, 172 insertions(+), 81 deletions(-) create mode 100644 projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java diff --git a/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java b/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java new file mode 100644 index 000000000..4c4cd829e --- /dev/null +++ b/projects/common/src/client/java/dan200/computercraft/client/model/turtle/ModelTransformer.java @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.client.model.turtle; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; +import com.mojang.math.Transformation; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import org.joml.Matrix4f; +import org.joml.Vector4f; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * Applies a {@link Transformation} (or rather a {@link Matrix4f}) to a list of {@link BakedQuad}s. + *

+ * This does a little bit of magic compared with other system (i.e. Forge's {@code QuadTransformers}), as it needs to + * handle flipping models upside down. + *

+ * This is typically used with a {@link BakedModel} subclass - see the loader-specific projects. + */ +public final class ModelTransformer { + public static final int[] ORDER = new int[]{ 3, 2, 1, 0 }; + public static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); + private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION); + + private final Matrix4f transformation; + private final boolean invert; + private @Nullable TransformedQuads cache; + + public ModelTransformer(Transformation transformation) { + this.transformation = transformation.getMatrix(); + invert = transformation.getMatrix().determinant() < 0; + } + + public List transform(List quads) { + if (quads.isEmpty()) return List.of(); + + // We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces, + // so it's not worth being smarter here. + var cache = this.cache; + if (cache != null && quads.equals(cache.original())) return cache.transformed(); + + List transformed = new ArrayList<>(quads.size()); + for (var quad : quads) transformed.add(transformQuad(quad)); + this.cache = new TransformedQuads(quads, transformed); + return transformed; + } + + private BakedQuad transformQuad(BakedQuad quad) { + var inputData = quad.getVertices(); + var outputData = new int[inputData.length]; + for (var i = 0; i < 4; i++) { + var inStart = STRIDE * i; + // Reverse the order of the quads if we're inverting + var outStart = STRIDE * (invert ? ORDER[i] : i); + System.arraycopy(inputData, inStart, outputData, outStart, STRIDE); + + // Apply the matrix to our position + var inPosStart = inStart + POS_OFFSET; + var outPosStart = outStart + POS_OFFSET; + + var x = Float.intBitsToFloat(inputData[inPosStart]); + var y = Float.intBitsToFloat(inputData[inPosStart + 1]); + var z = Float.intBitsToFloat(inputData[inPosStart + 2]); + + // Transform the position + var pos = new Vector4f(x, y, z, 1); + transformation.transformProject(pos); + + outputData[outPosStart] = Float.floatToRawIntBits(pos.x()); + outputData[outPosStart + 1] = Float.floatToRawIntBits(pos.y()); + outputData[outPosStart + 2] = Float.floatToRawIntBits(pos.z()); + } + + var direction = Direction.rotate(transformation, quad.getDirection()); + return new BakedQuad(outputData, quad.getTintIndex(), direction, quad.getSprite(), quad.isShade()); + } + + private record TransformedQuads(List original, List transformed) { + } + + private static int findOffset(VertexFormat format, VertexFormatElement element) { + var offset = 0; + for (var other : format.getElements()) { + if (other == element) return offset / Integer.BYTES; + offset += element.getByteSize(); + } + throw new IllegalArgumentException("Cannot find " + element + " in " + format); + } +} diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java index eae29199e..70576eaef 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/TurtleBlockEntityRenderer.java @@ -10,6 +10,7 @@ import com.mojang.math.Axis; import com.mojang.math.Transformation; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.client.model.turtle.ModelTransformer; import dan200.computercraft.client.platform.ClientPlatformHelper; import dan200.computercraft.client.turtle.TurtleUpgradeModellers; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -30,6 +31,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; +import org.joml.Vector4f; import javax.annotation.Nullable; import java.util.List; @@ -146,16 +148,30 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer quads, @Nullable int[] tints) { var matrix = transform.last(); + var inverted = matrix.pose().determinant() < 0; for (var bakedquad : quads) { var tint = -1; @@ -167,7 +183,50 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer> 16 & 255) / 255.0F; var g = (float) (tint >> 8 & 255) / 255.0F; var b = (float) (tint & 255) / 255.0F; - buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); + if (inverted) { + putBulkQuadInvert(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); + } else { + buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight); + } + } + } + + /** + * A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} for + * when the matrix is inverted. + * + * @param buffer The buffer to draw to. + * @param pose The current matrix stack. + * @param quad The quad to draw. + * @param red The red tint of this quad. + * @param green The green tint of this quad. + * @param blue The blue tint of this quad. + * @param lightmapCoord The lightmap coordinate + * @param overlayLight The overlay light. + */ + private static void putBulkQuadInvert(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight) { + var matrix = pose.pose(); + // It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in + // Direction.rotate (so not out of nowhere!), but is a little suspicious. + var dirNormal = quad.getDirection().getNormal(); + var normal = matrix.transform(new Vector4f(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f)).normalize(); + + var vertices = quad.getVertices(); + for (var vertex : ModelTransformer.ORDER) { + var i = vertex * ModelTransformer.STRIDE; + + var x = Float.intBitsToFloat(vertices[i]); + var y = Float.intBitsToFloat(vertices[i + 1]); + var z = Float.intBitsToFloat(vertices[i + 2]); + var transformed = matrix.transform(new Vector4f(x, y, z, 1)); + + var u = Float.intBitsToFloat(vertices[i + 4]); + var v = Float.intBitsToFloat(vertices[i + 5]); + buffer.vertex( + transformed.x(), transformed.y(), transformed.z(), + red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord, + normal.x(), normal.y(), normal.z() + ); } } diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java b/projects/fabric/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java index 9862101ff..01706cf90 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java @@ -4,84 +4,32 @@ package dan200.computercraft.client.model; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexFormatElement; import com.mojang.math.Transformation; +import dan200.computercraft.client.model.turtle.ModelTransformer; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.state.BlockState; -import org.joml.Matrix4f; -import org.joml.Vector4f; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.List; /** * A {@link BakedModel} which applies a transformation matrix to its underlying quads. + * + * @see ModelTransformer */ public class TransformedBakedModel extends CustomBakedModel { - private static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize(); - private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION); - - private final Matrix4f transformation; - private @Nullable TransformedQuads cache; + private final ModelTransformer transformation; public TransformedBakedModel(BakedModel model, Transformation transformation) { super(model); - this.transformation = transformation.getMatrix(); + this.transformation = new ModelTransformer(transformation); } @Override public List getQuads(@Nullable BlockState blockState, @Nullable Direction face, RandomSource rand) { - var cache = this.cache; - var quads = wrapped.getQuads(blockState, face, rand); - if (quads.isEmpty()) return List.of(); - - // We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces, - // so it's not worth being smarter here. - if (cache != null && quads.equals(cache.original())) return cache.transformed(); - - List transformed = new ArrayList<>(quads.size()); - for (var quad : quads) transformed.add(transformQuad(quad)); - this.cache = new TransformedQuads(quads, transformed); - return transformed; - } - - private BakedQuad transformQuad(BakedQuad quad) { - var vertexData = quad.getVertices().clone(); - for (var i = 0; i < 4; i++) { - // Apply the matrix to our position - var start = STRIDE * i + POS_OFFSET; - - var x = Float.intBitsToFloat(vertexData[start]); - var y = Float.intBitsToFloat(vertexData[start + 1]); - var z = Float.intBitsToFloat(vertexData[start + 2]); - - // Transform the position - var pos = new Vector4f(x, y, z, 1); - transformation.transformProject(pos); - - vertexData[start] = Float.floatToRawIntBits(pos.x()); - vertexData[start + 1] = Float.floatToRawIntBits(pos.y()); - vertexData[start + 2] = Float.floatToRawIntBits(pos.z()); - } - - return new BakedQuad(vertexData, quad.getTintIndex(), quad.getDirection(), quad.getSprite(), quad.isShade()); - } - - private record TransformedQuads(List original, List transformed) { - } - - private static int findOffset(VertexFormat format, VertexFormatElement element) { - var offset = 0; - for (var other : format.getElements()) { - if (other == element) return offset / Integer.BYTES; - offset += element.getByteSize(); - } - throw new IllegalArgumentException("Cannot find " + element + " in " + format); + return transformation.transform(wrapped.getQuads(blockState, face, rand)); } } diff --git a/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java b/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java index d1eecbbda..2d457fe52 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/model/TransformedBakedModel.java @@ -5,6 +5,7 @@ package dan200.computercraft.client.model; import com.mojang.math.Transformation; +import dan200.computercraft.client.model.turtle.ModelTransformer; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; @@ -12,7 +13,6 @@ import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.client.model.BakedModelWrapper; -import net.minecraftforge.client.model.QuadTransformers; import net.minecraftforge.client.model.data.ModelData; import javax.annotation.Nullable; @@ -20,16 +20,15 @@ import java.util.List; /** * A {@link BakedModel} which applies a transformation matrix to its underlying quads. + * + * @see ModelTransformer */ public class TransformedBakedModel extends BakedModelWrapper { - private final Transformation transformation; - private final boolean invert; - private @Nullable TransformedQuads cache; + private final ModelTransformer transformation; public TransformedBakedModel(BakedModel model, Transformation transformation) { super(model); - this.transformation = transformation; - invert = transformation.getNormalMatrix().determinant() < 0; + this.transformation = new ModelTransformer(transformation); } @Override @@ -39,19 +38,6 @@ public class TransformedBakedModel extends BakedModelWrapper { @Override public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) { - var cache = this.cache; - var quads = originalModel.getQuads(state, side, rand, extraData, renderType); - if (quads.isEmpty()) return List.of(); - - // We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces, - // so it's not worth being smarter here. - if (cache != null && quads.equals(cache.original())) return cache.transformed(); - - var transformed = QuadTransformers.applying(transformation).process(quads); - this.cache = new TransformedQuads(quads, transformed); - return transformed; - } - - private record TransformedQuads(List original, List transformed) { + return transformation.transform(originalModel.getQuads(state, side, rand, extraData, renderType)); } } From c8523bf4793d202457db9d97f27733a33aebc577 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Sun, 18 Jun 2023 17:42:28 -0400 Subject: [PATCH 11/11] Add ability to serialize Unicode strings to JSON (#1489) --- gradle/libs.versions.toml | 2 +- .../computercraft/lua/rom/apis/textutils.lua | 107 +++++++++++++----- .../test-rom/spec/apis/textutils_spec.lua | 15 ++- 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e13ca1c60..9ff0cbb6c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,7 +57,7 @@ fabric-loom = "1.1.10" forgeGradle = "5.1.+" githubRelease = "2.2.12" ideaExt = "1.1.6" -illuaminate = "0.1.0-24-gdb28902" +illuaminate = "0.1.0-28-ga7efd71" librarian = "1.+" minotaur = "2.+" mixinGradle = "0.7.+" diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua index 353dbb799..3d34c27b3 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua @@ -424,12 +424,31 @@ do if map[c] == nil then map[c] = hexify(c) end end - serializeJSONString = function(s) - return ('"%s"'):format(s:gsub("[\0-\x1f\"\\]", map):gsub("[\x7f-\xff]", hexify)) + serializeJSONString = function(s, options) + if options and options.unicode_strings and s:find("[\x80-\xff]") then + local retval = '"' + for _, code in utf8.codes(s) do + if code > 0xFFFF then + -- Encode the codepoint as a UTF-16 surrogate pair + code = code - 0x10000 + local high, low = bit32.extract(code, 10, 10) + 0xD800, bit32.extract(code, 0, 10) + 0xDC00 + retval = retval .. ("\\u%04X\\u%04X"):format(high, low) + elseif code <= 0x5C and map[string.char(code)] then -- 0x5C = `\`, don't run `string.char` if we don't need to + retval = retval .. map[string.char(code)] + elseif code < 0x20 or code >= 0x7F then + retval = retval .. ("\\u%04X"):format(code) + else + retval = retval .. string.char(code) + end + end + return retval .. '"' + else + return ('"%s"'):format(s:gsub("[\0-\x1f\"\\]", map):gsub("[\x7f-\xff]", hexify)) + end end end -local function serializeJSONImpl(t, tTracking, bNBTStyle) +local function serializeJSONImpl(t, tTracking, options) local sType = type(t) if t == empty_json_array then return "[]" elseif t == json_null then return "null" @@ -450,13 +469,14 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle) local nObjectSize = 0 local nArraySize = 0 local largestArrayIndex = 0 + local bNBTStyle = options and options.nbt_style for k, v in pairs(t) do if type(k) == "string" then local sEntry if bNBTStyle then - sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle) + sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, options) else - sEntry = serializeJSONString(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle) + sEntry = serializeJSONString(k, options) .. ":" .. serializeJSONImpl(v, tTracking, options) end if nObjectSize == 0 then sObjectResult = sObjectResult .. sEntry @@ -473,7 +493,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle) if t[k] == nil then --if the array is nil at index k the value is "null" as to keep the unused indexes in between used ones. sEntry = "null" else -- if the array index does not point to a nil we serialise it's content. - sEntry = serializeJSONImpl(t[k], tTracking, bNBTStyle) + sEntry = serializeJSONImpl(t[k], tTracking, options) end if nArraySize == 0 then sArrayResult = sArrayResult .. sEntry @@ -492,7 +512,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle) end elseif sType == "string" then - return serializeJSONString(t) + return serializeJSONString(t, options) elseif sType == "number" or sType == "boolean" then return tostring(t) @@ -813,32 +833,57 @@ end unserialise = unserialize -- GB version ---- Returns a JSON representation of the given data. --- --- This function attempts to guess whether a table is a JSON array or --- object. However, empty tables are assumed to be empty objects - use --- @{textutils.empty_json_array} to mark an empty array. --- --- This is largely intended for interacting with various functions from the --- @{commands} API, though may also be used in making @{http} requests. --- --- @param t The value to serialise. Like @{textutils.serialise}, this should not --- contain recursive tables or functions. --- @tparam[opt] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys) --- instead of standard JSON. --- @treturn string The JSON representation of the input. --- @throws If the object contains a value which cannot be --- serialised. This includes functions and tables which appear multiple --- times. --- @usage textutils.serialiseJSON({ values = { 1, "2", true } }) --- @since 1.7 --- @see textutils.json_null Use to serialise a JSON `null` value. --- @see textutils.empty_json_array Use to serialise a JSON empty array. -function serializeJSON(t, bNBTStyle) +--[[- Returns a JSON representation of the given data. + +This function attempts to guess whether a table is a JSON array or +object. However, empty tables are assumed to be empty objects - use +@{textutils.empty_json_array} to mark an empty array. + +This is largely intended for interacting with various functions from the +@{commands} API, though may also be used in making @{http} requests. + +@param[1] t The value to serialise. Like @{textutils.serialise}, this should not +contain recursive tables or functions. +@tparam[1,opt] { nbt_style? = boolean, unicode_strings? = boolean } options Options for serialisation. +- `nbt_style`: Whether to produce NBT-style JSON (non-quoted keys) instead of standard JSON. +- `unicode_strings`: Whether to treat strings as containing UTF-8 characters instead of + using the default 8-bit character set. + +@param[2] t The value to serialise. Like @{textutils.serialise}, this should not +contain recursive tables or functions. +@tparam[2] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys) +instead of standard JSON. + +@treturn string The JSON representation of the input. +@throws If the object contains a value which cannot be serialised. This includes +functions and tables which appear multiple times. + +@usage Serialise a simple object + + textutils.serialiseJSON({ values = { 1, "2", true } }) + +@usage Serialise an object to a NBT-style string + + textutils.serialiseJSON({ values = { 1, "2", true } }, { nbt_style = true }) + +@since 1.7 +@changed 1.106.0 Added `options` overload and `unicode_strings` option. + +@see textutils.json_null Use to serialise a JSON `null` value. +@see textutils.empty_json_array Use to serialise a JSON empty array. +]] +function serializeJSON(t, options) expect(1, t, "table", "string", "number", "boolean") - expect(2, bNBTStyle, "boolean", "nil") + expect(2, options, "table", "boolean", "nil") + if type(options) == "boolean" then + options = { nbt_style = options } + elseif type(options) == "table" then + field(options, "nbt_style", "boolean", "nil") + field(options, "unicode_strings", "boolean", "nil") + end + local tTracking = {} - return serializeJSONImpl(t, tTracking, bNBTStyle or false) + return serializeJSONImpl(t, tTracking, options) end serialiseJSON = serializeJSON -- GB version diff --git a/projects/core/src/test/resources/test-rom/spec/apis/textutils_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/textutils_spec.lua index af5ad9005..53df9fb56 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/textutils_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/textutils_spec.lua @@ -143,8 +143,10 @@ describe("The textutils library", function() textutils.serialiseJSON({}) textutils.serialiseJSON(false) textutils.serialiseJSON("", true) + textutils.serializeJSON("", {}) + textutils.serializeJSON(0, { nbt_style = true, unicode_strings = true }) expect.error(textutils.serialiseJSON, nil):eq("bad argument #1 (table, string, number or boolean expected, got nil)") - expect.error(textutils.serialiseJSON, "", 1):eq("bad argument #2 (boolean expected, got number)") + expect.error(textutils.serialiseJSON, "", 1):eq("bad argument #2 (table or boolean expected, got number)") end) it("serializes empty arrays", function() @@ -174,6 +176,17 @@ describe("The textutils library", function() expect(textutils.serializeJSON({ 5, "test", nil, nil, textutils.json_null })):eq('[5,"test",null,null,null]') expect(textutils.serializeJSON({ nil, nil, nil, nil, "text" })):eq('[null,null,null,null,"text"]') end) + + it("serializes NBT style", function() + expect(textutils.serializeJSON({ test = 2 }, { nbt_style = true })):eq('{test:2}') + expect(textutils.serializeJSON({ test = 2 }, true)):eq('{test:2}') -- old style + end) + + it("serializes Unicode strings", function() + expect(textutils.serializeJSON("\u{3053}\u{3093}\u{306B}\u{3061}\u{306F}", { unicode_strings = true })):eq([["\u3053\u3093\u306B\u3061\u306F"]]) + expect(textutils.serializeJSON("\u{1f62f}", { unicode_strings = true })):eq([["\uD83D\uDE2F"]]) + expect(textutils.serializeJSON("\\\"\u{00ff}\n\"", { unicode_strings = true })):eq('"\\\\\\"\\u00FF\\n\\""') + end) end) describe("textutils.unserializeJSON", function()